@stll/folio-core 0.11.0 → 0.13.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 (263) hide show
  1. package/dist/ai-edits/apply.d.ts +2 -1
  2. package/dist/ai-edits/apply.js +693 -63
  3. package/dist/ai-edits/block-identity.d.ts +4 -0
  4. package/dist/ai-edits/block-identity.js +9 -0
  5. package/dist/ai-edits/blockRange.d.ts +28 -1
  6. package/dist/ai-edits/blockRange.js +68 -2
  7. package/dist/ai-edits/clean-text.js +2 -1
  8. package/dist/ai-edits/headless.d.ts +4 -4
  9. package/dist/ai-edits/headless.js +9 -9
  10. package/dist/ai-edits/index.d.ts +2 -2
  11. package/dist/ai-edits/index.js +2 -2
  12. package/dist/ai-edits/read.d.ts +7 -7
  13. package/dist/ai-edits/read.js +110 -3
  14. package/dist/ai-edits/snapshot.js +21 -3
  15. package/dist/ai-edits/table-cell-mutations.d.ts +48 -0
  16. package/dist/ai-edits/table-cell-mutations.js +238 -0
  17. package/dist/ai-edits/table-mutation-plan.d.ts +38 -0
  18. package/dist/ai-edits/table-mutation-plan.js +90 -0
  19. package/dist/ai-edits/table-row-column-mutations.d.ts +108 -0
  20. package/dist/ai-edits/table-row-column-mutations.js +300 -0
  21. package/dist/ai-edits/table-targets.d.ts +32 -0
  22. package/dist/ai-edits/table-targets.js +70 -0
  23. package/dist/ai-edits/types.d.ts +62 -1
  24. package/dist/ai-edits/word-diff.js +21 -0
  25. package/dist/compat/eigenpal.d.ts +3 -3
  26. package/dist/compat/eigenpal.js +3 -3
  27. package/dist/content-controls/mutateContentControls.js +1 -1
  28. package/dist/controller/activeEditorStory.d.ts +36 -0
  29. package/dist/controller/activeEditorStory.js +30 -0
  30. package/dist/controller/contentControlWidgetController.d.ts +55 -0
  31. package/dist/controller/contentControlWidgetController.js +141 -0
  32. package/dist/controller/fontReadiness.js +2 -0
  33. package/dist/controller/headerFooterEditorManager.js +2 -2
  34. package/dist/controller/hiddenEditorApi.js +2 -2
  35. package/dist/controller/hiddenEditorManager.d.ts +12 -1
  36. package/dist/controller/hiddenEditorManager.js +44 -12
  37. package/dist/controller/layoutPipeline.d.ts +2 -0
  38. package/dist/controller/layoutPipeline.js +17 -2
  39. package/dist/controller/noteEditorManager.d.ts +36 -0
  40. package/dist/controller/noteEditorManager.js +210 -0
  41. package/dist/document-operations.d.ts +43 -11
  42. package/dist/document-operations.js +186 -18
  43. package/dist/docx/blockContentParser.js +21 -7
  44. package/dist/docx/commentParser.js +6 -1
  45. package/dist/docx/commentRangeIntegrity.d.ts +6 -0
  46. package/dist/docx/commentRangeIntegrity.js +186 -0
  47. package/dist/docx/conformance.js +62 -1
  48. package/dist/docx/documentGrid.d.ts +8 -0
  49. package/dist/docx/documentGrid.js +8 -0
  50. package/dist/docx/drawingUtils.js +1 -1
  51. package/dist/docx/encryption/agileDecryption.js +12 -2
  52. package/dist/docx/encryption/compoundFile.js +6 -1
  53. package/dist/docx/encryption/encryptionInfo.d.ts +9 -1
  54. package/dist/docx/encryption/encryptionInfo.js +18 -2
  55. package/dist/docx/encryption/openEncryptedDocx.js +1 -1
  56. package/dist/docx/ensureParaIds.js +8 -7
  57. package/dist/docx/fieldParser.js +1 -1
  58. package/dist/docx/groupDrawingParser.js +10 -5
  59. package/dist/docx/hyperlinkParser.js +1 -1
  60. package/dist/docx/imageParser.js +10 -6
  61. package/dist/docx/metadataPrivacy.js +17 -3
  62. package/dist/docx/notePropertiesParser.js +1 -1
  63. package/dist/docx/numberingParser.js +1 -1
  64. package/dist/docx/paragraphParser.js +70 -61
  65. package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
  66. package/dist/docx/paragraphTextBoxEnrichment.js +67 -13
  67. package/dist/docx/paragraphTraversal.js +1 -1
  68. package/dist/docx/parser.js +3 -3
  69. package/dist/docx/rezip.js +20 -9
  70. package/dist/docx/runParser.js +17 -6
  71. package/dist/docx/sdtProperties.js +1 -1
  72. package/dist/docx/sectionParser.js +18 -5
  73. package/dist/docx/selectiveSave.js +5 -1
  74. package/dist/docx/selectiveXmlPatch.d.ts +22 -1
  75. package/dist/docx/selectiveXmlPatch.js +61 -1
  76. package/dist/docx/serializer/blockSdtSerializer.js +3 -2
  77. package/dist/docx/serializer/commentSerializer.js +5 -6
  78. package/dist/docx/serializer/documentSerializer.js +2 -2
  79. package/dist/docx/serializer/headerFooterSerializer.js +2 -2
  80. package/dist/docx/serializer/noteSerializer.js +2 -2
  81. package/dist/docx/serializer/paragraphSerializer.js +23 -15
  82. package/dist/docx/serializer/runSerializer.js +25 -21
  83. package/dist/docx/serializer/sectionPropertiesSerializer.js +2 -1
  84. package/dist/docx/serializer/stylesSerializer.js +1 -1
  85. package/dist/docx/serializer/tableSerializer.d.ts +5 -4
  86. package/dist/docx/serializer/tableSerializer.js +26 -20
  87. package/dist/docx/serializer/xmlUtils.d.ts +13 -1
  88. package/dist/docx/serializer/xmlUtils.js +27 -1
  89. package/dist/docx/server/applyDocxXmlPatchProposal.d.ts +87 -0
  90. package/dist/docx/server/applyDocxXmlPatchProposal.js +204 -0
  91. package/dist/docx/server/boundedArchive.d.ts +14 -3
  92. package/dist/docx/server/boundedArchive.js +56 -11
  93. package/dist/docx/server/evaluateDocxXmlPatchProposal.d.ts +85 -0
  94. package/dist/docx/server/evaluateDocxXmlPatchProposal.js +361 -0
  95. package/dist/docx/server/extractDocxText.js +55 -8
  96. package/dist/docx/server/inspectDocxPackage.d.ts +70 -0
  97. package/dist/docx/server/inspectDocxPackage.js +217 -0
  98. package/dist/docx/server/validateDocxConformance.d.ts +41 -0
  99. package/dist/docx/server/validateDocxConformance.js +336 -0
  100. package/dist/docx/settingsParser.js +26 -3
  101. package/dist/docx/shapeParser.js +1 -1
  102. package/dist/docx/styleParser.js +4 -2
  103. package/dist/docx/tableParser.d.ts +8 -1
  104. package/dist/docx/tableParser.js +31 -10
  105. package/dist/docx/textBoxParser.d.ts +3 -3
  106. package/dist/docx/textBoxParser.js +16 -12
  107. package/dist/docx/unzip.js +1 -0
  108. package/dist/docx/vmlImageParser.js +11 -3
  109. package/dist/docx/xmlParser.js +11 -1
  110. package/dist/docx/xmlSafety.d.ts +5 -0
  111. package/dist/docx/xmlSafety.js +9 -0
  112. package/dist/fields/resolveFieldValues.js +1 -3
  113. package/dist/fonts/embeddedFonts.d.ts +33 -5
  114. package/dist/fonts/embeddedFonts.js +37 -8
  115. package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
  116. package/dist/i18n/messages/catalogs.gen.js +17 -0
  117. package/dist/i18n/messages/messages.gen.d.ts +1 -0
  118. package/dist/index.d.ts +3 -3
  119. package/dist/index.js +3 -3
  120. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  121. package/dist/layout-bridge/convert/footnoteLayout.js +47 -40
  122. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  123. package/dist/layout-bridge/convert/headerFooterLayout.js +3 -0
  124. package/dist/layout-bridge/convert/templatePreviewFlow.js +3 -3
  125. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
  126. package/dist/layout-bridge/convert/toFlowBlocks.js +151 -20
  127. package/dist/layout-bridge/dom/noteStoryDom.d.ts +6 -0
  128. package/dist/layout-bridge/dom/noteStoryDom.js +17 -0
  129. package/dist/layout-bridge/engine/clickToPosition.js +2 -2
  130. package/dist/layout-engine/index.d.ts +2 -2
  131. package/dist/layout-engine/index.js +58 -30
  132. package/dist/layout-engine/justificationCompatibility.d.ts +6 -0
  133. package/dist/layout-engine/justificationCompatibility.js +8 -0
  134. package/dist/layout-engine/keep-together.js +11 -1
  135. package/dist/layout-engine/measure/cache.js +23 -5
  136. package/dist/layout-engine/measure/effectiveLineBreakPolicy.js +8 -2
  137. package/dist/layout-engine/measure/lineBreakProvider.d.ts +2 -2
  138. package/dist/layout-engine/measure/lineBreakProvider.js +56 -37
  139. package/dist/layout-engine/measure/measureBlocks.js +15 -4
  140. package/dist/layout-engine/measure/measureContainer.js +1 -1
  141. package/dist/layout-engine/measure/measureHelpers.d.ts +11 -2
  142. package/dist/layout-engine/measure/measureHelpers.js +15 -4
  143. package/dist/layout-engine/measure/measureParagraph.js +85 -25
  144. package/dist/layout-engine/measure/measureProvider.js +4 -3
  145. package/dist/layout-engine/measure/tableCellFlow.js +5 -2
  146. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +9 -7
  147. package/dist/layout-engine/measure/textBoxParagraphLayout.js +22 -6
  148. package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +3 -1
  149. package/dist/layout-engine/measure/textMeasurementPolicy.js +7 -1
  150. package/dist/layout-engine/paginator.js +5 -1
  151. package/dist/layout-engine/paragraphSpacing.d.ts +28 -4
  152. package/dist/layout-engine/paragraphSpacing.js +107 -13
  153. package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
  154. package/dist/layout-engine/types.d.ts +48 -15
  155. package/dist/layout-painter/registry/modules/textBox.js +5 -1
  156. package/dist/layout-painter/renderImage.d.ts +12 -1
  157. package/dist/layout-painter/renderImage.js +21 -4
  158. package/dist/layout-painter/renderPage.d.ts +5 -2
  159. package/dist/layout-painter/renderPage.js +59 -14
  160. package/dist/layout-painter/renderParagraph.js +55 -15
  161. package/dist/layout-painter/renderTable.d.ts +5 -1
  162. package/dist/layout-painter/renderTable.js +7 -3
  163. package/dist/layout-painter/renderTextBox.d.ts +2 -1
  164. package/dist/layout-painter/renderTextBox.js +25 -14
  165. package/dist/layout-painter/renderWatermark.d.ts +1 -1
  166. package/dist/layout-painter/renderWatermark.js +2 -1
  167. package/dist/managers/DocumentLoaderManager.js +1 -1
  168. package/dist/markdown/fromMarkdown.js +1 -1
  169. package/dist/model.js +1 -1
  170. package/dist/prosemirror/attrs/index.d.ts +4 -2
  171. package/dist/prosemirror/attrs/index.js +175 -18
  172. package/dist/prosemirror/commands/comments.d.ts +79 -4
  173. package/dist/prosemirror/commands/comments.js +622 -13
  174. package/dist/prosemirror/commands/contentControls.js +1 -1
  175. package/dist/prosemirror/commands/hyperlink.js +15 -1
  176. package/dist/prosemirror/commands/image.js +7 -1
  177. package/dist/prosemirror/commands/index.d.ts +2 -2
  178. package/dist/prosemirror/commands/index.js +2 -2
  179. package/dist/prosemirror/commands/pageBreak.js +1 -1
  180. package/dist/prosemirror/commands/propertyChangeScope.d.ts +2 -2
  181. package/dist/prosemirror/commands/propertyChangeScope.js +3 -2
  182. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +9 -0
  183. package/dist/prosemirror/commands/tableCellMergeResolution.js +181 -0
  184. package/dist/prosemirror/commentIdAllocator.js +13 -3
  185. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -1
  186. package/dist/prosemirror/conversion/fromProseDoc.js +404 -62
  187. package/dist/prosemirror/conversion/index.js +1 -1
  188. package/dist/prosemirror/conversion/sdtAttrs.d.ts +9 -0
  189. package/dist/prosemirror/conversion/sdtAttrs.js +49 -0
  190. package/dist/prosemirror/conversion/toProseDoc.d.ts +2 -1
  191. package/dist/prosemirror/conversion/toProseDoc.js +364 -103
  192. package/dist/prosemirror/extensions/ExtensionManager.js +1 -1
  193. package/dist/prosemirror/extensions/StarterKit.js +6 -1
  194. package/dist/prosemirror/extensions/core/ParagraphExtension.js +6 -2
  195. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -0
  196. package/dist/prosemirror/extensions/features/ImagePasteExtension.js +10 -2
  197. package/dist/prosemirror/extensions/features/ListExtension.js +1 -1
  198. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +10 -1
  199. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +93 -2
  200. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +2 -1
  201. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +6 -2
  202. package/dist/prosemirror/extensions/features/pasteCleanup.js +15 -2
  203. package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +1 -1
  204. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +30 -4
  205. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.d.ts +9 -1
  206. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +70 -11
  207. package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
  208. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -0
  209. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +22 -0
  210. package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
  211. package/dist/prosemirror/extensions/nodes/TableExtension.js +14 -6
  212. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +13 -0
  213. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +41 -0
  214. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +5 -2
  215. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +4 -1
  216. package/dist/prosemirror/index.d.ts +1 -1
  217. package/dist/prosemirror/index.js +1 -1
  218. package/dist/prosemirror/plugins/contentControlWidgets.d.ts +36 -10
  219. package/dist/prosemirror/plugins/contentControlWidgets.js +128 -94
  220. package/dist/prosemirror/plugins/revisionIds.d.ts +23 -0
  221. package/dist/prosemirror/plugins/revisionIds.js +57 -0
  222. package/dist/prosemirror/plugins/selectionTracker.js +1 -0
  223. package/dist/prosemirror/plugins/suggestionMode.js +5 -4
  224. package/dist/prosemirror/runFormattingMarkNames.d.ts +14 -0
  225. package/dist/prosemirror/runFormattingMarkNames.js +40 -0
  226. package/dist/prosemirror/schema/index.d.ts +2 -2
  227. package/dist/prosemirror/schema/marks.d.ts +30 -3
  228. package/dist/prosemirror/schema/marks.js +15 -0
  229. package/dist/prosemirror/schema/nodes.d.ts +111 -6
  230. package/dist/prosemirror/selectionState.js +1 -0
  231. package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
  232. package/dist/prosemirror/tableCellMergeRevision.d.ts +9 -0
  233. package/dist/prosemirror/tableCellMergeRevision.js +22 -0
  234. package/dist/prosemirror/textBoxAnchorAttrs.d.ts +9 -0
  235. package/dist/prosemirror/textBoxAnchorAttrs.js +33 -0
  236. package/dist/prosemirror/utils/extractTrackedChanges.d.ts +1 -1
  237. package/dist/prosemirror/utils/extractTrackedChanges.js +62 -14
  238. package/dist/prosemirror/validation.js +9 -1
  239. package/dist/render-dom/RemoteSelectionOverlay.js +2 -2
  240. package/dist/server.d.ts +7 -3
  241. package/dist/server.js +7 -3
  242. package/dist/utils/clipboard.d.ts +10 -1
  243. package/dist/utils/clipboard.js +33 -6
  244. package/dist/utils/colorResolver.d.ts +8 -1
  245. package/dist/utils/colorResolver.js +35 -5
  246. package/dist/utils/fontFamilyMerge.js +9 -11
  247. package/dist/utils/fontLoader.js +3 -2
  248. package/dist/utils/fontResolver.d.ts +2 -1
  249. package/dist/utils/fontResolver.js +31 -7
  250. package/dist/utils/fontWeights.d.ts +1 -1
  251. package/dist/utils/fontWeights.js +1 -1
  252. package/dist/utils/hexId.d.ts +11 -1
  253. package/dist/utils/hexId.js +11 -1
  254. package/dist/utils/paragraphFormattingMerge.js +1 -0
  255. package/dist/utils/sanitizeImageSrc.d.ts +21 -0
  256. package/dist/utils/sanitizeImageSrc.js +30 -0
  257. package/dist/utils/stripXmlDeclarations.d.ts +11 -0
  258. package/dist/utils/stripXmlDeclarations.js +35 -0
  259. package/dist/version-comparison.d.ts +22 -2
  260. package/dist/version-comparison.js +41 -12
  261. package/dist/watermark/index.d.ts +11 -1
  262. package/dist/watermark/index.js +22 -1
  263. package/package.json +3 -2
