@stll/folio-core 0.6.1 → 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 (107) 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 +86 -11
  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/groupDrawingParser.d.ts +1 -1
  24. package/dist/docx/groupDrawingParser.js +49 -8
  25. package/dist/docx/numberingParser.js +1 -0
  26. package/dist/docx/paragraphParser.js +9 -0
  27. package/dist/docx/paragraphTextBoxEnrichment.js +141 -2
  28. package/dist/docx/parser.js +2 -0
  29. package/dist/docx/runParser.js +11 -2
  30. package/dist/docx/selectiveXmlPatch.d.ts +10 -1
  31. package/dist/docx/selectiveXmlPatch.js +1 -1
  32. package/dist/docx/serializer/paragraphSerializer.js +4 -0
  33. package/dist/docx/serializer/runSerializer.js +7 -2
  34. package/dist/docx/serializer/tableSerializer.js +4 -0
  35. package/dist/docx/server/boundedArchive.d.ts +24 -0
  36. package/dist/docx/server/boundedArchive.js +106 -0
  37. package/dist/docx/server/extractDocxText.d.ts +23 -0
  38. package/dist/docx/server/extractDocxText.js +154 -0
  39. package/dist/docx/tableParser.js +8 -0
  40. package/dist/docx/textBoxParser.js +6 -1
  41. package/dist/docx/vmlImageParser.js +145 -1
  42. package/dist/index.d.ts +5 -2
  43. package/dist/index.js +4 -1
  44. package/dist/layout-bridge/convert/headerFooterLayout.js +16 -2
  45. package/dist/layout-bridge/convert/paragraphFrames.d.ts +7 -0
  46. package/dist/layout-bridge/convert/paragraphFrames.js +136 -0
  47. package/dist/layout-bridge/convert/toFlowBlocks.js +106 -13
  48. package/dist/layout-bridge/engine/hitTest.js +2 -1
  49. package/dist/layout-bridge/engine/selectionRects.js +2 -1
  50. package/dist/layout-engine/headerFooterRefs.d.ts +7 -0
  51. package/dist/layout-engine/headerFooterRefs.js +40 -0
  52. package/dist/layout-engine/index.d.ts +3 -2
  53. package/dist/layout-engine/index.js +125 -72
  54. package/dist/layout-engine/keep-together.d.ts +7 -5
  55. package/dist/layout-engine/keep-together.js +20 -4
  56. package/dist/layout-engine/measure/cache.js +2 -0
  57. package/dist/layout-engine/measure/listMarkerWidth.d.ts +3 -1
  58. package/dist/layout-engine/measure/listMarkerWidth.js +23 -4
  59. package/dist/layout-engine/measure/measureBlocks.d.ts +3 -0
  60. package/dist/layout-engine/measure/measureBlocks.js +133 -51
  61. package/dist/layout-engine/measure/measureParagraph.js +42 -15
  62. package/dist/layout-engine/measure/tableCellGrid.d.ts +15 -0
  63. package/dist/layout-engine/measure/tableCellGrid.js +62 -0
  64. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +18 -0
  65. package/dist/layout-engine/measure/textBoxParagraphLayout.js +32 -0
  66. package/dist/layout-engine/paginator.d.ts +2 -1
  67. package/dist/layout-engine/paginator.js +17 -8
  68. package/dist/layout-engine/paragraphFrame.d.ts +22 -0
  69. package/dist/layout-engine/paragraphFrame.js +17 -0
  70. package/dist/layout-engine/paragraphSequence.d.ts +7 -0
  71. package/dist/layout-engine/paragraphSequence.js +25 -0
  72. package/dist/layout-engine/paragraphSpacing.d.ts +17 -0
  73. package/dist/layout-engine/paragraphSpacing.js +30 -0
  74. package/dist/layout-engine/renderedBreakReconciliation.d.ts +58 -0
  75. package/dist/layout-engine/renderedBreakReconciliation.js +63 -0
  76. package/dist/layout-engine/types.d.ts +31 -10
  77. package/dist/layout-engine/types.js +15 -2
  78. package/dist/layout-painter/renderPage.js +2 -2
  79. package/dist/layout-painter/renderParagraph.js +94 -10
  80. package/dist/layout-painter/renderTable.js +42 -12
  81. package/dist/layout-painter/renderTextBox.js +17 -7
  82. package/dist/managers/DocumentLoaderManager.js +1 -1
  83. package/dist/paged-layout/sectionBlockWidths.d.ts +3 -0
  84. package/dist/paged-layout/sectionBlockWidths.js +9 -0
  85. package/dist/paged-layout/sectionGeometry.js +1 -1
  86. package/dist/prosemirror/attrs/index.js +11 -0
  87. package/dist/prosemirror/conversion/fromProseDoc.js +2 -0
  88. package/dist/prosemirror/conversion/toProseDoc.js +29 -23
  89. package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
  90. package/dist/prosemirror/extensions/features/ListExtension.js +1 -0
  91. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +68 -16
  92. package/dist/prosemirror/extensions/nodes/TableExtension.js +1 -0
  93. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -1
  94. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +7 -0
  95. package/dist/prosemirror/schema/nodes.d.ts +6 -3
  96. package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
  97. package/dist/prosemirror/utils/tabCalculator.js +1 -1
  98. package/dist/server.d.ts +6 -2
  99. package/dist/server.js +5 -1
  100. package/dist/types/index.d.ts +2 -1
  101. package/dist/utils/createDocument.js +26 -18
  102. package/dist/utils/formatToStyle.js +3 -3
  103. package/dist/utils/hexId.d.ts +8 -1
  104. package/dist/utils/hexId.js +11 -3
  105. package/dist/utils/units.d.ts +6 -6
  106. package/dist/utils/units.js +8 -8
  107. package/package.json +1 -1
@@ -5,7 +5,7 @@ import { calculateTabWidth } from "../prosemirror/utils/tabCalculator.js";
5
5
  import { inlineImageBoundingBox, parseRotationDegrees, rotatedBoundingBox } from "../utils/rotationBoundingBox.js";
6
6
  import { hasCjk, segmentByScript } from "../utils/scriptSegments.js";
7
7
  import { isFloatingImageRun } from "../layout-engine/types.js";
8
- import { getListMarkerInlineWidth } from "../layout-engine/measure/listMarkerWidth.js";
8
+ import { getListMarkerInlineWidth, getListMarkerVisualOffset } from "../layout-engine/measure/listMarkerWidth.js";
9
9
  import { applySdtDataAttrs } from "./sdtBoundary.js";
10
10
  import { applyImageVisualAttrs, hasImageVisualAttrs, wrapImageWithCrop } from "./renderImage.js";
