@stll/folio-core 0.7.0 → 0.8.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 (92) hide show
  1. package/dist/ai-edits/blockRange.d.ts +13 -2
  2. package/dist/ai-edits/blockRange.js +23 -2
  3. package/dist/ai-edits/index.d.ts +4 -2
  4. package/dist/ai-edits/index.js +3 -1
  5. package/dist/ai-edits/scoped-reading.d.ts +9 -0
  6. package/dist/ai-edits/scoped-reading.js +60 -0
  7. package/dist/ai-edits/snapshot.js +13 -4
  8. package/dist/ai-edits/types.d.ts +44 -2
  9. package/dist/compat/eigenpal.d.ts +5 -2
  10. package/dist/compat/eigenpal.js +4 -1
  11. package/dist/controller/fontReadiness.d.ts +29 -0
  12. package/dist/controller/fontReadiness.js +139 -0
  13. package/dist/controller/layoutPipeline.js +76 -9
  14. package/dist/docx/blockContentParser.js +51 -8
  15. package/dist/docx/capabilities.d.ts +41 -0
  16. package/dist/docx/capabilities.js +322 -0
  17. package/dist/docx/compatibility.d.ts +34 -2
  18. package/dist/docx/compatibility.js +128 -38
  19. package/dist/docx/conformance.d.ts +6 -0
  20. package/dist/docx/conformance.js +18 -0
  21. package/dist/docx/ensureParaIds.d.ts +29 -0
  22. package/dist/docx/ensureParaIds.js +423 -0
  23. package/dist/docx/numberingParser.js +1 -0
  24. package/dist/docx/paragraphParser.js +9 -0
  25. package/dist/docx/paragraphTextBoxEnrichment.js +141 -2
  26. package/dist/docx/parser.js +2 -0
  27. package/dist/docx/selectiveXmlPatch.d.ts +10 -1
  28. package/dist/docx/selectiveXmlPatch.js +1 -1
  29. package/dist/docx/serializer/paragraphSerializer.js +4 -0
  30. package/dist/docx/serializer/runSerializer.js +7 -2
  31. package/dist/docx/serializer/tableSerializer.js +4 -0
  32. package/dist/docx/tableParser.js +8 -0
  33. package/dist/docx/textBoxParser.js +6 -1
  34. package/dist/docx/vmlImageParser.js +145 -1
  35. package/dist/index.d.ts +5 -2
  36. package/dist/index.js +4 -1
  37. package/dist/layout-bridge/convert/headerFooterLayout.js +16 -2
  38. package/dist/layout-bridge/convert/paragraphFrames.d.ts +7 -0
  39. package/dist/layout-bridge/convert/paragraphFrames.js +136 -0
  40. package/dist/layout-bridge/convert/toFlowBlocks.js +42 -3
  41. package/dist/layout-bridge/engine/hitTest.js +2 -1
  42. package/dist/layout-bridge/engine/selectionRects.js +2 -1
  43. package/dist/layout-engine/headerFooterRefs.d.ts +7 -0
  44. package/dist/layout-engine/headerFooterRefs.js +40 -0
  45. package/dist/layout-engine/index.d.ts +3 -2
  46. package/dist/layout-engine/index.js +36 -82
  47. package/dist/layout-engine/measure/listMarkerWidth.d.ts +3 -1
  48. package/dist/layout-engine/measure/listMarkerWidth.js +23 -4
  49. package/dist/layout-engine/measure/measureBlocks.d.ts +3 -0
  50. package/dist/layout-engine/measure/measureBlocks.js +133 -51
  51. package/dist/layout-engine/measure/measureParagraph.js +13 -4
  52. package/dist/layout-engine/measure/tableCellGrid.d.ts +15 -0
  53. package/dist/layout-engine/measure/tableCellGrid.js +62 -0
  54. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +18 -0
  55. package/dist/layout-engine/measure/textBoxParagraphLayout.js +32 -0
  56. package/dist/layout-engine/paginator.d.ts +2 -1
  57. package/dist/layout-engine/paginator.js +7 -6
  58. package/dist/layout-engine/paragraphFrame.d.ts +22 -0
  59. package/dist/layout-engine/paragraphFrame.js +17 -0
  60. package/dist/layout-engine/paragraphSequence.d.ts +7 -0
  61. package/dist/layout-engine/paragraphSequence.js +25 -0
  62. package/dist/layout-engine/paragraphSpacing.d.ts +17 -0
  63. package/dist/layout-engine/paragraphSpacing.js +30 -0
  64. package/dist/layout-engine/renderedBreakReconciliation.d.ts +58 -0
  65. package/dist/layout-engine/renderedBreakReconciliation.js +63 -0
  66. package/dist/layout-engine/types.d.ts +17 -8
  67. package/dist/layout-engine/types.js +15 -2
  68. package/dist/layout-painter/renderPage.js +2 -2
  69. package/dist/layout-painter/renderParagraph.js +6 -5
  70. package/dist/layout-painter/renderTable.js +42 -12
  71. package/dist/layout-painter/renderTextBox.js +17 -7
  72. package/dist/managers/DocumentLoaderManager.js +1 -1
  73. package/dist/paged-layout/sectionBlockWidths.d.ts +3 -0
  74. package/dist/paged-layout/sectionBlockWidths.js +9 -0
  75. package/dist/paged-layout/sectionGeometry.js +1 -1
  76. package/dist/prosemirror/attrs/index.js +11 -0
  77. package/dist/prosemirror/conversion/fromProseDoc.js +2 -0
  78. package/dist/prosemirror/conversion/toProseDoc.js +27 -23
  79. package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
  80. package/dist/prosemirror/extensions/features/ListExtension.js +1 -0
  81. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +68 -16
  82. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -1
  83. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +7 -0
  84. package/dist/prosemirror/schema/nodes.d.ts +4 -2
  85. package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
  86. package/dist/server.d.ts +4 -2
  87. package/dist/server.js +3 -1
  88. package/dist/types/index.d.ts +2 -1
  89. package/dist/utils/createDocument.js +26 -18
  90. package/dist/utils/hexId.d.ts +8 -1
  91. package/dist/utils/hexId.js +11 -3
  92. package/package.json +1 -1