@@ -1,15 +1,17 @@
1
1
  import { NUMBER_FORMAT_VALUES } from "../../types/documentEnumValues.js";
2
- import { AUTO_PARAGRAPH_SPACING_PX, halfPointsToPixels, halfPointsToPoints, pointsToPixels } from "../../utils/units.js";
3
- import { convertBulletToUnicode } from "../../docx/bulletMarkers.js";
4
- import { padDecimal } from "../../docx/numberingParser.js";
5
- import { autospacingMatchesBase } from "../../prosemirror/autospacingBase.js";
6
2
  import { directionIsRtl } from "../../prosemirror/paragraphDirection.js";
7
3
  import { expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs, expectRunShadingMarkAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs } from "../../prosemirror/attrs/index.js";
4
+ import { padDecimal } from "../../docx/numberingParser.js";
5
+ import { AUTO_PARAGRAPH_SPACING_PX, halfPointsToPixels, halfPointsToPoints, pointsToPixels } from "../../utils/units.js";
6
+ import { autospacingMatchesBase } from "../../prosemirror/autospacingBase.js";
8
7
  import { assertValidProseMirrorDocument } from "../../prosemirror/validation.js";
9
8
  import { runShadingAttrsToShading } from "../../prosemirror/conversion/runShadingMark.js";