11
11
  import { ommlToMathml } from "../docx/mathToMathml.js";
@@ -709,6 +709,75 @@ function splitTextRunsByEastAsia(runs) {
709
709
  }
710
710
  return result;
711
711
  }
712
+ const paintsLineEdgeSpaces = (run) => Boolean(run.underline || run.strike || run.highlight || run.shading || run.hyperlink || run.hidden || run.templatePreview || run.isInsertion || run.isDeletion || run.commentIds?.length || run.textEffect || run.emphasisMark);
713
+ const splitTextRunAt = (run, index) => {
714
+ const leading = {
715
+ ...run,
716
+ text: run.text.slice(0, index)
717
+ };
718
+ const trailing = {
719
+ ...run,
720
+ text: run.text.slice(index)
721
+ };
722
+ if (run.pmStart === void 0) return [leading, trailing];
723
+ const splitPosition = Math.min(run.pmStart + index, run.pmEnd ?? Number.POSITIVE_INFINITY);
724
+ leading.pmEnd = splitPosition;
725
+ trailing.pmStart = splitPosition;
726
+ return [leading, trailing];
727
+ };
728
+ /**
729
+ * Word keeps ordinary line-edge spaces addressable in the document model but
730
+ * gives them no painted advance. Collapsible trailing spaces always collapse;
731
+ * leading spaces collapse only after a soft wrap, not at the authored
732
+ * paragraph start or after a manual line break. Split paint runs to preserve
733
+ * exact PM ranges.
734
+ */
735
+ const splitCollapsibleLineEdgeSpaces = (sourceRuns, collapseLeading) => {
736
+ const runs = [...sourceRuns];
737
+ const collapsedLeadingRuns = /* @__PURE__ */ new Set();
738
+ const collapsedTrailingRuns = /* @__PURE__ */ new Set();
739
+ for (let index = runs.length - 1; index >= 0; index--) {
740
+ const run = runs[index];
741
+ if (!run || !isTextRun(run)) break;
742
+ if (run.text.length === 0) continue;
743
+ const trailingSpaces = / +$/u.exec(run.text);
744
+ if (!trailingSpaces || paintsLineEdgeSpaces(run)) break;
745
+ if (trailingSpaces.index === 0) {
746
+ collapsedTrailingRuns.add(run);
747
+ continue;
748
+ }
749
+ const [leading, trailing] = splitTextRunAt(run, trailingSpaces.index);
750
+ runs.splice(index, 1, leading, trailing);
751
+ collapsedTrailingRuns.add(trailing);
752
+ break;
753
+ }
754
+ if (collapseLeading) for (let index = 0; index < runs.length; index++) {
755
+ const run = runs[index];
756
+ if (!run || !isTextRun(run)) break;
757
+ if (run.text.length === 0) continue;
758
+ const leadingSpaces = /^ +/u.exec(run.text);
759
+ if (!leadingSpaces || paintsLineEdgeSpaces(run)) break;
760
+ if (leadingSpaces[0].length === run.text.length) {
761
+ collapsedLeadingRuns.add(run);
762
+ continue;
763
+ }
764
+ const [leading, trailing] = splitTextRunAt(run, leadingSpaces[0].length);
765
+ runs.splice(index, 1, leading, trailing);
766
+ collapsedLeadingRuns.add(leading);
767
+ break;
768
+ }
769
+ return {
770
+ runs,
771
+ collapsedLeadingRuns,
772
+ collapsedTrailingRuns
773
+ };
774
+ };
775
+ const startsAfterSoftWrap = (block, line) => {
776
+ if (line.fromChar > 0) return true;
777
+ if (line.fromRun === 0) return false;
778
+ const previousRun = block.runs.at(line.fromRun - 1);
779
+ return previousRun !== void 0 && !isLineBreakRun(previousRun);
780
+ };
712
781
  /**
713
782
  * Sub-pixel tolerance when comparing canvas-measured widths against the DOM's
714
783
  * actual right edge. Accumulated rounding from canvas measureText vs. browser
@@ -878,7 +947,20 @@ function renderLine(block, line, alignment, doc, options) {
878
947
  lineEl.style.boxSizing = "content-box";
879
948
  lineEl.style.height = `${line.lineHeight}px`;
880
949
  lineEl.style.lineHeight = `${line.lineHeight}px`;
881
- const runsForLine = splitTextRunsByEastAsia(sliceRunsForLine(block, line));
950
+ const splitRuns = splitTextRunsByEastAsia(sliceRunsForLine(block, line));
951
+ const { runs: runsForLine, collapsedLeadingRuns: collapsedLeadingSpaceRuns, collapsedTrailingRuns: collapsedTrailingSpaceRuns } = splitCollapsibleLineEdgeSpaces(splitRuns, startsAfterSoftWrap(block, line));
952
+ const isCollapsedLineEdgeSpaceRun = (run) => collapsedLeadingSpaceRuns.has(run) || collapsedTrailingSpaceRuns.has(run);
953
+ const renderLineTextRun = (run) => {
954
+ const runEl = renderTextRun(run, doc);
955
+ if (collapsedLeadingSpaceRuns.has(run)) runEl.dataset["collapsedLeadingSpaces"] = "true";
956
+ if (collapsedTrailingSpaceRuns.has(run)) runEl.dataset["collapsedTrailingSpaces"] = "true";
957
+ if (isCollapsedLineEdgeSpaceRun(run)) {
958
+ runEl.style.fontSize = "0";
959
+ runEl.style.letterSpacing = "0";
960
+ runEl.style.wordSpacing = "0";
961
+ }
962
+ return runEl;
963
+ };
882
964
  const onlyRun = runsForLine.length === 1 ? runsForLine[0] : void 0;
883
965
  if (onlyRun && isMathRun(onlyRun) && onlyRun.display === "block" && block.attrs?.alignment !== "right") lineEl.style.textAlign = "center";
884
966
  if (runsForLine.length === 1 && isImageRun(runsForLine[0])) {
@@ -914,7 +996,7 @@ function renderLine(block, line, alignment, doc, options) {
914
996
  const firstLineHangingExpansionPx = hasVisibleListMarker ? Math.min(firstLineHangingPx, Math.max(0, options.leftIndentPx ?? 0)) : firstLineHangingPx;
915
997
  const justifyCapacityPx = options.availableWidth + firstLineHangingExpansionPx;
916
998
  const overfullPx = line.width - justifyCapacityPx;
917
- const shrinkableSpaces = countShrinkableSpaces(runsForLine);
999
+ const shrinkableSpaces = countShrinkableSpaces(runsForLine.filter((run) => !isTextRun(run) || !isCollapsedLineEdgeSpaceRun(run)));
918
1000
  if (overfullPx > RIGHT_EDGE_EPSILON_PX && shrinkableSpaces > 0) {
919
1001
  lineEl.style.textAlign = "left";
920
1002
  lineEl.style.textAlignLast = "auto";
@@ -989,7 +1071,7 @@ function renderLine(block, line, alignment, doc, options) {
989
1071
  for (let j = i + 1; j < runsForLine.length; j++) {
990
1072
  const next = runsForLine[j];
991
1073
  if (isTabRun(next) || isLineBreakRun(next)) break;
992
- if (isTextRun(next)) lineEl.append(renderTextRun(next, doc));
1074
+ if (isTextRun(next)) lineEl.append(renderLineTextRun(next));
993
1075
  else if (isFieldRun(next) && options?.context) lineEl.append(renderFieldRun(next, doc, options.context));
994
1076
  else if (isImageRun(next)) {
995
1077
  if (isFloatingImageRun(next)) continue;
@@ -1003,13 +1085,14 @@ function renderLine(block, line, alignment, doc, options) {
1003
1085
  break;
1004
1086
  }
1005
1087
  let tabWidth = tabResult.width;
1006
- if (lineRightEdgeX !== void 0 && canClampTabToRightEdge(tabResult.alignment, i > 0, runsForLine.slice(0, i).some(isTabRun), options?.isLastLine === true) && currentX + tabWidth + followingWidthForCheck > lineRightEdgeX) tabWidth = Math.max(1, lineRightEdgeX - currentX - followingWidthForCheck);
1088
+ if (!(tabResult.alignment === "start" && leftIndentPx > 0 && Math.abs(currentX + tabWidth - leftIndentPx) <= RIGHT_EDGE_EPSILON_PX) && lineRightEdgeX !== void 0 && canClampTabToRightEdge(tabResult.alignment, i > 0, runsForLine.slice(0, i).some(isTabRun), options?.isLastLine === true) && currentX + tabWidth + followingWidthForCheck > lineRightEdgeX) tabWidth = Math.max(1, lineRightEdgeX - currentX - followingWidthForCheck);
1007
1089
  const tabEl = renderTabRun(run, doc, tabWidth, tabResult.leader);
1008
1090
  lineEl.append(tabEl);
1009
1091
  currentX += tabWidth;
1010
1092
  } else if (isTextRun(run)) {
1011
- const runEl = renderTextRun(run, doc);
1093
+ const runEl = renderLineTextRun(run);
1012
1094
  lineEl.append(runEl);
1095
+ if (isCollapsedLineEdgeSpaceRun(run)) continue;
1013
1096
  const fontSize = run.fontSize || 11;
1014
1097
  const fontFamily = run.fontFamily || "Calibri";
1015
1098
  const measuredWidth = measureText(run.allCaps ? run.text.toLocaleUpperCase() : run.text, fontSize, fontFamily, {
@@ -1268,7 +1351,7 @@ function renderParagraphFragment(fragment, block, measure, context, options = {}
1268
1351
  }
1269
1352
  const markerFontFamily = block.attrs.listMarkerFontFamily ?? firstTextRun?.fontFamily ?? block.attrs.defaultFontFamily;
1270
1353
  const markerFontSize = block.attrs.listMarkerFontSize ?? firstTextRun?.fontSize ?? block.attrs.defaultFontSize;
1271
- const marker = renderListMarker(block.attrs.listMarker, getListMarkerInlineWidth(block), doc, markerFontFamily, markerFontSize, block.attrs.listMarkerRevision, block.attrs.listMarkerSecondSlotOffsetTwips);
1354
+ const marker = renderListMarker(block.attrs.listMarker, getListMarkerInlineWidth(block), getListMarkerVisualOffset(block), doc, markerFontFamily, markerFontSize, block.attrs.listMarkerRevision, block.attrs.listMarkerSecondSlotOffsetTwips);
1272
1355
  const markerMarginLeft = markerStart - Math.min(indentLeft, 0);
1273
1356
  if (markerMarginLeft < 0) marker.style.marginLeft = `${markerMarginLeft}px`;
1274
1357
  lineEl.prepend(marker);
@@ -1279,13 +1362,13 @@ function renderParagraphFragment(fragment, block, measure, context, options = {}
1279
1362
  }
1280
1363
  /**
1281
1364
  * Render a list marker element as an inline-block at the start of the first
1282
- * body line. `minWidth` (from `getListMarkerInlineWidth`) sizes the marker
1365
+ * body line. `inlineWidth` (from `getListMarkerInlineWidth`) sizes the marker
1283
1366
  * so the body text aligns at the next tab stop per ECMA-376 §17.9.25 —
1284
1367
  * this honours `w:suff` (`tab` / `space` / `nothing`) and the document's
1285
1368
  * tab grid. Long markers like "1.1.1." therefore grow to the next stop
1286
1369
  * instead of butting against the body text.
1287
1370
  */