@@ -16,6 +16,10 @@ function hasAuthoredVisualContent(block) {
16
16
  if (attrs.spacingExplicit?.before || attrs.spacingExplicit?.after) return true;
17
17
  return false;
18
18
  }
19
+ function preservesInheritedSpacing(block) {
20
+ if (!isVisuallyEmptyParagraph(block)) return false;
21
+ return block.attrs?.styleId !== void 0 || block.attrs?.hasDirectParagraphFormatting === true || block.attrs?.hasDirectParagraphMarkFormatting === true;
22
+ }
19
23
  function normalizeHeaderFooterMeasureBlocks(blocks, section = "header") {
20
24
  return normalizeFlowBlockArray(blocks, { suppressTrailingEmptyAfterTable: section === "header" });
21
25
  }
@@ -33,8 +37,9 @@ function normalizeFlowBlockArray(blocks, normalization) {
33
37
  const explicit = block.attrs?.spacingExplicit;
34
38
  const hasResolvedBefore = block.attrs?.spacing?.before != null;
35
39
  const hasResolvedAfter = block.attrs?.spacing?.after != null;
36
- const beforeIsInherited = hasResolvedBefore && !explicit?.before;
37
- const afterIsInherited = hasResolvedAfter && !explicit?.after;
40
+ const preserveInherited = preservesInheritedSpacing(block);
41
+ const beforeIsInherited = hasResolvedBefore && !explicit?.before && !preserveInherited;
42
+ const afterIsInherited = hasResolvedAfter && !explicit?.after && !preserveInherited;
38
43
  const stripsSpacing = beforeIsInherited || afterIsInherited;
39
44
  const stripsImages = block.runs.some(isAnchoredImageRun);
40
45
  if (!stripsSpacing && !stripsImages && !isTrailingEmpty) return block;
@@ -79,6 +84,10 @@ function isVisuallyEmptyParagraph(block) {
79
84
  if (block.kind !== "paragraph") return false;
80
85
  return block.runs.every((run) => run.kind === "text" && run.text.trim().length === 0);
81
86
  }
87
+ function isPaintlessParagraph(block) {
88
+ if (block.kind !== "paragraph") return false;
89
+ return block.runs.every((run) => run.kind === "text" && run.text.trim().length === 0 || run.kind === "tab" || run.kind === "lineBreak");
90
+ }
82
91
  function normalizeTableBlock(block) {
83
92
  const blockState = { changed: false };
84
93
  const rows = block.rows.map((row) => {
@@ -232,6 +241,10 @@ function calculateHeaderFooterVisualBounds(blocks, measures, flowHeight, metrics
232
241
  * these bounds separately describe the in-flow portion for API consumers.
233
242
  */
234
243
  function calculateHeaderFooterMarginPushBounds(blocks, measures, flowHeight, metrics) {
244
+ if (blocks.length > 0 && blocks.every((block) => isPaintlessParagraph(block) && !hasAuthoredVisualContent(block))) return {
245
+ top: 0,
246
+ bottom: 0
247
+ };
235
248
  let top = 0;
236
249
  let bottom = 0;
237
250
  let cursorY = 0;
@@ -422,6 +435,7 @@ function serializeParagraphAttrs(attrs) {
422
435
  "listMarker",
423
436
  "listIsBullet",
424
437
  "listMarkerHidden",
438
+ "listMarkerAlignment",
425
439
  "listMarkerSuffix",
426
440
  "tabs"
427
441
  ];
@@ -0,0 +1,7 @@
1
+ import { BlockId, FlowBlock } from "../../layout-engine/types.js";
2
+
3
+ //#region src/layout-bridge/convert/paragraphFrames.d.ts
4
+ type BlockIdFactory = () => BlockId;
5
+ declare function groupParagraphFrames(blocks: FlowBlock[], nextBlockId: BlockIdFactory): FlowBlock[];
6
+ //#endregion
7
+ export { groupParagraphFrames };
@@ -0,0 +1,136 @@
1
+ import { pixelsToEmu } from "../../utils/units.js";
2
+ import { setTextBoxGroupId } from "../../layout-engine/textBoxGroup.js";
3
+ import { getParagraphFrame, markParagraphFrameTextBox } from "../../layout-engine/paragraphFrame.js";
4
+ //#region src/layout-bridge/convert/paragraphFrames.ts
5
+ const FRAME_PROPERTIES = [
6
+ "width",
7
+ "height",
8
+ "hSpace",
9
+ "vSpace",
10
+ "hAnchor",
11
+ "vAnchor",
12
+ "x",
13
+ "y",
14
+ "xAlign",
15
+ "yAlign",
16
+ "wrap"
17
+ ];
18
+ function framesEqual(left, right) {
19
+ return FRAME_PROPERTIES.every((property) => left[property] === right[property]);
20
+ }
21
+ function horizontalRelativeTo(anchor) {
22
+ if (anchor === "page" || anchor === "margin") return anchor;
23
+ return "column";
24
+ }
25
+ function verticalRelativeTo(anchor) {
26
+ if (anchor === "page" || anchor === "margin") return anchor;
27
+ return "paragraph";
28
+ }
29
+ function framePosition(frame) {
30
+ const horizontal = frame.x !== void 0 || frame.xAlign !== void 0 || frame.hAnchor !== void 0 ? {
31
+ relativeTo: horizontalRelativeTo(frame.hAnchor),
32
+ ...frame.x !== void 0 ? { posOffset: pixelsToEmu(frame.x) } : {},
33
+ ...frame.xAlign !== void 0 ? { align: frame.xAlign } : {}
34
+ } : void 0;
35
+ const verticalAlign = frame.yAlign === "inline" ? void 0 : frame.yAlign;
36
+ const vertical = frame.y !== void 0 || verticalAlign !== void 0 || frame.vAnchor !== void 0 ? {
37
+ relativeTo: verticalRelativeTo(frame.vAnchor),
38
+ ...frame.y !== void 0 ? { posOffset: pixelsToEmu(frame.y) } : {},
39
+ ...verticalAlign !== void 0 ? { align: verticalAlign } : {}
40
+ } : void 0;
41
+ if (horizontal === void 0 && vertical === void 0) return;
42
+ return {
43
+ ...horizontal !== void 0 ? { horizontal } : {},
44
+ ...vertical !== void 0 ? { vertical } : {}
45
+ };
46
+ }
47
+ function frameWrapType(frame) {
48
+ switch (frame.wrap) {
49
+ case "notBeside": return "topAndBottom";
50
+ case "none": return "inFront";
51
+ case "through": return "through";
52
+ case "tight": return "tight";
53
+ case "around":
54
+ case "auto":
55
+ case void 0: return "square";
56
+ default:
57
+ frame.wrap;
58
+ return "square";
59
+ }
60
+ }
61
+ function toFrameTextBox(content, frame, nextBlockId) {
62
+ const first = content.at(0);
63
+ const last = content.at(-1);
64
+ const textBox = {
65
+ kind: "textBox",
66
+ id: nextBlockId(),
67
+ width: frame.width ?? 200,
68
+ margins: {
69
+ top: 0,
70
+ right: 0,
71
+ bottom: 0,
72
+ left: 0
73
+ },
74
+ content,
75
+ displayMode: "float",
76
+ wrapType: frameWrapType(frame),
77
+ wrapText: "bothSides",
78
+ distTop: frame.vSpace ?? 0,
79
+ distBottom: frame.vSpace ?? 0,
80
+ distLeft: frame.hSpace ?? 0,
81
+ distRight: frame.hSpace ?? 0,
82
+ ...first?.pmStart !== void 0 ? { pmStart: first.pmStart } : {},
83
+ ...last?.pmEnd !== void 0 ? { pmEnd: last.pmEnd } : {}
84
+ };
85
+ if (frame.height !== void 0) textBox.height = frame.height;
86
+ const position = framePosition(frame);
87
+ if (position !== void 0) textBox.position = position;
88
+ markParagraphFrameTextBox(textBox);
89
+ return textBox;
90
+ }
91
+ function groupParagraphFrames(blocks, nextBlockId) {
92
+ const grouped = [];
93
+ let index = 0;
94
+ let activeFrameSetId;
95
+ while (index < blocks.length) {
96
+ const block = blocks[index];
97
+ if (block?.kind !== "paragraph") {
98
+ if (block) grouped.push(block);
99
+ activeFrameSetId = void 0;
100
+ index += 1;
101
+ continue;
102
+ }
103
+ const frame = getParagraphFrame(block);
104
+ if (frame === void 0) {
105
+ grouped.push(block);
106
+ if (block.runs.length > 0 || activeFrameSetId === void 0 || !hasFollowingParagraphFrame(blocks, index + 1)) activeFrameSetId = void 0;
107
+ index += 1;
108
+ continue;
109
+ }
110
+ activeFrameSetId ??= `paragraph-frame-${block.pmStart ?? block.id}`;
111
+ const content = [];
112
+ while (index < blocks.length) {
113
+ const paragraph = blocks[index];
114
+ if (paragraph?.kind !== "paragraph") break;
115
+ const paragraphFrame = getParagraphFrame(paragraph);
116
+ if (paragraphFrame === void 0 || !framesEqual(frame, paragraphFrame)) break;
117
+ content.push(paragraph);
118
+ index += 1;
119
+ }
120
+ const textBox = toFrameTextBox(content, frame, nextBlockId);
121
+ setTextBoxGroupId(textBox, activeFrameSetId);
122
+ grouped.push(textBox);
123
+ }
124
+ return grouped;
125
+ }
126
+ function hasFollowingParagraphFrame(blocks, startIndex) {
127
+ for (let index = startIndex; index < blocks.length; index++) {
128
+ const block = blocks[index];
129
+ if (block?.kind !== "paragraph") return false;
130
+ if (getParagraphFrame(block) !== void 0) return true;
131
+ if (block.runs.length > 0) return false;
132
+ }
133
+ return false;
134
+ }
135
+ //#endregion
136
+ export { groupParagraphFrames };
@@ -6,12 +6,14 @@ import { resolveColor, resolveHighlightToCss } from "../../utils/colorResolver.j
6
6
  import { resolveShadingFill } from "../../utils/formatToStyle.js";
7
7
  import { convertBulletToUnicode } from "../../docx/bulletMarkers.js";
8
8
  import { setTextBoxGroupId } from "../../layout-engine/textBoxGroup.js";
9
+ import { setParagraphFrame } from "../../layout-engine/paragraphFrame.js";
9
10
  import { getColumns } from "../sectionColumns.js";
10
11
  import { directionIsRtl } from "../../prosemirror/paragraphDirection.js";
11
12
  import { expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectMathAttrs, expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs, expectRunShadingMarkAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs } from "../../prosemirror/attrs/index.js";
12
13
  import { autospacingMatchesBase } from "../../prosemirror/autospacingBase.js";
13
14
  import { runShadingAttrsToShading } from "../../prosemirror/conversion/runShadingMark.js";
14
15
  import { assertValidProseMirrorDocument } from "../../prosemirror/validation.js";
16
+ import { groupParagraphFrames } from "./paragraphFrames.js";
15
17
  //#region src/layout-bridge/convert/toFlowBlocks.ts
16
18
  const DEFAULT_FONT = "Calibri";
17
19
  const DEFAULT_TABLE_CELL_MARGIN_TWIPS = {
@@ -613,6 +615,9 @@ function readNumberFormats(value) {
613
615
  function readListMarkerSuffix(value) {
614
616
  if (value === "tab" || value === "space" || value === "nothing") return value;
615
617
  }
618
+ function readListMarkerAlignment(value) {
619
+ if (value === "left" || value === "center" || value === "right") return value;
620
+ }
616
621
  function toPreviousListAttrs(previousFormatting) {
617
622
  const attrs = {};
618
623
  const numPr = previousFormatting["numPr"];
@@ -639,6 +644,8 @@ function toPreviousListAttrs(previousFormatting) {
639
644
  if (typeof listMarkerFontFamily === "string") attrs.listMarkerFontFamily = listMarkerFontFamily;
640
645
  const listMarkerFontSize = previousFormatting["listMarkerFontSize"];
641
646
  if (typeof listMarkerFontSize === "number") attrs.listMarkerFontSize = listMarkerFontSize;
647
+ const listMarkerAlignment = readListMarkerAlignment(previousFormatting["listMarkerAlignment"]);
648
+ if (listMarkerAlignment) attrs.listMarkerAlignment = listMarkerAlignment;
642
649
  const listMarkerSuffix = readListMarkerSuffix(previousFormatting["listMarkerSuffix"]);
643
650
  if (listMarkerSuffix) attrs.listMarkerSuffix = listMarkerSuffix;
644
651
  return attrs;
@@ -662,6 +669,7 @@ function applyDeletedListMarkerAttrs(attrs, change, listCounters, listAbstractCo
662
669
  if (previousListAttrs.listMarkerHidden !== void 0) attrs.listMarkerHidden = previousListAttrs.listMarkerHidden;
663
670
  if (previousListAttrs.listMarkerFontFamily) attrs.listMarkerFontFamily = previousListAttrs.listMarkerFontFamily;
664
671
  if (previousListAttrs.listMarkerFontSize) attrs.listMarkerFontSize = previousListAttrs.listMarkerFontSize;
672
+ if (previousListAttrs.listMarkerAlignment) attrs.listMarkerAlignment = previousListAttrs.listMarkerAlignment;
665
673
  if (previousListAttrs.listMarkerSuffix) attrs.listMarkerSuffix = previousListAttrs.listMarkerSuffix;
666
674
  }
667
675
  function convertParagraphAttrs(pmAttrs, theme, listCounters, listAbstractCounters, listSeenNumIds, defaultTabStopTwips, originalListCounters, originalListAbstractCounters, originalListSeenNumIds) {
@@ -793,6 +801,7 @@ function convertParagraphAttrs(pmAttrs, theme, listCounters, listAbstractCounter
793
801
  if (pmAttrs.listMarkerHidden) attrs.listMarkerHidden = true;
794
802
  if (pmAttrs.listMarkerFontFamily) attrs.listMarkerFontFamily = pmAttrs.listMarkerFontFamily;
795
803
  if (pmAttrs.listMarkerFontSize) attrs.listMarkerFontSize = pmAttrs.listMarkerFontSize;
804
+ if (pmAttrs.listMarkerAlignment) attrs.listMarkerAlignment = pmAttrs.listMarkerAlignment;
796
805
  if (pmAttrs.listMarkerSuffix) attrs.listMarkerSuffix = pmAttrs.listMarkerSuffix;
797
806
  if (pmAttrs.listMarkerSecondSlotOffsetTwips !== void 0) attrs.listMarkerSecondSlotOffsetTwips = pmAttrs.listMarkerSecondSlotOffsetTwips;
798
807
  if (!pmAttrs.numPr) {
@@ -836,6 +845,7 @@ function convertParagraph(node, startPos, options) {
836
845
  if (runs.length === 0) {
837
846
  if (pmAttrs._originalFormatting && Object.entries(pmAttrs._originalFormatting).some(([key, value]) => key !== "runProperties" && value !== void 0 && value !== null)) attrs.hasDirectParagraphFormatting = true;
838
847
  const paragraphMarkFormatting = pmAttrs._originalFormatting?.runProperties;
848
+ if (paragraphMarkFormatting && Object.values(paragraphMarkFormatting).some((value) => value !== void 0 && value !== null)) attrs.hasDirectParagraphMarkFormatting = true;
839
849
  if (paragraphMarkFormatting?.fontSize !== void 0) attrs.defaultFontSize = paragraphMarkFormatting.fontSize / 2;
840
850
  const paragraphMarkFontFamily = paragraphMarkFormatting?.fontFamily?.ascii ?? paragraphMarkFormatting?.fontFamily?.hAnsi;
841
851
  if (paragraphMarkFontFamily) attrs.defaultFontFamily = paragraphMarkFontFamily;
@@ -845,7 +855,7 @@ function convertParagraph(node, startPos, options) {
845
855
  if (attrs.listMarker !== void 0) attrs.listMarkerHidden = true;
846
856
  }
847
857
  const bookmarkNames = pmAttrs.bookmarks?.map((b) => b.name);
848
- return {
858
+ const block = {
849
859
  kind: "paragraph",
850
860
  id: nextBlockId(),
851
861
  runs,
@@ -855,6 +865,21 @@ function convertParagraph(node, startPos, options) {
855
865
  pmStart: startPos,
856
866
  pmEnd: startPos + node.nodeSize
857
867
  };
868
+ const frame = pmAttrs._originalFormatting?.frame;
869
+ if (frame !== void 0 && frame.dropCap !== "drop" && frame.dropCap !== "margin") setParagraphFrame(block, {
870
+ ...frame.width !== void 0 ? { width: twipsToPixels(frame.width) } : {},
871
+ ...frame.height !== void 0 ? { height: twipsToPixels(frame.height) } : {},
872
+ ...frame.hSpace !== void 0 ? { hSpace: twipsToPixels(frame.hSpace) } : {},
873
+ ...frame.vSpace !== void 0 ? { vSpace: twipsToPixels(frame.vSpace) } : {},
874
+ ...frame.hAnchor !== void 0 ? { hAnchor: frame.hAnchor } : {},
875
+ ...frame.vAnchor !== void 0 ? { vAnchor: frame.vAnchor } : {},
876
+ ...frame.x !== void 0 ? { x: twipsToPixels(frame.x) } : {},
877
+ ...frame.y !== void 0 ? { y: twipsToPixels(frame.y) } : {},
878
+ ...frame.xAlign !== void 0 ? { xAlign: frame.xAlign } : {},
879
+ ...frame.yAlign !== void 0 ? { yAlign: frame.yAlign } : {},
880
+ ...frame.wrap !== void 0 ? { wrap: frame.wrap } : {}
881
+ });
882
+ return block;
858
883
  }
859
884
  /**
860
885
  * Word keeps terminal empty body paragraphs after a final table as editable
@@ -879,6 +904,16 @@ function suppressTerminalEmptyParagraphsAfterTable(blocks) {
879
904
  };
880
905
  }
881
906
  }
907
+ function suppressFinalParagraphInRepeatedEmptySuffix(blocks) {
908
+ let suffixStart = blocks.length;
909
+ while (suffixStart > 0 && isPaintlessTerminalParagraph(blocks[suffixStart - 1])) suffixStart -= 1;
910
+ if (suffixStart === 0 || blocks.length - suffixStart < 2 || blocks[suffixStart - 1]?.kind === "table") return;
911
+ const finalBlock = blocks.at(-1);
912
+ if (isPaintlessTerminalParagraph(finalBlock)) finalBlock.attrs = {
913
+ ...finalBlock.attrs,
914
+ suppressEmptyParagraphHeight: true
915
+ };
916
+ }
882
917
  function reserveLeadingEmptyOutlineHeight(blocks) {
883
918
  const firstBlock = blocks.at(0);
884
919
  if (firstBlock?.kind !== "paragraph" || firstBlock.runs.length !== 0 || firstBlock.attrs?.outlineLevel !== 0) return;
@@ -983,7 +1018,7 @@ function convertTableCell(node, startPos, options, tableCellMargins) {
983
1018
  };
984
1019
  const cell = {
985
1020
  id: nextBlockId(),
986
- blocks,
1021
+ blocks: groupParagraphFrames(blocks, nextBlockId),
987
1022
  colSpan: attrs.colspan,
988
1023
  rowSpan: attrs.rowspan,
989
1024
  padding
@@ -1011,6 +1046,8 @@ function convertTableRow(node, startPos, options, tableCellMargins) {
1011
1046
  id: nextBlockId(),
1012
1047
  cells
1013
1048
  };
1049
+ if (attrs._originalFormatting?.gridBefore) row.gridBefore = attrs._originalFormatting.gridBefore;
1050
+ if (attrs._originalFormatting?.gridAfter) row.gridAfter = attrs._originalFormatting.gridAfter;
1014
1051
  if (attrs.height) row.height = twipsToPixels(attrs.height);
1015
1052
  if (attrs.heightRule) row.heightRule = attrs.heightRule;
1016
1053
  if (attrs.isHeader) row.isHeader = attrs.isHeader;
@@ -1136,6 +1173,7 @@ function convertTextBoxNode(node, startPos, opts) {
1136
1173
  pmEnd: startPos + node.nodeSize
1137
1174
  };
1138
1175
  if (attrs.height !== void 0) textBox.height = attrs.height;
1176
+ if (attrs.autoFit !== void 0) textBox.autoFit = attrs.autoFit;
1139
1177
  if (attrs.fillColor !== void 0) textBox.fillColor = attrs.fillColor;
1140
1178
  if (attrs.outlineWidth !== void 0) textBox.outlineWidth = attrs.outlineWidth;
1141
1179
  if (attrs.outlineColor !== void 0) textBox.outlineColor = attrs.outlineColor;
@@ -1342,7 +1380,8 @@ function toFlowBlocks(doc, options = {}) {
1342
1380
  });
1343
1381
  reserveLeadingEmptyOutlineHeight(blocks);
1344
1382
  suppressTerminalEmptyParagraphsAfterTable(blocks);
1345
- return mergeRunInParagraphs(blocks);
1383
+ suppressFinalParagraphInRepeatedEmptySuffix(blocks);
1384
+ return groupParagraphFrames(mergeRunInParagraphs(blocks), nextBlockId);
1346
1385
  }
1347
1386
  /**
1348
1387
  * Merge consecutive paragraph blocks where the first carries
@@ -1,4 +1,5 @@
1
1
  import { measuredLineRangeHeight } from "../../layout-engine/lineFlow.js";
2
+ import { getTableRowLeadingWidth } from "../../layout-engine/types.js";
2
3
  import { getHeaderRowsHeight } from "../../layout-engine/index.js";
3
4
  //#region src/layout-bridge/engine/hitTest.ts
4
5
  /**
@@ -213,7 +214,7 @@ function hitTestTableCell(pageHit, blocks, measures, pagePoint) {
213
214
  const rowMeasure = tableMeasure.rows[rowIndex];
214
215
  const row = tableBlock.rows[rowIndex];
215
216
  if (!rowMeasure || !row) continue;
216
- let colX = 0;
217
+ let colX = getTableRowLeadingWidth(row, tableMeasure.columnWidths);
217
218
  let colIndex = -1;
218
219
  if (rowMeasure.cells.length === 0 || row.cells.length === 0) continue;
219
220
  for (let c = 0; c < rowMeasure.cells.length; c++) {
@@ -2,6 +2,7 @@ import { measureRun } from "../../layout-engine/measure/measureProvider.js";
2
2
  import { buildRunFontStyle } from "../../layout-engine/measure/measureHelpers.js";
3
3
  import { inlineImageBoundingBox } from "../../utils/rotationBoundingBox.js";
4
4
  import { measuredLineContentOffset } from "../../layout-engine/lineFlow.js";
5
+ import { getTableRowLeadingWidth } from "../../layout-engine/types.js";
5
6
  import { measureParagraph } from "../../layout-engine/measure/measureParagraph.js";
6
7
  import { buildTableCellFloatingZones, getTableCellContentWidth, getTableCellFloatingImages } from "../../layout-engine/measure/tableCellFloating.js";
7
8
  import { getHeaderRowsHeight } from "../../layout-engine/index.js";
@@ -314,7 +315,7 @@ function selectionToRects(layout, blocks, measures, from, to) {
314
315
  if (!row || !rowMeasure) continue;
315
316
  const clipTop = tableFragment.topClip ?? 0;
316
317
  const clipBottom = tableFragment.bottomClip ?? rowMeasure.height;
317
- let cellX = 0;
318
+ let cellX = getTableRowLeadingWidth(row, tableMeasure.columnWidths);
318
319
  for (let cellIndex = 0; cellIndex < row.cells.length; cellIndex++) {
319
320
  const cell = row.cells[cellIndex];
320
321
  const cellMeasure = rowMeasure.cells[cellIndex];
@@ -0,0 +1,7 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ import { PageHeaderFooterRefs } from "./types.js";
3
+
4
+ //#region src/layout-engine/headerFooterRefs.d.ts
5
+ declare const resolveSectionHeaderFooterRefs: (documentModel: document_d_exports.Document | null | undefined) => PageHeaderFooterRefs[] | undefined;
6
+ //#endregion
7
+ export { resolveSectionHeaderFooterRefs };
@@ -0,0 +1,40 @@
1
+ //#region src/layout-engine/headerFooterRefs.ts
2
+ const resolveSectionRefs = (props, inherited, documentEvenAndOddHeaders) => {
3
+ const refs = {
4
+ ...inherited,
5
+ evenAndOddHeaders: props.evenAndOddHeaders ?? documentEvenAndOddHeaders
6
+ };
7
+ if (props.titlePg !== void 0) refs.titlePg = props.titlePg;
8
+ for (const ref of props.headerReferences ?? []) if (ref.type === "default") refs.headerDefault = ref.rId;
9
+ else if (ref.type === "first") refs.headerFirst = ref.rId;
10
+ else refs.headerEven = ref.rId;
11
+ for (const ref of props.footerReferences ?? []) if (ref.type === "default") refs.footerDefault = ref.rId;
12
+ else if (ref.type === "first") refs.footerFirst = ref.rId;
13
+ else refs.footerEven = ref.rId;
14
+ return refs;
15
+ };
16
+ const inheritedRefsFrom = (refs) => ({
17
+ ...refs.headerDefault === void 0 ? {} : { headerDefault: refs.headerDefault },
18
+ ...refs.headerFirst === void 0 ? {} : { headerFirst: refs.headerFirst },
19
+ ...refs.headerEven === void 0 ? {} : { headerEven: refs.headerEven },
20
+ ...refs.footerDefault === void 0 ? {} : { footerDefault: refs.footerDefault },
21
+ ...refs.footerFirst === void 0 ? {} : { footerFirst: refs.footerFirst },
22
+ ...refs.footerEven === void 0 ? {} : { footerEven: refs.footerEven }
23
+ });
24
+ const resolveSectionHeaderFooterRefs = (documentModel) => {
25
+ const body = documentModel?.package.document;
26
+ if (!body) return;
27
+ const sections = [];
28
+ if (body.sections && body.sections.length > 0) sections.push(...body.sections.map(({ properties }) => properties));
29
+ else if (body.finalSectionProperties) sections.push(body.finalSectionProperties);
30
+ if (sections.length === 0) return;
31
+ const documentEvenAndOddHeaders = documentModel.package.settings?.evenAndOddHeaders === true;
32
+ let inherited = {};
33
+ return sections.map((props) => {
34
+ const refs = resolveSectionRefs(props, inherited, documentEvenAndOddHeaders);
35
+ inherited = inheritedRefsFrom(refs);
36
+ return refs;
37
+ });
38
+ };
39
+ //#endregion
40
+ export { resolveSectionHeaderFooterRefs };
@@ -1,6 +1,7 @@
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, 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, 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
+ import { resolveSectionHeaderFooterRefs } from "./headerFooterRefs.js";
4
5
  import { BreakDecision, SectionState, applyPendingToActive, createInitialSectionState, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, scheduleSectionBreak } from "./section-breaks.js";
5
6
  import { assertExhaustiveFlowBlock, findPageIndexContainingPmPos } from "./pmPageIndex.js";
6
7
 
@@ -43,4 +44,4 @@ declare function layoutDocument(blocks: FlowBlock[], measures: Measure[], option
43
44
  */
44
45
  declare function getHeaderRowsHeight(measure: TableMeasure, headerRowCount: number): number;
45
46
  //#endregion
46
- 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, hasKeepLines, hasPageBreakBefore, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, layoutDocument, 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, 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 };