10
9
  import { resolveColor, resolveHighlightToCss } from "../../utils/colorResolver.js";
10
+ import { resolveThemeFont } from "../../utils/fontResolver.js";
11
11
  import { resolveShadingFill } from "../../utils/formatToStyle.js";
12
+ import { convertBulletToUnicode } from "../../docx/bulletMarkers.js";
12
13
  import { DEFAULT_TEXTBOX_MARGINS } from "../../layout-engine/types.js";
14
+ import { resolveDocumentGridLinePitch } from "../../docx/documentGrid.js";
13
15
  import { setTextBoxGroupId } from "../../layout-engine/textBoxGroup.js";
14
16
  import { setParagraphFrame } from "../../layout-engine/paragraphFrame.js";
15
17
  import { getColumns } from "../sectionColumns.js";
@@ -239,9 +241,10 @@ function extractRunFormatting(marks, theme) {
239
241
  break;
240
242
  case "fontFamily": {
241
243
  const attrs = expectFontFamilyMarkAttrs(mark);
242
- const font = attrs.ascii || attrs.hAnsi;
244
+ const font = resolveWesternThemeFont(attrs, theme);
243
245
  if (font) formatting.fontFamily = font;
244
- if (attrs.eastAsia) formatting.eastAsiaFontFamily = attrs.eastAsia;
246
+ const eastAsiaFont = resolveEastAsiaThemeFont(attrs, theme);
247
+ if (eastAsiaFont) formatting.eastAsiaFontFamily = eastAsiaFont;
245
248
  break;
246
249
  }
247
250
  case "language": {
@@ -330,6 +333,10 @@ function extractRunFormatting(marks, theme) {
330
333
  formatting.changeAuthor = attrs.author;
331
334
  if (attrs.date !== void 0) formatting.changeDate = attrs.date;
332
335
  formatting.changeRevisionId = attrs.revisionId;
336
+ if (attrs.provenance === "suggested") {
337
+ formatting.isSuggestion = true;
338
+ if (attrs.suggestionId) formatting.suggestionId = attrs.suggestionId;
339
+ }
333
340
  break;
334
341
  }
335
342
  case "deletion": {
@@ -338,6 +345,10 @@ function extractRunFormatting(marks, theme) {
338
345
  formatting.changeAuthor = attrs.author;
339
346
  if (attrs.date !== void 0) formatting.changeDate = attrs.date;
340
347
  formatting.changeRevisionId = attrs.revisionId;
348
+ if (attrs.provenance === "suggested") {
349
+ formatting.isSuggestion = true;
350
+ if (attrs.suggestionId) formatting.suggestionId = attrs.suggestionId;
351
+ }
341
352
  break;
342
353
  }
343
354
  default: break;
@@ -345,6 +356,13 @@ function extractRunFormatting(marks, theme) {
345
356
  if (hasNoteRef && formatting.subscript) delete formatting.superscript;
346
357
  return formatting;
347
358
  }
359
+ const resolveWesternThemeFont = (fontFamily, theme) => {
360
+ const themeRef = fontFamily.asciiTheme ?? fontFamily.hAnsiTheme;
361
+ return (themeRef ? resolveThemeFont(themeRef, theme?.fontScheme) : null) ?? fontFamily.ascii ?? fontFamily.hAnsi ?? void 0;
362
+ };
363
+ const resolveEastAsiaThemeFont = (fontFamily, theme) => {
364
+ return (fontFamily.eastAsiaTheme ? resolveThemeFont(fontFamily.eastAsiaTheme, theme?.fontScheme) : null) ?? fontFamily.eastAsia ?? void 0;
365
+ };
348
366
  function isAutomaticTextColorValue(color) {
349
367
  const rgb = color.rgb?.trim().toLowerCase();
350
368
  return color.auto === true || rgb === "auto" || !rgb && !color.themeColor;
@@ -381,9 +399,10 @@ function paragraphRunDefaults(pmAttrs, theme) {
381
399
  const defaultTextFormatting = pmAttrs.defaultTextFormatting;
382
400
  if (!defaultTextFormatting) return {};
383
401
  const result = {};
384
- const fontFamily = defaultTextFormatting.fontFamily?.ascii ?? defaultTextFormatting.fontFamily?.hAnsi;
402
+ const fontFamily = defaultTextFormatting.fontFamily ? resolveWesternThemeFont(defaultTextFormatting.fontFamily, theme) : void 0;
385
403
  if (fontFamily) result.fontFamily = fontFamily;
386
- if (defaultTextFormatting.fontFamily?.eastAsia) result.eastAsiaFontFamily = defaultTextFormatting.fontFamily.eastAsia;
404
+ const eastAsiaFontFamily = defaultTextFormatting.fontFamily ? resolveEastAsiaThemeFont(defaultTextFormatting.fontFamily, theme) : void 0;
405
+ if (eastAsiaFontFamily) result.eastAsiaFontFamily = eastAsiaFontFamily;
387
406
  if (defaultTextFormatting.language) result.language = { ...defaultTextFormatting.language };
388
407
  if (defaultTextFormatting.fontSize !== void 0) result.fontSize = defaultTextFormatting.fontSize / 2;
389
408
  if (defaultTextFormatting.bold !== void 0) result.bold = defaultTextFormatting.bold;
@@ -444,6 +463,9 @@ function buildImageRun(attrs, constrained, pmStart, pmEnd, trackedChange) {
444
463
  if (attrs.cropRight != null) run.cropRight = attrs.cropRight;
445
464
  if (attrs.cropBottom != null) run.cropBottom = attrs.cropBottom;
446
465
  if (attrs.cropLeft != null) run.cropLeft = attrs.cropLeft;
466
+ if (attrs.borderWidth != null) run.borderWidth = attrs.borderWidth;
467
+ if (attrs.borderColor) run.borderColor = attrs.borderColor;
468
+ if (attrs.borderStyle) run.borderStyle = attrs.borderStyle;
447
469
  if (attrs.position !== void 0) run.position = attrs.position;
448
470
  if (attrs.layoutInCell !== void 0) run.layoutInCell = attrs.layoutInCell;
449
471
  if (trackedChange?.isInsertion) run.isInsertion = true;
@@ -488,7 +510,21 @@ function paragraphToRuns(node, startPos, _options) {
488
510
  const paraDefaults = paragraphRunDefaults(pmAttrs, theme);
489
511
  const paragraphStyleId = pmAttrs.styleId;
490
512
  const inTocParagraph = typeof paragraphStyleId === "string" && TOC_STYLE_ID.test(paragraphStyleId);
513
+ let leadingRenderedPageBreakPending = pmAttrs.renderedPageBreakBefore === true;
491
514
  function pushRunsForChild(child, childPos) {
515
+ if (child.type.name === "renderedPageBreak") {
516
+ if (leadingRenderedPageBreakPending) {
517
+ leadingRenderedPageBreakPending = false;
518
+ return;
519
+ }
520
+ runs.push({
521
+ kind: "renderedPageBreak",
522
+ pmStart: childPos,
523
+ pmEnd: childPos + child.nodeSize
524
+ });
525
+ return;
526
+ }
527
+ if (child.type.name !== "sdt") leadingRenderedPageBreakPending = false;
492
528
  if (child.isText && child.text) {
493
529
  const formatting = extractRunFormatting(child.marks, theme);
494
530
  if (inTocParagraph) stripTocHyperlinkStyle(formatting);
@@ -700,6 +736,7 @@ function convertParagraphAttrs(pmAttrs, theme, listCounters, listAbstractCounter
700
736
  const spaceBefore = pmAttrs.spaceBefore;
701
737
  const spaceAfter = pmAttrs.spaceAfter;
702
738
  const lineSpacing = pmAttrs.lineSpacing;
739
+ if (typeof pmAttrs.snapToGrid === "boolean") attrs.snapToGrid = pmAttrs.snapToGrid;
703
740
  const autoBefore = autospacingMatchesBase(pmAttrs._autospacingBase, "before", spaceBefore);
704
741
  const autoAfter = autospacingMatchesBase(pmAttrs._autospacingBase, "after", spaceAfter);
705
742
  if (autoBefore || autoAfter || typeof spaceBefore === "number" || typeof spaceAfter === "number" || typeof lineSpacing === "number") {
@@ -714,9 +751,10 @@ function convertParagraphAttrs(pmAttrs, theme, listCounters, listAbstractCounter
714
751
  };
715
752
  const pmSpacingExplicit = pmAttrs.spacingExplicit;
716
753
  const spacingFromDocDefaults = pmAttrs.spacingFromDocDefaults;
754
+ const spacingFromImplicitDefaultStyle = pmAttrs.spacingFromImplicitDefaultStyle;
717
755
  const explicit = {};
718
- if (autoBefore || pmSpacingExplicit?.before || spacingFromDocDefaults?.before) explicit.before = true;
719
- if (autoAfter || pmSpacingExplicit?.after || spacingFromDocDefaults?.after) explicit.after = true;
756
+ if (autoBefore || pmSpacingExplicit?.before || spacingFromDocDefaults?.before || spacingFromImplicitDefaultStyle?.before) explicit.before = true;
757
+ if (autoAfter || pmSpacingExplicit?.after || spacingFromDocDefaults?.after || spacingFromImplicitDefaultStyle?.after) explicit.after = true;
720
758
  if (explicit.before !== void 0 || explicit.after !== void 0) attrs.spacingExplicit = explicit;
721
759
  if (typeof lineSpacing === "number") if (pmAttrs.lineSpacingRule === "exact" || pmAttrs.lineSpacingRule === "atLeast") {
722
760
  attrs.spacing.line = twipsToPixels(lineSpacing);
@@ -831,8 +869,9 @@ function convertParagraphAttrs(pmAttrs, theme, listCounters, listAbstractCounter
831
869
  const dtf = pmAttrs.defaultTextFormatting;
832
870
  if (dtf) {
833
871
  if (dtf.fontSize !== void 0) attrs.defaultFontSize = dtf.fontSize / 2;
872
+ if (attrs.listMarker !== void 0 && !attrs.listMarkerHidden && dtf.fontSizeCs !== void 0 && (dtf.fontSize === void 0 || dtf.fontSizeCs > dtf.fontSize)) attrs.listParagraphMarkFontSize = dtf.fontSizeCs / 2;
834
873
  if (dtf.fontFamily) {
835
- const resolvedFamily = dtf.fontFamily.ascii || dtf.fontFamily.hAnsi;
874
+ const resolvedFamily = resolveWesternThemeFont(dtf.fontFamily, theme);
836
875
  if (resolvedFamily) attrs.defaultFontFamily = resolvedFamily;
837
876
  }
838
877
  }
@@ -856,19 +895,23 @@ function mapTabAlignment(align) {
856
895
  /**
857
896
  * Convert a paragraph node to a ParagraphBlock.
858
897
  */
898
+ function hasOnlyVisuallyEmptyTextRuns(runs) {
899
+ return runs.length > 0 && runs.every((run) => run.kind === "text" && run.text.replace(/\u00a0/gu, " ").trim().length === 0);
900
+ }
859
901
  function convertParagraph(node, startPos, options) {
860
902
  const pmAttrs = expectParagraphAttrs(node);
861
903
  const runs = paragraphToRuns(node, startPos, options);
862
904
  const attrs = convertParagraphAttrs(pmAttrs, options.theme, options.listCounters, options.listAbstractCounters, options.listSeenNumIds, options.defaultTabStopTwips, options.originalListCounters, options.originalListAbstractCounters, options.originalListSeenNumIds);
863
905
  if (options.lineBreakRules) attrs.lineBreakRules = options.lineBreakRules;
906
+ if (options.justificationCompatibility) attrs.justificationCompatibility = options.justificationCompatibility;
864
907
  if (options.automaticHyphenation) attrs.automaticHyphenation = options.automaticHyphenation;
865
908
  const defaultTextFormatting = pmAttrs.defaultTextFormatting;
866
- if (runs.length === 0) {
909
+ if (runs.length === 0 || hasOnlyVisuallyEmptyTextRuns(runs)) {
867
910
  if (pmAttrs._originalFormatting && Object.entries(pmAttrs._originalFormatting).some(([key, value]) => key !== "runProperties" && value !== void 0 && value !== null)) attrs.hasDirectParagraphFormatting = true;
868
911
  const paragraphMarkFormatting = pmAttrs._originalFormatting?.runProperties;
869
912
  if (paragraphMarkFormatting && Object.values(paragraphMarkFormatting).some((value) => value !== void 0 && value !== null)) attrs.hasDirectParagraphMarkFormatting = true;
870
913
  if (paragraphMarkFormatting?.fontSize !== void 0) attrs.defaultFontSize = paragraphMarkFormatting.fontSize / 2;
871
- const paragraphMarkFontFamily = paragraphMarkFormatting?.fontFamily?.ascii ?? paragraphMarkFormatting?.fontFamily?.hAnsi;
914
+ const paragraphMarkFontFamily = paragraphMarkFormatting?.fontFamily ? resolveWesternThemeFont(paragraphMarkFormatting.fontFamily, options.theme) : void 0;
872
915
  if (paragraphMarkFontFamily) attrs.defaultFontFamily = paragraphMarkFontFamily;
873
916
  }
874
917
  if (runs.length === 0 && defaultTextFormatting?.hidden === true) {
@@ -1037,12 +1080,13 @@ function convertTableCell(node, startPos, options, tableCellMargins) {
1037
1080
  suppressEmptyParagraphHeight: true
1038
1081
  };
1039
1082
  const attrs = expectTableCellAttrs(node);
1083
+ if (attrs.hideMark && trailingBlock?.kind === "paragraph" && trailingBlock.runs.every((run) => run.kind === "text" && run.text.length === 0)) trailingBlock.attrs = {
1084
+ ...trailingBlock.attrs,
1085
+ suppressEmptyParagraphHeight: true
1086
+ };
1040
1087
  const margins = attrs.margins;
1041
1088
  const resolvePaddingSide = (side, cellTwips, tableTwips) => {
1042
- if (cellTwips !== void 0) {
1043
- const px = twipsToPixels(cellTwips);
1044
- if (px > 0) return px;
1045
- }
1089
+ if (cellTwips !== void 0) return twipsToPixels(cellTwips);
1046
1090
  if (tableTwips !== void 0) return twipsToPixels(tableTwips);
1047
1091
  return twipsToPixels(DEFAULT_TABLE_CELL_MARGIN_TWIPS[side]);
1048
1092
  };
@@ -1182,6 +1226,9 @@ function convertImage(node, startPos, pageContentHeight) {
1182
1226
  if (attrs.cropRight != null) imgBlock.cropRight = attrs.cropRight;
1183
1227
  if (attrs.cropBottom != null) imgBlock.cropBottom = attrs.cropBottom;
1184
1228
  if (attrs.cropLeft != null) imgBlock.cropLeft = attrs.cropLeft;
1229
+ if (attrs.borderWidth != null) imgBlock.borderWidth = attrs.borderWidth;
1230
+ if (attrs.borderColor) imgBlock.borderColor = attrs.borderColor;
1231
+ if (attrs.borderStyle) imgBlock.borderStyle = attrs.borderStyle;
1185
1232
  return imgBlock;
1186
1233
  }
1187
1234
  /**
@@ -1191,10 +1238,12 @@ function convertTextBoxNode(node, startPos, opts) {
1191
1238
  const attrs = expectTextBoxAttrs(node);
1192
1239
  const contentBlocks = [];
1193
1240
  node.forEach((child, offset) => {
1241
+ const childPos = startPos + 1 + offset;
1194
1242
  if (child.type.name === "paragraph") {
1195
- const block = convertParagraph(child, startPos + 1 + offset, opts);
1196
- contentBlocks.push(block);
1243
+ contentBlocks.push(convertParagraph(child, childPos, opts));
1244
+ return;
1197
1245
  }
1246
+ if (child.type.name === "table") contentBlocks.push(convertTable(child, childPos, opts));
1198
1247
  });
1199
1248
  const textBox = {
1200
1249
  kind: "textBox",
@@ -1271,6 +1320,23 @@ function toFlowBlocks(doc, options = {}) {
1271
1320
  tagBlockWithSdtStack(block);
1272
1321
  blocks.push(block);
1273
1322
  };
1323
+ const trailingPageBreakSectionPositions = /* @__PURE__ */ new Set();
1324
+ const consumedPageBreakPositions = /* @__PURE__ */ new Set();
1325
+ const collectTrailingPageBreakSections = (parent, contentStart) => {
1326
+ let childStart = contentStart;
1327
+ for (let index = 0; index < parent.childCount; index += 1) {
1328
+ const child = parent.child(index);
1329
+ const next = index + 1 < parent.childCount ? parent.child(index + 1) : void 0;
1330
+ const paragraphAttrs = child.type.name === "paragraph" ? expectParagraphAttrs(child) : void 0;
1331
+ if (paragraphAttrs && next?.type.name === "pageBreak" && paragraphAttrs._trailingPageBreak === true && (paragraphAttrs._sectionProperties || paragraphAttrs.sectionBreakType)) {
1332
+ trailingPageBreakSectionPositions.add(childStart);
1333
+ consumedPageBreakPositions.add(childStart + child.nodeSize);
1334
+ }
1335
+ if (child.type.name === "blockSdt") collectTrailingPageBreakSections(child, childStart + 1);
1336
+ childStart += child.nodeSize;
1337
+ }
1338
+ };
1339
+ collectTrailingPageBreakSections(doc, offset);
1274
1340
  const visit = (node, pos) => {
1275
1341
  switch (node.type.name) {
1276
1342
  case "blockSdt": {
@@ -1356,6 +1422,41 @@ function toFlowBlocks(doc, options = {}) {
1356
1422
  trackedPush(paragraph);
1357
1423
  } else if (node.content.size > 0 || hasListFormatting || !hasSectionBreak) trackedPush(convertParagraph(node, pos, opts));
1358
1424
  if (hasSectionBreak) {
1425
+ if (trailingPageBreakSectionPositions.has(pos)) {
1426
+ const sourceParagraph = blocks.at(-1);
1427
+ if (sourceParagraph?.kind === "paragraph" && sourceParagraph.pmStart === pos) {
1428
+ const pageBreak = {
1429
+ kind: "pageBreak",
1430
+ id: nextBlockId(),
1431
+ pmStart: pos + node.nodeSize,
1432
+ pmEnd: pos + node.nodeSize + 1
1433
+ };
1434
+ trackedPush(pageBreak);
1435
+ const sourceAttrs = sourceParagraph.attrs;
1436
+ const carrierSpacing = sourceAttrs?.spacing ? { ...sourceAttrs.spacing } : void 0;
1437
+ if (carrierSpacing) delete carrierSpacing.before;
1438
+ const carrierAttrs = {
1439
+ ...carrierSpacing ? { spacing: carrierSpacing } : {},
1440
+ ...sourceAttrs?.automaticSpacing?.after === true ? { automaticSpacing: { after: true } } : {},
1441
+ ...sourceAttrs?.spacingExplicit?.after === true ? { spacingExplicit: { after: true } } : {},
1442
+ ...sourceAttrs?.hasDirectParagraphFormatting === true ? { hasDirectParagraphFormatting: true } : {},
1443
+ ...sourceAttrs?.hasDirectParagraphMarkFormatting === true ? { hasDirectParagraphMarkFormatting: true } : {},
1444
+ ...sourceAttrs?.snapToGrid !== void 0 ? { snapToGrid: sourceAttrs.snapToGrid } : {},
1445
+ ...sourceAttrs?.documentGridLinePitch !== void 0 ? { documentGridLinePitch: sourceAttrs.documentGridLinePitch } : {},
1446
+ ...sourceAttrs?.defaultFontSize !== void 0 ? { defaultFontSize: sourceAttrs.defaultFontSize } : {},
1447
+ ...sourceAttrs?.defaultFontFamily !== void 0 ? { defaultFontFamily: sourceAttrs.defaultFontFamily } : {}
1448
+ };
1449
+ const carrier = {
1450
+ kind: "paragraph",
1451
+ id: nextBlockId(),
1452
+ runs: [],
1453
+ attrs: carrierAttrs,
1454
+ pmStart: pos + node.nodeSize,
1455
+ pmEnd: pos + node.nodeSize
1456
+ };
1457
+ trackedPush(carrier);
1458
+ }
1459
+ }
1359
1460
  const sectionBreak = {
1360
1461
  kind: "sectionBreak",
1361
1462
  id: nextBlockId()
@@ -1363,6 +1464,8 @@ function toFlowBlocks(doc, options = {}) {
1363
1464
  const breakType = secProps?.sectionStart ?? pmAttrs.sectionBreakType;
1364
1465
  if (breakType) sectionBreak.type = breakType;
1365
1466
  if (secProps) {
1467
+ const documentGridLinePitchTwips = resolveDocumentGridLinePitch(secProps.docGrid);
1468
+ if (documentGridLinePitchTwips !== void 0) sectionBreak.documentGridLinePitchTwips = documentGridLinePitchTwips;
1366
1469
  if (secProps.pageWidth || secProps.pageHeight) sectionBreak.pageSize = {
1367
1470
  w: twipsToPixels(secProps.pageWidth ?? 12240),
1368
1471
  h: twipsToPixels(secProps.pageHeight ?? 15840)
@@ -1401,6 +1504,7 @@ function toFlowBlocks(doc, options = {}) {
1401
1504
  break;
1402
1505
  case "horizontalRule":
1403
1506
  case "pageBreak": {
1507
+ if (consumedPageBreakPositions.has(pos)) break;
1404
1508
  const pb = {
1405
1509
  kind: "pageBreak",
1406
1510
  id: nextBlockId(),
@@ -1419,7 +1523,34 @@ function toFlowBlocks(doc, options = {}) {
1419
1523
  reserveLeadingEmptyOutlineHeight(blocks);
1420
1524
  suppressTerminalEmptyParagraphsAfterTable(blocks);
1421
1525
  suppressFinalParagraphInRepeatedEmptySuffix(blocks);
1422
- return groupParagraphFrames(mergeRunInParagraphs(blocks), nextBlockId);
1526
+ return groupParagraphFrames(applySectionDocumentGrid(mergeRunInParagraphs(blocks), opts.finalSectionDocumentGridLinePitchTwips), nextBlockId);
1527
+ }
1528
+ function applySectionDocumentGrid(blocks, finalLinePitchTwips) {
1529
+ const result = [...blocks];
1530
+ let sectionStart = 0;
1531
+ const stampSection = (end, linePitchTwips) => {
1532
+ if (linePitchTwips === void 0 || linePitchTwips <= 0) return;
1533
+ const linePitch = twipsToPixels(linePitchTwips);
1534
+ for (let index = sectionStart; index < end; index += 1) {
1535
+ const block = result[index];
1536
+ if (block?.kind !== "paragraph") continue;
1537
+ result[index] = {
1538
+ ...block,
1539
+ attrs: {
1540
+ ...block.attrs,
1541
+ documentGridLinePitch: linePitch
1542
+ }
1543
+ };
1544
+ }
1545
+ };
1546
+ for (let index = 0; index < blocks.length; index += 1) {
1547
+ const block = blocks[index];
1548
+ if (block?.kind !== "sectionBreak") continue;
1549
+ stampSection(index, block.documentGridLinePitchTwips);
1550
+ sectionStart = index + 1;
1551
+ }
1552
+ stampSection(blocks.length, finalLinePitchTwips);
1553
+ return result;
1423
1554
  }
1424
1555
  /**
1425
1556
  * Merge consecutive paragraph blocks where the first carries
@@ -0,0 +1,6 @@
1
+ import { NoteStoryKey } from "../../controller/noteEditorManager.js";
2
+
3
+ //#region src/layout-bridge/dom/noteStoryDom.d.ts
4
+ declare const findNoteStoryForTarget: (target: unknown) => NoteStoryKey | null;
5
+ //#endregion
6
+ export { findNoteStoryForTarget };
@@ -0,0 +1,17 @@
1
+ //#region src/layout-bridge/dom/noteStoryDom.ts
2
+ const isClosestNoteTarget = (value) => typeof value === "object" && value !== null && "closest" in value && typeof value.closest === "function";
3
+ const findNoteStoryForTarget = (target) => {
4
+ if (!isClosestNoteTarget(target)) return null;
5
+ const noteTarget = target.closest("[data-note-kind][data-note-id]");
6
+ const kind = noteTarget?.dataset["noteKind"];
7
+ const rawNoteId = noteTarget?.dataset["noteId"] ?? "";
8
+ if (!/^-?\d+$/u.test(rawNoteId)) return null;
9
+ const noteId = Number(rawNoteId);
10
+ if (kind !== "footnote" && kind !== "endnote" || !Number.isSafeInteger(noteId)) return null;
11
+ return {
12
+ kind,
13
+ noteId
14
+ };
15
+ };
16
+ //#endregion
17
+ export { findNoteStoryForTarget };
@@ -72,7 +72,7 @@ function computeLinePmRange(block, line) {
72
72
  if (!run) continue;
73
73
  if (runIndex < line.fromRun) {
74
74
  if (run.kind === "text") charOffset += run.text.length;
75
- else if (run.kind === "tab" || run.kind === "lineBreak") charOffset += 1;
75
+ else if (run.kind === "tab" || run.kind === "lineBreak" || run.kind === "renderedPageBreak") charOffset += 1;
76
76
  else if (run.kind === "image") charOffset += 1;
77
77
  } else if (runIndex === line.fromRun) {
78
78
  charOffset += line.fromChar;
@@ -90,7 +90,7 @@ function computeLinePmRange(block, line) {
90
90
  const start = runIndex === line.fromRun ? line.fromChar : 0;
91
91
  const end = runIndex === line.toRun ? line.toChar : text.length;
92
92
  lineLength += end - start;
93
- } else if (run.kind === "tab" || run.kind === "lineBreak") lineLength += 1;
93
+ } else if (run.kind === "tab" || run.kind === "lineBreak" || run.kind === "renderedPageBreak") lineLength += 1;
94
94
  else if (run.kind === "image") lineLength += 1;
95
95
  }
96
96
  pmEnd = pmStart + lineLength;
@@ -1,4 +1,4 @@
1
- import { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned } from "./types.js";
1
+ import { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned } from "./types.js";
2
2
  import { PageState, Paginator, PaginatorOptions, createPaginator } from "./paginator.js";
3
3
  import { KeepNextChain, calculateChainHeight, computeKeepNextChains, getMidChainIndices, hasKeepLines, hasPageBreakBefore } from "./keep-together.js";
4
4
  import { resolveSectionHeaderFooterRefs } from "./headerFooterRefs.js";
@@ -44,4 +44,4 @@ declare function layoutDocument(blocks: FlowBlock[], measures: Measure[], option
44
44
  */
45
45
  declare function getHeaderRowsHeight(measure: TableMeasure, headerRowCount: number): number;
46
46
  //#endregion
47
- export { BlockId, BorderStyle, type BreakDecision, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, type KeepNextChain, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, type PageState, type Paginator, type PaginatorOptions, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionLayoutConfig, type SectionState, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, applyContextualSpacing, applyPendingToActive, assertExhaustiveFlowBlock, calculateChainHeight, collectSectionConfigs, computeKeepNextChains, createInitialSectionState, createPaginator, findPageIndexContainingPmPos, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, getHeaderRowsHeight, getMidChainIndices, getTableRowLeadingWidth, hasKeepLines, hasPageBreakBefore, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, layoutDocument, resolveSectionHeaderFooterRefs, scheduleSectionBreak, tableColumnsArePinned };
47
+ export { BlockId, BorderStyle, type BreakDecision, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, type KeepNextChain, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, type PageState, type Paginator, type PaginatorOptions, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionLayoutConfig, type SectionState, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, applyContextualSpacing, applyPendingToActive, assertExhaustiveFlowBlock, calculateChainHeight, collectSectionConfigs, computeKeepNextChains, createInitialSectionState, createPaginator, findPageIndexContainingPmPos, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, getHeaderRowsHeight, getMidChainIndices, getTableRowLeadingWidth, hasKeepLines, hasPageBreakBefore, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, layoutDocument, resolveSectionHeaderFooterRefs, scheduleSectionBreak, tableColumnsArePinned };
@@ -1,9 +1,8 @@
1
1
  import { emuToPixels } from "../utils/units.js";
2
2
  import { measuredLineAdvance } from "./lineFlow.js";
3
3
  import { DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned } from "./types.js";
4
- import { getParagraphSpacingAfter, getParagraphSpacingBefore } from "./paragraphSpacing.js";
4
+ import { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore, paragraphsShareStyle, resolveEffectiveParagraphSpacingTree } from "./paragraphSpacing.js";
5
5
  import { calculateChainHeight, computeKeepNextChains, getMidChainIndices, hasKeepLines, hasPageBreakBefore } from "./keep-together.js";
6
- import { continuesNumberedSequence } from "./paragraphSequence.js";
7
6
  import { createPaginator } from "./paginator.js";
8
7
  import { getParagraphFragmentPmRange } from "./paragraphFragmentRange.js";
9
8
  import { INITIAL_RENDERED_BREAK_STATE, reconcileAfterBlock, reconcileBreakBeforeBlock, recordReflowBoundary } from "./renderedBreakReconciliation.js";
@@ -19,6 +18,7 @@ import { panic } from "better-result";
19
18
  *
20
19
  * Converts blocks + measures into positioned fragments on pages.
21
20
  */
21
+ const RENDERED_BREAK_REFLOW_TOLERANCE_LINES = 3;
22
22
  const DEFAULT_COLUMNS = {
23
23
  count: 1,
24
24
  gap: 0
@@ -58,7 +58,10 @@ function balancedParagraphSectionHeight({ blocks, measures, startIndex, endIndex
58
58
  const measure = measures[index];
59
59
  if (block?.kind !== "paragraph" || measure?.kind !== "paragraph") return;
60
60
  if (block.attrs?.keepNext === true || block.attrs?.keepLines === true || block.runs.some((run) => run.kind === "text" && run.footnoteRefId !== void 0)) return;
61
- const leadingSpacing = Math.max(getParagraphSpacingBefore(block), trailingSpacing);
61
+ const leadingSpacing = collapseParagraphSpacing({
62
+ before: getParagraphSpacingBefore(block),
63
+ after: trailingSpacing
64
+ });
62
65
  if (measure.lines.length === 0) {
63
66
  if (leadingSpacing > 0) {
64
67
  lineUnits.push(leadingSpacing);
@@ -117,7 +120,7 @@ function applyContextualSpacing(blocks) {
117
120
  if (curr.kind !== "paragraph" || next.kind !== "paragraph") continue;
118
121
  const currAttrs = curr.attrs;
119
122
  const nextAttrs = next.attrs;
120
- if (currAttrs?.styleId !== nextAttrs?.styleId) continue;
123
+ if (!paragraphsShareStyle(curr, next)) continue;
121
124
  if (currAttrs?.contextualSpacing && currAttrs.spacing) currAttrs.spacing = {
122
125
  ...currAttrs.spacing,
123
126
  after: 0
@@ -130,24 +133,6 @@ function applyContextualSpacing(blocks) {
130
133
  for (const block of blocks) if (block.kind === "table") for (const row of block.rows) for (const cell of row.cells) applyContextualSpacing(cell.blocks);
131
134
  else if (block.kind === "textBox") applyContextualSpacing(block.content);
132
135
  }
133
- /** Suppress automatic inter-item spacing within one numbered sequence. */
134
- const applyAutomaticListSpacing = (blocks) => {
135
- for (let index = 1; index < blocks.length; index++) {
136
- const previous = blocks[index - 1];
137
- const current = blocks[index];
138
- if (previous?.kind !== "paragraph" || current?.kind !== "paragraph" || !continuesNumberedSequence(previous, current)) continue;
139
- if (previous.attrs?.automaticSpacing?.after && previous.attrs.spacing) previous.attrs.spacing = {
140
- ...previous.attrs.spacing,
141
- after: 0
142
- };
143
- if (current.attrs?.automaticSpacing?.before && current.attrs.spacing) current.attrs.spacing = {
144
- ...current.attrs.spacing,
145
- before: 0
146
- };
147
- }
148
- for (const block of blocks) if (block.kind === "table") for (const row of block.rows) for (const cell of row.cells) applyAutomaticListSpacing(cell.blocks);
149
- else if (block.kind === "textBox") applyAutomaticListSpacing(block.content);
150
- };
151
136
  /**
152
137
  * Layout a document: convert blocks + measures into pages with positioned fragments.
153
138
  *
@@ -196,8 +181,7 @@ function layoutDocument(blocks, measures, options) {
196
181
  ...options.footnoteReservedHeights !== void 0 ? { footnoteReservedHeights: options.footnoteReservedHeights } : {},
197
182
  ...options.sectionHeaderFooterRefs !== void 0 ? { sectionHeaderFooterRefs: options.sectionHeaderFooterRefs } : {}
198
183
  });
199
- applyContextualSpacing(blocks);
200
- applyAutomaticListSpacing(blocks);
184
+ blocks = resolveEffectiveParagraphSpacingTree(blocks);
201
185
  const keepNextChains = computeKeepNextChains(blocks);
202
186
  const midChainIndices = getMidChainIndices(keepNextChains);
203
187
  const blocksById = /* @__PURE__ */ new Map();
@@ -210,7 +194,9 @@ function layoutDocument(blocks, measures, options) {
210
194
  for (let i = 0; i < blocks.length; i++) {
211
195
  const block = blocks[i];
212
196
  const measure = measures[i];
213
- const renderedBreakNeedsSnap = measure.kind === "paragraph" && !paginator.fits(measure.totalHeight);
197
+ const firstLine = measure.kind === "paragraph" ? measure.lines.at(0) : void 0;
198
+ const firstLineAdvance = firstLine ? measuredLineAdvance(firstLine) : 0;
199
+ const renderedBreakNeedsSnap = measure.kind === "paragraph" && (!paginator.fits(measure.totalHeight) || firstLineAdvance > 0 && paginator.getAvailableHeight() <= firstLineAdvance * RENDERED_BREAK_REFLOW_TOLERANCE_LINES);
214
200
  const hasExplicitPageBreak = hasPageBreakBefore(block);
215
201
  const breakDecision = reconcileBreakBeforeBlock({
216
202
  state: renderedBreakState,
@@ -370,11 +356,20 @@ function layoutParagraph({ block, measure, paginator, contentWidth, footnoteHeig
370
356
  while (currentLineIndex < lines.length) {
371
357
  const state = paginator.getCurrentState();
372
358
  const availableHeight = paginator.getAvailableHeight();
359
+ const currentLine = lines.at(currentLineIndex);
360
+ const markerAlreadySatisfied = state.columnIndex === 0 && state.cursorY === state.topMargin && state.page.fragments.length === 0;
361
+ if (currentLine?.renderedPageBreakBefore === true && !markerAlreadySatisfied && availableHeight <= measuredLineAdvance(currentLine) * RENDERED_BREAK_REFLOW_TOLERANCE_LINES) {
362
+ paginator.forcePageBreak();
363
+ continue;
364
+ }
373
365
  if (currentLineIndex === 0 && state.trailingSpacing > 0 && state.cursorY !== state.topMargin) {
374
366
  const firstLine = lines[0];
375
367
  const firstLineRefs = getLineFootnoteRefs(block, firstLine.fromRun, firstLine.toRun, footnoteHeightById);
376
368
  const firstLineHeight = measuredLineAdvance(firstLine) + firstLineRefs.height;
377
- const collapsedLead = Math.max(spaceBefore, state.trailingSpacing);
369
+ const collapsedLead = collapseParagraphSpacing({
370
+ before: spaceBefore,
371
+ after: state.trailingSpacing
372
+ });
378
373
  const columnCapacity = state.contentBottom - state.topMargin;
379
374
  if (collapsedLead + firstLineHeight > availableHeight && spaceBefore + firstLineHeight <= columnCapacity) {
380
375
  paginator.ensureFits(collapsedLead + firstLineHeight);
@@ -385,10 +380,18 @@ function layoutParagraph({ block, measure, paginator, contentWidth, footnoteHeig
385
380
  let linesFnHeight = 0;
386
381
  const linesFnIds = [];
387
382
  let fittingLines = 0;
388
- const firstFragmentSpaceBefore = currentLineIndex === 0 ? Math.max(spaceBefore, state.trailingSpacing) : 0;
383
+ let forcePageBreakAfterFragment = false;
384
+ const firstFragmentSpaceBefore = currentLineIndex === 0 ? collapseParagraphSpacing({
385
+ before: spaceBefore,
386
+ after: state.trailingSpacing
387
+ }) : 0;
389
388
  for (let j = currentLineIndex; j < lines.length; j++) {
390
389
  const line = lines[j];
391
390
  const lineAdvance = measuredLineAdvance(line);
391
+ if (j > currentLineIndex && line.renderedPageBreakBefore === true && availableHeight - (linesHeight + firstFragmentSpaceBefore + linesFnHeight) <= lineAdvance * RENDERED_BREAK_REFLOW_TOLERANCE_LINES) {
392
+ forcePageBreakAfterFragment = true;
393
+ break;
394
+ }
392
395
  const lineRefs = getLineFootnoteRefs(block, line.fromRun, line.toRun, footnoteHeightById);
393
396
  const totalWithLine = linesHeight + lineAdvance;
394
397
  if (totalWithLine + firstFragmentSpaceBefore + linesFnHeight + lineRefs.height <= availableHeight || fittingLines === 0) {
@@ -447,7 +450,8 @@ function layoutParagraph({ block, measure, paginator, contentWidth, footnoteHeig
447
450
  fragment.y = paginator.addFragment(fragment, linesHeight, effectiveSpaceBefore, effectiveSpaceAfter).y;
448
451
  if (linesFnHeight > 0) paginator.addFootnoteHeight(linesFnHeight, linesFnIds);
449
452
  currentLineIndex += fittingLines;
450
- if (currentLineIndex < lines.length) if (forceBreakAfterFragment) paginator.forceColumnBreak();
453
+ if (currentLineIndex < lines.length) if (forcePageBreakAfterFragment) paginator.forcePageBreak();
454
+ else if (forceBreakAfterFragment) paginator.forceColumnBreak();
451
455
  else paginator.ensureFits(measuredLineAdvance(lines[currentLineIndex]));
452
456
  }
453
457
  }
@@ -469,6 +473,20 @@ function getHeaderRowsHeight(measure, headerRowCount) {
469
473
  for (let i = 0; i < headerRowCount && i < measure.rows.length; i++) height += measure.rows[i].height;
470
474
  return height;
471
475
  }
476
+ const tableRowStartsWithRenderedPageBreak = (block, rowIndex) => block.rows[rowIndex]?.cells.some((cell) => {
477
+ const firstBlock = cell.blocks.at(0);
478
+ return firstBlock?.kind === "paragraph" && firstBlock.attrs?.renderedPageBreakBefore === true;
479
+ }) ?? false;
480
+ const flowBlockHasTrackedChanges = (block) => {
481
+ if (block.kind === "paragraph") return block.runs.some((run) => {
482
+ if (run.kind === "lineBreak") return false;
483
+ return run.isInsertion || run.isDeletion;
484
+ });
485
+ if (block.kind === "table") return block.rows.some((row) => row.cells.some((cell) => cell.blocks.some(flowBlockHasTrackedChanges)));
486
+ if (block.kind === "textBox") return block.content.some(flowBlockHasTrackedChanges);
487
+ return false;
488
+ };
489
+ const tableRowHasTrackedChanges = (block, rowIndex) => block.rows[rowIndex]?.cells.some((cell) => cell.blocks.some(flowBlockHasTrackedChanges)) ?? false;
472
490
  /**
473
491
  * Layout a table block onto pages.
474
492
  */
@@ -511,6 +529,13 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
511
529
  return oversized || row.height > currentAvailableHeight;
512
530
  };
513
531
  while (currentRowIndex < rows.length) {
532
+ const rowState = paginator.getCurrentState();
533
+ const rowHeaderOverhead = shouldRepeatHeaderRows(currentRowIndex, 0, rowState) ? headerRowsHeight : 0;
534
+ const rowAvailableHeight = paginator.getAvailableHeight() - rowHeaderOverhead - rowState.trailingSpacing;
535
+ if (!(rowState.cursorY === rowState.topMargin && rowState.page.fragments.length === 0) && tableRowStartsWithRenderedPageBreak(block, currentRowIndex) && !tableRowHasTrackedChanges(block, currentRowIndex) && rows[currentRowIndex].height > rowAvailableHeight) {
536
+ paginator.forcePageBreak();
537
+ continue;
538
+ }
514
539
  const splittableRow = rows[currentRowIndex];
515
540
  if (canSplitRow(currentRowIndex)) {
516
541
  let consumed = 0;
@@ -642,7 +667,7 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
642
667
  }
643
668
  }
644
669
  function collectTableRowFootnoteIds(row, footnoteHeightById) {
645
- if (!row || !footnoteHeightById) return [];
670
+ if (!row || !footnoteHeightById || row.hidden) return [];
646
671
  const ids = [];
647
672
  const walk = (blocks) => {
648
673
  for (const block of blocks) {
@@ -651,7 +676,10 @@ function collectTableRowFootnoteIds(row, footnoteHeightById) {
651
676
  continue;
652
677
  }
653
678
  if (block.kind === "table") {
654
- for (const nestedRow of block.rows) for (const cell of nestedRow.cells) walk(cell.blocks);
679
+ for (const nestedRow of block.rows) {
680
+ if (nestedRow.hidden) continue;
681
+ for (const cell of nestedRow.cells) walk(cell.blocks);
682
+ }
655
683
  continue;
656
684
  }
657
685
  if (block.kind === "textBox") walk(block.content);
@@ -0,0 +1,6 @@
1
+ import { ParagraphAttrs } from "./types.js";
2
+
3
+ //#region src/layout-engine/justificationCompatibility.d.ts
4
+ declare function resolveJustificationCompatibility(compatibilityMode: number | undefined): NonNullable<ParagraphAttrs["justificationCompatibility"]> | undefined;
5
+ //#endregion
6
+ export { resolveJustificationCompatibility };
@@ -0,0 +1,8 @@
1
+ //#region src/layout-engine/justificationCompatibility.ts
2
+ const LEGACY_JUSTIFICATION_MAX_COMPATIBILITY_MODE = 14;
3
+ function resolveJustificationCompatibility(compatibilityMode) {
4
+ if (compatibilityMode === void 0 || compatibilityMode > LEGACY_JUSTIFICATION_MAX_COMPATIBILITY_MODE) return;
5
+ return { type: "legacy" };
6
+ }
7
+ //#endregion
8
+ export { resolveJustificationCompatibility };
@@ -1,4 +1,11 @@
1
+ import { collapseParagraphSpacing } from "./paragraphSpacing.js";
1
2
  //#region src/layout-engine/keep-together.ts
3
+ /**
4
+ * Keep Together Logic - Handle keepNext and keepLines paragraph properties
5
+ *
6
+ * DOCX paragraphs can have keepNext (keep with next paragraph) and keepLines
7
+ * (keep all lines together) properties that affect pagination.
8
+ */
2
9
  function isVisuallyEmptyParagraph(block) {
3
10
  if (block.runs.length === 0) return true;
4
11
  if (block.runs.length !== 1) return false;
@@ -87,7 +94,10 @@ function calculateChainHeight(chain, blocks, measures) {
87
94
  const successorBlock = blocks[successorIndex];
88
95
  const successorMeasure = measures[successorIndex];
89
96
  if (successorBlock?.kind !== "paragraph" || successorMeasure?.kind !== "paragraph") return totalHeight;
90
- totalHeight += Math.max(trailingSpacing, successorBlock.attrs?.spacing?.before ?? 0);
97
+ totalHeight += collapseParagraphSpacing({
98
+ before: successorBlock.attrs?.spacing?.before ?? 0,
99
+ after: trailingSpacing
100
+ });
91
101
  const firstLine = successorMeasure.lines.at(0);
92
102
  if (!firstLine) return totalHeight;
93
103
  const isAnchor = index === successorIndices.length - 1 && chain.anchorIndex !== -1;