1288
- function renderListMarker(marker, minWidth, doc, fontFamily, fontSize, revision, secondSlotOffsetTwips) {
1371
+ function renderListMarker(marker, inlineWidth, visualOffset, doc, fontFamily, fontSize, revision, secondSlotOffsetTwips) {
1289
1372
  const span = doc.createElement("span");
1290
1373
  span.className = "layout-list-marker";
1291
1374
  span.style.display = "inline-block";
@@ -1294,7 +1377,8 @@ function renderListMarker(marker, minWidth, doc, fontFamily, fontSize, revision,
1294
1377
  span.style.textAlign = "left";
1295
1378
  span.style.textAlignLast = "left";
1296
1379
  span.style.boxSizing = "border-box";
1297
- if (minWidth > 0) span.style.minWidth = `${minWidth}px`;
1380
+ span.style.width = `${inlineWidth}px`;
1381
+ if (visualOffset !== 0) span.style.transform = `translateX(${visualOffset}px)`;
1298
1382
  if (revision) {
1299
1383
  const authorIdx = getAuthorColorIdx(revision.author ?? "");
1300
1384
  const authorColor = AUTHOR_COLORS[authorIdx];
@@ -1,5 +1,5 @@
1
1
  import { emuToPixels } from "../utils/units.js";
2
- import { isFloatingImageRun, isFloatingTextBoxBlock, tableColumnsArePinned } from "../layout-engine/types.js";
2
+ import { getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, tableColumnsArePinned } from "../layout-engine/types.js";
3
3
  import { measureParagraph } from "../layout-engine/measure/measureParagraph.js";
4
4
  import { getAutomaticTextColorForBackground } from "./documentColors.js";
5
5
  import { renderParagraphFragment } from "./renderParagraph.js";
@@ -207,6 +207,7 @@ function renderNestedTable(block, measure, context, doc) {
207
207
  rowYPositions.push(yPos);
208
208
  const spanningCells = /* @__PURE__ */ new Map();
209
209
  const columnsPinned = tableColumnsArePinned(block);
210
+ const cellGrid = buildTableCellGrid(block, measure.columnWidths.length);
210
211
  let y = 0;
211
212
  for (let rowIndex = 0; rowIndex < block.rows.length; rowIndex++) {
212
213
  const row = block.rows[rowIndex];
@@ -216,7 +217,7 @@ function renderNestedTable(block, measure, context, doc) {
216
217
  y += rowMeasure.height;
217
218
  continue;
218
219
  }
219
- const rowEl = renderTableRow(row, rowMeasure, rowIndex, y, measure.columnWidths, block.rows.length, context, doc, spanningCells, rowYPositions, void 0, block.bidi, columnsPinned);
220
+ const rowEl = renderTableRow(row, rowMeasure, rowIndex, y, measure.columnWidths, block.rows.length, context, doc, spanningCells, rowYPositions, void 0, block.bidi, columnsPinned, cellGrid);
220
221
  tableEl.append(rowEl);
221
222
  y += rowMeasure.height;
222
223
  }
@@ -255,10 +256,10 @@ function renderTableCell(cell, cellMeasure, x, rowHeight, borderFlags, columnsPi
255
256
  const padLeft = cell.padding?.left ?? 7;
256
257
  cellEl.style.padding = `${padTop}px ${padRight}px ${padBottom}px ${padLeft}px`;
257
258
  if (cell.borders) {
258
- if (borderFlags.isFirstRow) applyBorder(cellEl, "top", cell.borders.top);
259
+ if (borderFlags.drawTop) applyBorder(cellEl, "top", cell.borders.top);
259
260
  applyBorder(cellEl, "right", cell.borders.right);
260
261
  applyBorder(cellEl, "bottom", cell.borders.bottom);
261
- if (borderFlags.isFirstCol) applyBorder(cellEl, "left", cell.borders.left);
262
+ if (borderFlags.drawLeft) applyBorder(cellEl, "left", cell.borders.left);
262
263
  }
263
264
  if (cell.background) {
264
265
  cellEl.style.backgroundColor = cell.background;
@@ -303,10 +304,30 @@ function renderTableCell(cell, cellMeasure, x, rowHeight, borderFlags, columnsPi
303
304
  }
304
305
  return cellEl;
305
306
  }
307
+ const tableCellGridKey = (rowIndex, columnIndex) => `${rowIndex}:${columnIndex}`;
308
+ function buildTableCellGrid(block, columnCount) {
309
+ const grid = /* @__PURE__ */ new Map();
310
+ for (let rowIndex = 0; rowIndex < block.rows.length; rowIndex++) {
311
+ const row = block.rows[rowIndex];
312
+ if (!row) continue;
313
+ let columnIndex = 0;
314
+ for (const cell of row.cells) {
315
+ while (grid.has(tableCellGridKey(rowIndex, columnIndex))) columnIndex += 1;
316
+ const colSpan = cell.colSpan ?? 1;
317
+ const rowSpan = cell.rowSpan ?? 1;
318
+ const rowEnd = Math.min(block.rows.length, rowIndex + rowSpan);
319
+ const columnEnd = Math.min(columnCount, columnIndex + colSpan);
320
+ for (let gridRow = rowIndex; gridRow < rowEnd; gridRow++) for (let gridColumn = columnIndex; gridColumn < columnEnd; gridColumn++) grid.set(tableCellGridKey(gridRow, gridColumn), cell);
321
+ columnIndex += colSpan;
322
+ }
323
+ }
324
+ return grid;
325
+ }
326
+ const hasVisibleBorder = (border) => border !== void 0 && border.width !== 0 && border.style !== "none" && border.style !== "nil";
306
327
  /**
307
328
  * Render a table row with rowSpan support
308
329
  */
309
- function renderTableRow(row, rowMeasure, rowIndex, y, columnWidths, totalRows, context, doc, spanningCells, rowYPositions, isFirstRowInFragment, bidi = false, columnsPinned = false) {
330
+ function renderTableRow(row, rowMeasure, rowIndex, y, columnWidths, totalRows, context, doc, spanningCells, rowYPositions, isFirstRowInFragment, bidi = false, columnsPinned = false, cellGrid) {
310
331
  const rowEl = doc.createElement("div");
311
332
  rowEl.className = TABLE_CLASS_NAMES.row;
312
333
  const tableWidth = bidi ? columnWidths.reduce((sum, columnWidth) => sum + columnWidth, 0) : 0;
@@ -320,8 +341,9 @@ function renderTableRow(row, rowMeasure, rowIndex, y, columnWidths, totalRows, c
320
341
  if (spanningCells) {
321
342
  for (const [, spanCell] of spanningCells) if (spanCell.startRow < rowIndex && spanCell.startRow + spanCell.rowSpan > rowIndex) for (let c = 0; c < spanCell.colSpan; c++) occupiedColumns.add(spanCell.columnIndex + c);
322
343
  }
323
- let x = 0;
324
- let columnIndex = 0;
344
+ const gridBefore = row.gridBefore ?? 0;
345
+ let x = getTableRowLeadingWidth(row, columnWidths);
346
+ let columnIndex = gridBefore;
325
347
  while (occupiedColumns.has(columnIndex)) {
326
348
  x += columnWidths[columnIndex] ?? 0;
327
349
  columnIndex++;
@@ -345,11 +367,18 @@ function renderTableRow(row, rowMeasure, rowIndex, y, columnWidths, totalRows, c
345
367
  const isLastRow = rowIndex + rowSpan >= totalRows;
346
368
  const atLogicalStart = columnIndex === 0;
347
369
  const atLogicalEnd = columnIndex + colSpan >= columnWidths.length;
370
+ const isFirstCol = bidi ? atLogicalEnd : atLogicalStart;
371
+ const isLastCol = bidi ? atLogicalStart : atLogicalEnd;
372
+ const aboveCell = cellGrid?.get(tableCellGridKey(rowIndex - 1, columnIndex));
373
+ const leftNeighborColumn = bidi ? columnIndex + colSpan : columnIndex - 1;
374
+ const leftCell = cellGrid?.get(tableCellGridKey(rowIndex, leftNeighborColumn));
375
+ const drawTop = isFirstRow || !hasVisibleBorder(aboveCell?.borders?.bottom);
376
+ const drawLeft = isFirstCol || !hasVisibleBorder(leftCell?.borders?.right);
348
377
  const cellEl = renderTableCell(cell, cellMeasure, cellLeft, cellHeight, {
349
- isFirstRow,
378
+ drawTop,
350
379
  isLastRow,
351
- isFirstCol: bidi ? atLogicalEnd : atLogicalStart,
352
- isLastCol: bidi ? atLogicalStart : atLogicalEnd
380
+ drawLeft,
381
+ isLastCol
353
382
  }, columnsPinned, context, doc);
354
383
  cellEl.dataset["cellIndex"] = String(cellIndex);
355
384
  cellEl.dataset["columnIndex"] = String(columnIndex);
@@ -429,6 +458,7 @@ function renderTableFragment(fragment, block, measure, context, options = {}) {
429
458
  rowYPositions.push(yPos);
430
459
  const spanningCells = /* @__PURE__ */ new Map();
431
460
  const columnsPinned = tableColumnsArePinned(block);
461
+ const cellGrid = buildTableCellGrid(block, measure.columnWidths.length);
432
462
  const headerRowCount = fragment.headerRowCount ?? 0;
433
463
  let y = 0;
434
464
  if (headerRowCount > 0 && fragment.continuesFromPrev) for (let hdrIdx = 0; hdrIdx < headerRowCount; hdrIdx++) {
@@ -439,7 +469,7 @@ function renderTableFragment(fragment, block, measure, context, options = {}) {
439
469
  y += hdrRowMeasure.height;
440
470
  continue;
441
471
  }
442
- const rowEl = renderTableRow(hdrRow, hdrRowMeasure, hdrIdx, y, measure.columnWidths, block.rows.length, context, doc, spanningCells, rowYPositions, hdrIdx === 0, block.bidi, columnsPinned);
472
+ const rowEl = renderTableRow(hdrRow, hdrRowMeasure, hdrIdx, y, measure.columnWidths, block.rows.length, context, doc, spanningCells, rowYPositions, hdrIdx === 0, block.bidi, columnsPinned, cellGrid);
443
473
  rowEl.dataset["repeatedHeader"] = "true";
444
474
  tableEl.append(rowEl);
445
475
  y += hdrRowMeasure.height;
@@ -467,7 +497,7 @@ function renderTableFragment(fragment, block, measure, context, options = {}) {
467
497
  continue;
468
498
  }
469
499
  const isFirstRowInFragment = headerRowCount > 0 && fragment.continuesFromPrev ? false : fragment.continuesFromPrev && rowIndex === fragment.fromRow;
470
- const rowEl = renderTableRow(row, rowMeasure, rowIndex, y, measure.columnWidths, block.rows.length, context, doc, spanningCells, rowYPositions, isFirstRowInFragment, block.bidi, columnsPinned);
500
+ const rowEl = renderTableRow(row, rowMeasure, rowIndex, y, measure.columnWidths, block.rows.length, context, doc, spanningCells, rowYPositions, isFirstRowInFragment, block.bidi, columnsPinned, cellGrid);
471
501
  contentParent.append(rowEl);
472
502
  y += rowMeasure.height;
473
503
  }
@@ -1,5 +1,6 @@
1
1
  import { DEFAULT_TEXTBOX_MARGINS } from "../layout-engine/types.js";
2
2
  import { renderParagraphFragment } from "./renderParagraph.js";
3
+ import { layoutTextBoxParagraphs } from "../layout-engine/measure/textBoxParagraphLayout.js";
3
4
  //#region src/layout-painter/renderTextBox.ts
4
5
  /**
5
6
  * Text Box Renderer
@@ -38,28 +39,37 @@ function renderTextBoxFragment(fragment, block, measure, context, options = {})
38
39
  if (fragment.pmStart !== void 0) containerEl.dataset["pmStart"] = String(fragment.pmStart);
39
40
  if (fragment.pmEnd !== void 0) containerEl.dataset["pmEnd"] = String(fragment.pmEnd);
40
41
  const innerWidth = fragment.width - margins.left - margins.right;
41
- let yOffset = 0;
42
+ const paragraphLayout = layoutTextBoxParagraphs(block.content, measure.innerMeasures);
42
43
  for (let i = 0; i < block.content.length; i++) {
43
44
  const paraBlock = block.content[i];
44
45
  const paraMeasure = measure.innerMeasures[i];
45
- if (!paraBlock || !paraMeasure) continue;
46
- const paraEl = renderParagraphFragment({
46
+ const placement = paragraphLayout.placements[i];
47
+ if (!paraBlock || !paraMeasure || !placement) continue;
48
+ const paraFragment = {
47
49
  kind: "paragraph",
48
50
  blockId: paraBlock.id,
49
51
  x: 0,
50
- y: yOffset,
52
+ y: 0,
51
53
  width: innerWidth,
52
- height: paraMeasure.totalHeight,
54
+ height: placement.contentHeight,
53
55
  ...paraBlock.pmStart !== void 0 ? { pmStart: paraBlock.pmStart } : {},
54
56
  ...paraBlock.pmEnd !== void 0 ? { pmEnd: paraBlock.pmEnd } : {},
55
57
  fromLine: 0,
56
58
  toLine: paraMeasure.lines.length
57
- }, paraBlock, paraMeasure, context, { document: doc });
59
+ };
60
+ const prevBorders = block.content[i - 1]?.attrs?.borders;
61
+ const nextBorders = block.content[i + 1]?.attrs?.borders;
62
+ const paraEl = renderParagraphFragment(paraFragment, paraBlock, paraMeasure, context, {
63
+ document: doc,
64
+ ...prevBorders !== void 0 ? { prevBorders } : {},
65
+ ...nextBorders !== void 0 ? { nextBorders } : {}
66
+ });
58
67
  paraEl.style.position = "relative";
59
68
  paraEl.style.left = "0";
60
69
  paraEl.style.top = "0";
70
+ paraEl.style.height = `${placement.contentHeight}px`;
71
+ paraEl.style.marginTop = `${placement.leadingSpacing}px`;
61
72
  containerEl.append(paraEl);
62
- yOffset += paraMeasure.totalHeight;
63
73
  }
64
74
  return containerEl;
65
75
  }
@@ -1,8 +1,8 @@
1
+ import { inspectDocxCompatibility } from "../docx/compatibility.js";
1
2
  import { resetAuthorColors } from "../utils/authorColors.js";
2
3
  import { loadFontsWithMapping } from "../utils/fontLoader.js";
3
4
  import { parseDocx } from "../docx/parser.js";
4
5
  import { recordDocumentLoadPhase } from "../layout-engine/layoutInstrumentation.js";
5
- import { inspectDocxCompatibility } from "../docx/compatibility.js";
6
6
  //#region src/managers/DocumentLoaderManager.ts
7
7
  /**
8
8
  * DocumentLoaderManager
@@ -11,6 +11,9 @@ type PerBlockMeasureInputs = {
11
11
  widths: number[];
12
12
  marginTops: number[];
13
13
  pageHeights: number[];
14
+ pageWidths: number[];
15
+ marginLefts: number[];
16
+ marginRights: number[];
14
17
  marginBottoms: number[];
15
18
  };
16
19
  /**
@@ -35,6 +35,9 @@ function computePerBlockMeasureInputs({ blocks, bodyConfig, finalConfig }) {
35
35
  const widths = [];
36
36
  const marginTops = [];
37
37
  const pageHeights = [];
38
+ const pageWidths = [];
39
+ const marginLefts = [];
40
+ const marginRights = [];
38
41
  const marginBottoms = [];
39
42
  for (let i = 0; i < blocks.length; i++) {
40
43
  const config = sectionConfigs[sectionIdx] ?? finalConfig;
@@ -42,6 +45,9 @@ function computePerBlockMeasureInputs({ blocks, bodyConfig, finalConfig }) {
42
45
  widths.push(colWidth(contentWidth(config), columns, columnIndex));
43
46
  marginTops.push(config.margins.top);
44
47
  pageHeights.push(config.pageSize.h);
48
+ pageWidths.push(config.pageSize.w);
49
+ marginLefts.push(config.margins.left);
50
+ marginRights.push(config.margins.right);
45
51
  marginBottoms.push(config.margins.bottom);
46
52
  if (sectionIdx < breakIndices.length && i === breakIndices[sectionIdx]) {
47
53
  sectionIdx++;
@@ -53,6 +59,9 @@ function computePerBlockMeasureInputs({ blocks, bodyConfig, finalConfig }) {
53
59
  widths,
54
60
  marginTops,
55
61
  pageHeights,
62
+ pageWidths,
63
+ marginLefts,
64
+ marginRights,
56
65
  marginBottoms
57
66
  };
58
67
  }
@@ -9,7 +9,7 @@ const DEFAULT_MARGINS = {
9
9
  bottom: 96,
10
10
  left: 96
11
11
  };
12
- const twipsToPixels = (twips) => Math.round(twips / 1440 * 96);
12
+ const twipsToPixels = (twips) => twips / 1440 * 96;
13
13
  /**
14
14
  * Convert an offset-like twip dimension to px. Explicit 0 is meaningful for
15
15
  * page margins and header/footer distances; only absence should use fallback.
@@ -18,6 +18,11 @@ const IMAGE_CSS_FLOATS = [
18
18
  "right",
19
19
  "none"
20
20
  ];
21
+ const TEXT_BOX_AUTO_FIT_VALUES = [
22
+ "none",
23
+ "normal",
24
+ "shape"
25
+ ];
21
26
  const FIELD_KINDS = ["simple", "complex"];
22
27
  const MATH_DISPLAYS = ["inline", "block"];
23
28
  const SHAPE_FILL_TYPES = [
@@ -156,6 +161,11 @@ const readParagraphAttrs = (node) => {
156
161
  optionalBoolean(attrs, "listMarkerHidden", "paragraph.attrs.listMarkerHidden", issues);
157
162
  optionalString(attrs, "listMarkerFontFamily", "paragraph.attrs.listMarkerFontFamily", issues);
158
163
  optionalNumber(attrs, "listMarkerFontSize", "paragraph.attrs.listMarkerFontSize", issues);
164
+ optionalOneOf(attrs, "listMarkerAlignment", "paragraph.attrs.listMarkerAlignment", issues, [
165
+ "left",
166
+ "center",
167
+ "right"
168
+ ]);
159
169
  optionalString(attrs, "listMarkerSuffix", "paragraph.attrs.listMarkerSuffix", issues);
160
170
  optionalBoolean(attrs, "listMarkerAllCaps", "paragraph.attrs.listMarkerAllCaps", issues);
161
171
  optionalNumber(attrs, "listImplicitChildLevelAdvances", "paragraph.attrs.listImplicitChildLevelAdvances", issues);
@@ -410,6 +420,7 @@ const readTextBoxAttrs = (node) => {
410
420
  expectNodeType(node, "textBox", issues);
411
421
  optionalNumber(attrs, "width", "textBox.attrs.width", issues);
412
422
  optionalNumber(attrs, "height", "textBox.attrs.height", issues);
423
+ optionalOneOf(attrs, "autoFit", "textBox.attrs.autoFit", issues, TEXT_BOX_AUTO_FIT_VALUES);
413
424
  optionalString(attrs, "textBoxId", "textBox.attrs.textBoxId", issues);
414
425
  optionalString(attrs, "fillColor", "textBox.attrs.fillColor", issues);
415
426
  optionalNumber(attrs, "outlineWidth", "textBox.attrs.outlineWidth", issues);
@@ -210,6 +210,7 @@ function listRenderingFromAttrs(attrs) {
210
210
  ...attrs.listMarkerHidden != null && { markerHidden: attrs.listMarkerHidden },
211
211
  ...attrs.listMarkerFontFamily != null && { markerFontFamily: attrs.listMarkerFontFamily },
212
212
  ...attrs.listMarkerFontSize != null && { markerFontSize: attrs.listMarkerFontSize },
213
+ ...attrs.listMarkerAlignment != null && { markerAlignment: attrs.listMarkerAlignment },
213
214
  ...attrs.listMarkerSuffix != null && { markerSuffix: attrs.listMarkerSuffix },
214
215
  ...attrs.listMarkerAllCaps != null && { markerAllCaps: attrs.listMarkerAllCaps },
215
216
  ...attrs.listImplicitChildLevelAdvances != null && { implicitChildLevelAdvances: attrs.listImplicitChildLevelAdvances },
@@ -1516,6 +1517,7 @@ function convertPMTextBox(node) {
1516
1517
  type: "paragraph",
1517
1518
  content: []
1518
1519
  }],
1520
+ ...attrs.autoFit !== void 0 ? { autoFit: attrs.autoFit } : {},
1519
1521
  margins: (() => {
1520
1522
  const m = {};
1521
1523
  if (typeof attrs.marginTop === "number") m.top = pixelsToEmu(attrs.marginTop);
@@ -227,6 +227,7 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
227
227
  if (paragraph.listRendering?.markerHidden) attrs.listMarkerHidden = paragraph.listRendering.markerHidden;
228
228
  if (paragraph.listRendering?.markerFontFamily) attrs.listMarkerFontFamily = paragraph.listRendering.markerFontFamily;
229
229
  if (paragraph.listRendering?.markerFontSize) attrs.listMarkerFontSize = paragraph.listRendering.markerFontSize;
230
+ if (paragraph.listRendering?.markerAlignment) attrs.listMarkerAlignment = paragraph.listRendering.markerAlignment;
230
231
  if (paragraph.listRendering?.markerSuffix) attrs.listMarkerSuffix = paragraph.listRendering.markerSuffix;
231
232
  if (paragraph.listRendering?.markerAllCaps) attrs.listMarkerAllCaps = paragraph.listRendering.markerAllCaps;
232
233
  if (paragraph.listRendering?.implicitChildLevelAdvances !== void 0) attrs.listImplicitChildLevelAdvances = paragraph.listRendering.implicitChildLevelAdvances;
@@ -259,11 +260,11 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
259
260
  if (formatting?.spaceBefore === void 0 && tableParagraphOverlay?.spaceBefore === void 0 && paragraphStyle?.pPr?.spaceBefore === void 0 && docDefaultSpacing?.spaceBefore !== void 0) spacingFromDocDefaults.before = true;
260
261
  if (formatting?.spaceAfter === void 0 && tableParagraphOverlay?.spaceAfter === void 0 && paragraphStyle?.pPr?.spaceAfter === void 0 && docDefaultSpacing?.spaceAfter !== void 0) spacingFromDocDefaults.after = true;
261
262
  if (spacingFromDocDefaults.before || spacingFromDocDefaults.after) attrs.spacingFromDocDefaults = spacingFromDocDefaults;
262
- set("indentLeft", formatting?.indentLeft ?? stylePpr?.indentLeft);
263
+ const numberingStyleIndent = formatting?.numPr?.numId === 0 && stylePpr?.numPr !== void 0 && stylePpr.numPr.numId !== 0 ? void 0 : stylePpr;
264
+ set("indentLeft", formatting?.indentLeft ?? numberingStyleIndent?.indentLeft);
263
265
  set("indentRight", formatting?.indentRight ?? stylePpr?.indentRight);
264
- const styleFirstLine = formatting?.numPr?.numId === 0 && stylePpr?.numPr !== void 0 && stylePpr.numPr.numId !== 0 ? void 0 : stylePpr;
265
- set("indentFirstLine", formatting?.indentFirstLine ?? styleFirstLine?.indentFirstLine);
266
- set("hangingIndent", formatting?.hangingIndent ?? styleFirstLine?.hangingIndent);
266
+ set("indentFirstLine", formatting?.indentFirstLine ?? numberingStyleIndent?.indentFirstLine);
267
+ set("hangingIndent", formatting?.hangingIndent ?? numberingStyleIndent?.hangingIndent);
267
268
  set("borders", formatting?.borders ?? stylePpr?.borders);
268
269
  set("shading", formatting?.shading ?? stylePpr?.shading);
269
270
  set("tabs", formatting?.tabs ?? stylePpr?.tabs);
@@ -301,7 +302,7 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
301
302
  set("runInWithNext", formatting?.runInWithNext);
302
303
  set("outlineLevel", formatting?.outlineLevel);
303
304
  set("direction", directionFromBidi(formatting?.bidi));
304
- set("defaultTextFormatting", resolveTextFormatting(formatting?.runProperties, styleResolver));
305
+ set("defaultTextFormatting", stripParagraphMarkOnlyFormatting(resolveTextFormatting(formatting?.runProperties, styleResolver) ?? {}));
305
306
  }
306
307
  if (paragraph.sectionProperties) {
307
308
  attrs._sectionProperties = paragraph.sectionProperties;
@@ -401,33 +402,33 @@ function hasDirectRunFormatting(formatting) {
401
402
  return Object.entries(formatting).some(([key, value]) => key !== "styleId" && value !== void 0);
402
403
  }
403
404
  function stripParagraphMarkOnlyFormatting(formatting) {
404
- const { allCaps: _ac, highlight: _h, shading: _s, smallCaps: _sc, ...rest } = formatting;
405
+ const { allCaps: _ac, highlight: _h, shading: _s, smallCaps: _sc, vertAlign: _va, ...rest } = formatting;
405
406
  return Object.keys(rest).length > 0 ? rest : void 0;
406
407
  }
407
408
  function suppressParagraphMarkFormatting(base, paragraphMark, direct, paragraphMarkPrecedesStyle = false) {
408
409
  if (!paragraphMark) return base;
409
410
  const result = (paragraphMarkPrecedesStyle ? mergeTextFormatting(paragraphMark, base) : mergeTextFormatting(base, paragraphMark)) ?? {};
410
- suppressBooleanParagraphMark(result, paragraphMark, direct, "bold");
411
- suppressBooleanParagraphMark(result, paragraphMark, direct, "italic");
412
- suppressBooleanParagraphMark(result, paragraphMark, direct, "strike");
413
- suppressBooleanParagraphMark(result, paragraphMark, direct, "doubleStrike");
414
- suppressBooleanParagraphMark(result, paragraphMark, direct, "allCaps");
415
- suppressBooleanParagraphMark(result, paragraphMark, direct, "smallCaps");
416
- suppressBooleanParagraphMark(result, paragraphMark, direct, "hidden");
417
- suppressBooleanParagraphMark(result, paragraphMark, direct, "emboss");
418
- suppressBooleanParagraphMark(result, paragraphMark, direct, "imprint");
419
- suppressBooleanParagraphMark(result, paragraphMark, direct, "shadow");
420
- suppressBooleanParagraphMark(result, paragraphMark, direct, "outline");
421
- suppressBooleanParagraphMark(result, paragraphMark, direct, "rtl");
411
+ suppressBooleanParagraphMark(result, base, paragraphMark, direct, "bold");
412
+ suppressBooleanParagraphMark(result, base, paragraphMark, direct, "italic");
413
+ suppressBooleanParagraphMark(result, base, paragraphMark, direct, "strike");
414
+ suppressBooleanParagraphMark(result, base, paragraphMark, direct, "doubleStrike");
415
+ suppressBooleanParagraphMark(result, base, paragraphMark, direct, "allCaps");
416
+ suppressBooleanParagraphMark(result, base, paragraphMark, direct, "smallCaps");
417
+ suppressBooleanParagraphMark(result, base, paragraphMark, direct, "hidden");
418
+ suppressBooleanParagraphMark(result, base, paragraphMark, direct, "emboss");
419
+ suppressBooleanParagraphMark(result, base, paragraphMark, direct, "imprint");
420
+ suppressBooleanParagraphMark(result, base, paragraphMark, direct, "shadow");
421
+ suppressBooleanParagraphMark(result, base, paragraphMark, direct, "outline");
422
+ suppressBooleanParagraphMark(result, base, paragraphMark, direct, "rtl");
422
423
  if (paragraphMark.fontSize !== void 0 && direct?.fontSize === void 0 && base?.fontSize !== void 0) result.fontSize = base.fontSize;
423
424
  if (paragraphMark.fontSizeCs !== void 0 && direct?.fontSizeCs === void 0 && base?.fontSizeCs !== void 0) result.fontSizeCs = base.fontSizeCs;
424
425
  if (paragraphMark.underline !== void 0 && direct?.underline === void 0) result.underline = { style: "none" };
425
426
  if (paragraphMark.spacing !== void 0 && direct?.spacing === void 0) result.spacing = 0;
426
427
  return Object.keys(result).length > 0 ? result : void 0;
427
428
  }
428
- function suppressBooleanParagraphMark(result, paragraphMark, direct, key) {
429
+ function suppressBooleanParagraphMark(result, base, paragraphMark, direct, key) {
429
430
  if (paragraphMark[key] === void 0 || direct?.[key] !== void 0) return;
430
- result[key] = false;
431
+ result[key] = base?.[key] ?? false;
431
432
  }
432
433
  function resolveTextFormatting(formatting, styleResolver) {
433
434
  if (!formatting) return styleResolver?.resolveRunStyle(null);
@@ -456,7 +457,7 @@ function calculateRowSpans(table) {
456
457
  clearActiveVerticalMerges(activeMerges, result);
457
458
  continue;
458
459
  }
459
- let colIndex = 0;
460
+ let colIndex = row.formatting?.gridBefore ?? 0;
460
461
  const rowCells = row.cells.map((cell) => {
461
462
  const colspan = cell.formatting?.gridSpan ?? 1;
462
463
  const vMerge = cell.formatting?.vMerge;
@@ -549,6 +550,7 @@ function convertTable(table, styleResolver, context) {
549
550
  const fallbackTableStyle = tableStyleId ? void 0 : defaultTableStyle;
550
551
  const conditionalTableStyleId = tableStyle?.styleId ?? fallbackTableStyle?.styleId;
551
552
  const resolvedTableBorders = table.formatting?.borders ?? tableStyle?.tblPr?.borders ?? fallbackTableStyle?.tblPr?.borders;
553
+ const resolvedTableIndent = table.formatting?.indent ?? tableStyle?.tblPr?.indent ?? fallbackTableStyle?.tblPr?.indent;
552
554
  const tableCellMargins = table.formatting?.cellMargins ?? tableStyle?.tblPr?.cellMargins ?? fallbackTableStyle?.tblPr?.cellMargins;
553
555
  let cellMarginsAttr;
554
556
  if (tableCellMargins) {
@@ -569,6 +571,7 @@ function convertTable(table, styleResolver, context) {
569
571
  if (cellMarginsAttr) attrs.cellMargins = cellMarginsAttr;
570
572
  if (table.formatting?.look) attrs.look = table.formatting.look;
571
573
  if (table.formatting?.borders) attrs.borders = table.formatting.borders;
574
+ if (resolvedTableIndent) attrs._resolvedIndent = resolvedTableIndent;
572
575
  if (table.formatting) attrs._originalFormatting = table.formatting;
573
576
  if (table.propertyChanges && table.propertyChanges.length > 0) attrs.tblPrChange = [...table.propertyChanges];
574
577
  const conditionalStyles = {};
@@ -614,8 +617,9 @@ function convertTable(table, styleResolver, context) {
614
617
  function countTableColumns(rows) {
615
618
  let maxColumns = 0;
616
619
  for (const row of rows) {
617
- let rowColumns = 0;
620
+ let rowColumns = row.formatting?.gridBefore ?? 0;
618
621
  for (const cell of row.cells) rowColumns += cell.formatting?.gridSpan ?? 1;
622
+ rowColumns += row.formatting?.gridAfter ?? 0;
619
623
  maxColumns = Math.max(maxColumns, rowColumns);
620
624
  }
621
625
  return maxColumns;
@@ -649,7 +653,7 @@ function convertTableRow(row, styleResolver, context, isHeaderRow, columnWidths,
649
653
  if (totalCols > 1) fallback.formatting = { gridSpan: totalCols };
650
654
  effectiveCells = [fallback];
651
655
  }
652
- let colIndex = 0;
656
+ let colIndex = row.formatting?.gridBefore ?? 0;
653
657
  const cells = [];
654
658
  for (const cellIndex_item of effectiveCells) {
655
659
  const cell = cellIndex_item;
@@ -1262,6 +1266,7 @@ function extractTextBoxesFromParagraph(paragraph) {
1262
1266
  if (shape.fill) textBox.fill = shape.fill;
1263
1267
  if (shape.outline) textBox.outline = shape.outline;
1264
1268
  if (shape.textBody.margins) textBox.margins = shape.textBody.margins;
1269
+ if (shape.textBody.autoFit) textBox.autoFit = shape.textBody.autoFit;
1265
1270
  textBoxes.push(textBox);
1266
1271
  }
1267
1272
  }
@@ -1330,6 +1335,7 @@ function convertTextBox(textBox, styleResolver, options = {}) {
1330
1335
  return schema.node("textBox", {
1331
1336
  width: widthPx,
1332
1337
  height: heightPx,
1338
+ autoFit: textBox.autoFit,
1333
1339
  textBoxId: textBox.id,
1334
1340
  fillColor,
1335
1341
  outlineWidth,
@@ -199,6 +199,7 @@ const paragraphNodeSpec = {
199
199
  listMarkerHidden: { default: null },
200
200
  listMarkerFontFamily: { default: null },
201
201
  listMarkerFontSize: { default: null },
202
+ listMarkerAlignment: { default: null },
202
203
  listMarkerSuffix: { default: null },
203
204
  listMarkerAllCaps: { default: null },
204
205
  listImplicitChildLevelAdvances: { default: null },
@@ -34,6 +34,7 @@ const LIST_FORMATTING_ATTRS = [
34
34
  "listMarkerHidden",
35
35
  "listMarkerFontFamily",
36
36
  "listMarkerFontSize",
37
+ "listMarkerAlignment",
37
38
  "listMarkerSuffix",
38
39
  "listLevelNumFmts",
39
40
  "listLevelStarts",