@stll/folio-core 0.43.0 → 0.45.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 (237) hide show
  1. package/dist/ai-edits/__fixtures__/paragraphs.js +2 -2
  2. package/dist/ai-edits/headless.js +7 -5
  3. package/dist/ai-edits/index.d.ts +2 -2
  4. package/dist/ai-edits/index.js +2 -2
  5. package/dist/ai-edits/snapshot.js +13 -9
  6. package/dist/compare/content-alignment.js +94 -54
  7. package/dist/compare/inline-atoms.js +34 -20
  8. package/dist/compare/style-resources.js +6 -0
  9. package/dist/content-controls/mutateContentControls.js +4 -2
  10. package/dist/display-list/dom/renderDisplayListToDom.js +8 -8
  11. package/dist/document-operations.js +14 -3
  12. package/dist/docx/appVersionNormalization.d.ts +0 -18
  13. package/dist/docx/blockContentParser.js +8 -0
  14. package/dist/docx/blockRangeMarkers.d.ts +36 -0
  15. package/dist/docx/blockRangeMarkers.js +59 -0
  16. package/dist/docx/bookmarkParser.d.ts +2 -20
  17. package/dist/docx/bookmarkParser.js +6 -30
  18. package/dist/docx/borderParser.d.ts +13 -0
  19. package/dist/docx/borderParser.js +71 -0
  20. package/dist/docx/builtInStyles.d.ts +165 -0
  21. package/dist/docx/builtInStyles.js +239 -0
  22. package/dist/docx/commentIdNormalization.d.ts +3 -1
  23. package/dist/docx/commentIdNormalization.js +18 -1
  24. package/dist/docx/commentParser.d.ts +2 -1
  25. package/dist/docx/commentParser.js +80 -42
  26. package/dist/docx/commentReferenceNormalization.d.ts +4 -1
  27. package/dist/docx/commentReferenceNormalization.js +23 -14
  28. package/dist/docx/commentThreadKey.d.ts +18 -0
  29. package/dist/docx/commentThreadKey.js +22 -0
  30. package/dist/docx/danglingRelationshipReferences.d.ts +15 -0
  31. package/dist/docx/danglingRelationshipReferences.js +30 -0
  32. package/dist/docx/defaultParagraphStyle.d.ts +18 -1
  33. package/dist/docx/defaultParagraphStyle.js +23 -1
  34. package/dist/docx/diagramPreview.js +87 -27
  35. package/dist/docx/documentParser.d.ts +2 -1
  36. package/dist/docx/documentParser.js +2 -2
  37. package/dist/docx/drawingUtils.d.ts +8 -1
  38. package/dist/docx/drawingUtils.js +12 -3
  39. package/dist/docx/fieldParser.js +3 -5
  40. package/dist/docx/footnoteParser.d.ts +3 -2
  41. package/dist/docx/footnoteParser.js +19 -4
  42. package/dist/docx/groupDrawingParser.js +4 -4
  43. package/dist/docx/headerFooterRefParser.d.ts +4 -3
  44. package/dist/docx/headerFooterRefParser.js +42 -12
  45. package/dist/docx/headerFooterReferenceNormalization.d.ts +4 -1
  46. package/dist/docx/headerFooterReferenceNormalization.js +5 -1
  47. package/dist/docx/hyperlinkParser.js +13 -17
  48. package/dist/docx/imageParser.d.ts +10 -2
  49. package/dist/docx/imageParser.js +80 -30
  50. package/dist/docx/imageRawXml.d.ts +14 -1
  51. package/dist/docx/imageRawXml.js +35 -11
  52. package/dist/docx/markupRangeMarker.d.ts +15 -0
  53. package/dist/docx/markupRangeMarker.js +44 -0
  54. package/dist/docx/mathToMathml.js +12 -14
  55. package/dist/docx/nonVisualDrawingProps.d.ts +34 -0
  56. package/dist/docx/nonVisualDrawingProps.js +46 -0
  57. package/dist/docx/noteReferenceStyles.d.ts +29 -0
  58. package/dist/docx/noteReferenceStyles.js +70 -0
  59. package/dist/docx/numberingReferenceNormalization.d.ts +4 -1
  60. package/dist/docx/numberingReferenceNormalization.js +20 -1
  61. package/dist/docx/paraIdRangeNormalization.d.ts +0 -19
  62. package/dist/docx/paragraphParser.js +66 -99
  63. package/dist/docx/paragraphPropertySource.js +1 -0
  64. package/dist/docx/paragraphTextBoxEnrichment.js +3 -0
  65. package/dist/docx/paragraphTraversal.d.ts +37 -1
  66. package/dist/docx/paragraphTraversal.js +84 -1
  67. package/dist/docx/parseContext.d.ts +37 -0
  68. package/dist/docx/parseContext.js +67 -0
  69. package/dist/docx/parseWarningMessage.d.ts +6 -0
  70. package/dist/docx/parseWarningMessage.js +44 -0
  71. package/dist/docx/parser.js +83 -29
  72. package/dist/docx/previewBudget.d.ts +64 -0
  73. package/dist/docx/previewBudget.js +88 -0
  74. package/dist/docx/relsParser.d.ts +28 -11
  75. package/dist/docx/relsParser.js +26 -13
  76. package/dist/docx/revisionIdNormalization.js +96 -10
  77. package/dist/docx/rezip.js +80 -40
  78. package/dist/docx/runConsolidator.js +1 -2
  79. package/dist/docx/runParser.d.ts +8 -1
  80. package/dist/docx/runParser.js +30 -48
  81. package/dist/docx/sdtPropertiesPatch.js +24 -18
  82. package/dist/docx/sectionParser.d.ts +2 -1
  83. package/dist/docx/sectionParser.js +21 -65
  84. package/dist/docx/sectionReferenceHistory.js +2 -2
  85. package/dist/docx/selectiveSave.js +6 -6
  86. package/dist/docx/serializer/blockSdtSerializer.js +38 -26
  87. package/dist/docx/serializer/borderSerializer.d.ts +2 -3
  88. package/dist/docx/serializer/borderSerializer.js +13 -12
  89. package/dist/docx/serializer/commentSerializer.d.ts +41 -16
  90. package/dist/docx/serializer/commentSerializer.js +82 -72
  91. package/dist/docx/serializer/documentSerializer.d.ts +1 -5
  92. package/dist/docx/serializer/documentSerializer.js +6 -16
  93. package/dist/docx/serializer/fontTableSerializer.js +6 -6
  94. package/dist/docx/serializer/headerFooterSerializer.js +10 -5
  95. package/dist/docx/serializer/markupRangeAttributes.d.ts +8 -0
  96. package/dist/docx/serializer/markupRangeAttributes.js +24 -0
  97. package/dist/docx/serializer/noteSerializer.js +5 -0
  98. package/dist/docx/serializer/numberingSerializer.js +7 -6
  99. package/dist/docx/serializer/paragraphSerializer.d.ts +1 -5
  100. package/dist/docx/serializer/paragraphSerializer.js +47 -52
  101. package/dist/docx/serializer/partNamespaces.js +2 -2
  102. package/dist/docx/serializer/runSerializer.js +57 -31
  103. package/dist/docx/serializer/sectionPropertiesSerializer.js +11 -10
  104. package/dist/docx/serializer/settingsSerializer.js +4 -3
  105. package/dist/docx/serializer/stylesSerializer.js +6 -6
  106. package/dist/docx/serializer/tableSerializer.js +37 -21
  107. package/dist/docx/serializer/textFormattingSerializer.d.ts +2 -3
  108. package/dist/docx/serializer/textFormattingSerializer.js +29 -28
  109. package/dist/docx/serializer/themeSerializer.js +6 -6
  110. package/dist/docx/serializer/trackedChangeAttributes.js +2 -2
  111. package/dist/docx/serializer/xmlUtils.d.ts +1 -2
  112. package/dist/docx/serializer/xmlUtils.js +1 -13
  113. package/dist/docx/server/boundedArchive.d.ts +12 -0
  114. package/dist/docx/server/boundedArchive.js +20 -1
  115. package/dist/docx/server/build.js +8 -1
  116. package/dist/docx/server/createBilingualDocument.js +10 -18
  117. package/dist/docx/server/extractDocxText.js +3 -4
  118. package/dist/docx/server/validateDocxConformance.js +22 -1
  119. package/dist/docx/shadingParser.d.ts +6 -0
  120. package/dist/docx/shadingParser.js +32 -0
  121. package/dist/docx/shapeParser.js +10 -8
  122. package/dist/docx/styleParser.js +13 -87
  123. package/dist/docx/styleReferenceResolution.d.ts +36 -0
  124. package/dist/docx/styleReferenceResolution.js +51 -0
  125. package/dist/docx/tableLook.d.ts +57 -0
  126. package/dist/docx/tableLook.js +63 -0
  127. package/dist/docx/tableParser.d.ts +7 -9
  128. package/dist/docx/tableParser.js +64 -110
  129. package/dist/docx/textBoxParser.js +11 -6
  130. package/dist/docx/trackedMoveRangeNormalization.d.ts +3 -1
  131. package/dist/docx/trackedMoveRangeNormalization.js +11 -21
  132. package/dist/docx/transitionalSpelling.d.ts +13 -2
  133. package/dist/docx/transitionalSpelling.js +23 -1
  134. package/dist/docx/unzip.d.ts +23 -0
  135. package/dist/docx/unzip.js +32 -22
  136. package/dist/docx/verbatimCapture.js +5 -12
  137. package/dist/docx/vmlImageParser.js +5 -4
  138. package/dist/docx/vmlPreview.d.ts +1 -3
  139. package/dist/docx/vmlPreview.js +2 -30
  140. package/dist/docx/watermarkParser.js +2 -2
  141. package/dist/docx/xmlParser.d.ts +38 -33
  142. package/dist/docx/xmlParser.js +92 -47
  143. package/dist/docx/xmlResourceLimits.d.ts +89 -9
  144. package/dist/docx/xmlResourceLimits.js +105 -24
  145. package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
  146. package/dist/internal/paragraphFormattingSerialization.d.ts +2 -3
  147. package/dist/internal/paragraphFormattingSerialization.js +29 -8
  148. package/dist/layout-bridge/convert/footnoteLayout.js +2 -7
  149. package/dist/layout-engine/index.d.ts +2 -2
  150. package/dist/layout-engine/index.js +2 -2
  151. package/dist/layout-engine/measure/measureBlocks.js +1 -6
  152. package/dist/layout-engine/types.d.ts +8 -2
  153. package/dist/layout-engine/types.js +35 -2
  154. package/dist/layout-painter/renderImage.js +4 -3
  155. package/dist/layout-painter/renderParagraph.js +4 -3
  156. package/dist/managers/autoSaveCodec.js +2 -8
  157. package/dist/markdown/images.js +1 -4
  158. package/dist/markdown/index.js +1 -1
  159. package/dist/markdown/internals.d.ts +6 -1
  160. package/dist/markdown/internals.js +14 -1
  161. package/dist/markdown/renderBlock.js +35 -21
  162. package/dist/markdown/renderParagraph.js +14 -5
  163. package/dist/markdown/renderRuns.js +4 -3
  164. package/dist/markdown/renderTable.js +4 -3
  165. package/dist/markdown/trailers.js +41 -7
  166. package/dist/markdown/types.d.ts +3 -7
  167. package/dist/prosemirror/attrs/index.js +71 -5
  168. package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +11 -1
  169. package/dist/prosemirror/bookmarkBoundaryAttrs.js +18 -3
  170. package/dist/prosemirror/commands/image.js +1 -0
  171. package/dist/prosemirror/commands/index.d.ts +3 -3
  172. package/dist/prosemirror/commands/index.js +2 -2
  173. package/dist/prosemirror/commands/paragraph.d.ts +3 -3
  174. package/dist/prosemirror/commands/paragraph.js +2 -2
  175. package/dist/prosemirror/commentIdAllocator.js +2 -7
  176. package/dist/prosemirror/conversion/fromProseDoc.js +197 -68
  177. package/dist/prosemirror/conversion/toProseDoc.d.ts +1 -14
  178. package/dist/prosemirror/conversion/toProseDoc.js +458 -335
  179. package/dist/prosemirror/extensions/core/ParagraphExtension.d.ts +14 -1
  180. package/dist/prosemirror/extensions/core/ParagraphExtension.js +11 -6
  181. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +3 -3
  182. package/dist/prosemirror/extensions/features/PasteCleanupExtension.d.ts +4 -1
  183. package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +6 -2
  184. package/dist/prosemirror/extensions/features/pastedHeadingStyles.d.ts +7 -0
  185. package/dist/prosemirror/extensions/features/pastedHeadingStyles.js +74 -0
  186. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +2 -3
  187. package/dist/prosemirror/extensions/marks/markUtils.d.ts +11 -3
  188. package/dist/prosemirror/extensions/marks/markUtils.js +98 -19
  189. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +7 -3
  190. package/dist/prosemirror/extensions/nodes/ImageExtension.js +6 -1
  191. package/dist/prosemirror/extensions/nodes/ShapeExtension.js +8 -2
  192. package/dist/prosemirror/extensions/nodes/TableExtension.js +15 -1
  193. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +8 -4
  194. package/dist/prosemirror/extensions/types.d.ts +2 -2
  195. package/dist/prosemirror/index.d.ts +3 -3
  196. package/dist/prosemirror/index.js +3 -3
  197. package/dist/prosemirror/insertOperations.d.ts +9 -2
  198. package/dist/prosemirror/insertOperations.js +9 -4
  199. package/dist/prosemirror/paragraphFormattingProvenance.d.ts +162 -0
  200. package/dist/prosemirror/paragraphFormattingProvenance.js +115 -0
  201. package/dist/prosemirror/plugins/documentStyles.d.ts +9 -1
  202. package/dist/prosemirror/plugins/documentStyles.js +11 -1
  203. package/dist/prosemirror/plugins/index.d.ts +2 -2
  204. package/dist/prosemirror/plugins/index.js +2 -2
  205. package/dist/prosemirror/plugins/revisionIds.d.ts +11 -2
  206. package/dist/prosemirror/plugins/revisionIds.js +21 -6
  207. package/dist/prosemirror/runFormattingReconciliation.js +3 -2
  208. package/dist/prosemirror/runStyleFormatting.d.ts +1 -1
  209. package/dist/prosemirror/schema/nodes.d.ts +81 -1
  210. package/dist/prosemirror/styles/resolvedStyleAttrs.js +2 -0
  211. package/dist/prosemirror/styles/styleResolver.d.ts +9 -0
  212. package/dist/prosemirror/styles/styleResolver.js +12 -0
  213. package/dist/style-engine/styleEngine.d.ts +3 -0
  214. package/dist/style-engine/styleEngine.js +3 -0
  215. package/dist/style-sets/extract.js +1 -23
  216. package/dist/style-sets/stellaStyle.js +46 -39
  217. package/dist/style-sets/styleSetNormalization.d.ts +19 -0
  218. package/dist/style-sets/styleSetNormalization.js +99 -0
  219. package/dist/types/content.d.ts +2 -2
  220. package/dist/utils/base64.d.ts +36 -0
  221. package/dist/utils/base64.js +40 -0
  222. package/dist/utils/clipboard.js +2 -1
  223. package/dist/utils/createDocument.js +145 -20
  224. package/dist/utils/headingCollector.d.ts +8 -5
  225. package/dist/utils/headingCollector.js +23 -25
  226. package/dist/utils/tableOfContentsStyle.js +9 -2
  227. package/dist/utils/units.d.ts +10 -1
  228. package/dist/utils/units.js +12 -1
  229. package/dist/utils/urlSecurity.d.ts +8 -2
  230. package/dist/utils/urlSecurity.js +21 -3
  231. package/package.json +2 -2
  232. package/dist/docx/textWhitespace.d.ts +0 -4
  233. package/dist/docx/textWhitespace.js +0 -4
  234. package/dist/layout-bridge/engine/tableWidthUtils.d.ts +0 -6
  235. package/dist/layout-bridge/engine/tableWidthUtils.js +0 -25
  236. package/dist/markdown/headings.d.ts +0 -13
  237. package/dist/markdown/headings.js +0 -20
@@ -1,28 +1,30 @@
1
1
  import { resolveColorValueToHex } from "../../docx/drawingUtils.js";
2
2
  import { isNumberingReference } from "../../docx/numberingReference.js";
3
3
  import { PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, createProseParagraphWithPropertySource, getDocumentParagraphPropertySourceContract, recreateProseNodeWithParagraphPropertySource, transportTableCellsWithParagraphPropertySources } from "../../docx/paragraphPropertySource.js";
4
+ import { isBaselineVertAlign } from "../../docx/runParser.js";
5
+ import { resolveTableLook } from "../../docx/tableLook.js";
6
+ import { isCellMergeContinuation } from "../../docx/tableParser.js";
4
7
  import { buildPageBreakRunSourceDescendantIndex } from "../../internal/pageBreakRunSourceDescendantIndex.js";
5
8
  import { createStyleEngine } from "../../style-engine/styleEngine.js";
6
9
  import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
7
10
  import { mergeParagraphFormatting, mergeParagraphTabStops } from "../../utils/paragraphFormattingMerge.js";
8
11
  import { tableOfContentsStyleLevel } from "../../utils/tableOfContentsStyle.js";
9
12
  import { mergeTextFormatting } from "../../utils/textFormattingMerge.js";
10
- import { emuToPixels } from "../../utils/units.js";
13
+ import { emuToPixels, emuToStrokePixels } from "../../utils/units.js";
11
14
  import { setAutospacingBaseValue } from "../autospacingBase.js";
12
- import { buildRunFormattingOverrideAttrs } from "../extensions/marks/RunFormattingOverrideExtension.js";
15
+ import { textFormattingToMarks } from "../extensions/marks/markUtils.js";
13
16
  import { listRenderingAttrPatch } from "../listRenderingAttrs.js";
14
17
  import { stampNumberedRefFieldBaselines } from "../numberedRefFields.js";
15
18
  import { pageBreakRunParagraphProjectionDispositionForFeatures } from "../pageBreakRunProjection.js";
16
19
  import { directionFromBidi } from "../paragraphDirection.js";
20
+ import { styleResolvedParagraphFormatting } from "../paragraphFormattingProvenance.js";
17
21
  import { lineSpacingProvenanceFromSpacing } from "../paragraphSpacing.js";
18
22
  import { getParagraphMarkSuppressionOverrides, hasDirectRunFormatting, resolveParagraphBodyRunFormatting, stripParagraphMarkFormattingForBodyRuns, stripParagraphMarkOnlyFormatting, suppressParagraphMarkFormatting } from "../runStyleFormatting.js";
19
23
  import { schema } from "../schema/index.js";
20
- import { COMPLEX_SCRIPT_RUN_PROPERTY_KEYS } from "../schema/marks.js";
21
24
  import { cascadeStyleTextFormatting } from "../styles/styleToggleCascade.js";
22
25
  import { canCarryTrackedRunMark, trackedRunInlineAtomDisposition } from "../trackedRunInlineAtoms.js";
23
26
  import { assertValidProseMirrorDocument } from "../validation.js";
24
27
  import { resolveEffectiveTableCellFormatting } from "./effectiveTableCellFormatting.js";
25
- import { shadingToRunShadingAttrs } from "./runShadingMark.js";
26
28
  import { sdtAttrsFromProperties } from "./sdtAttrs.js";
27
29
  import { TaggedError, panic } from "better-result";
28
30
  import { DRAWING_RAW_XML_MODES } from "@stll/docx-core/model";
@@ -77,25 +79,72 @@ const collectPairedBookmarkIds = (blocks) => {
77
79
  for (const content of run.content) if (content.type === "shape" && content.shape.textBody) visitBlocks(content.shape.textBody.content);
78
80
  };
79
81
  const visitHyperlink = (hyperlink) => {
80
- for (const child of hyperlink.children) if (child.type === "bookmarkStart") countBoundary(child.id, "start");
81
- else if (child.type === "bookmarkEnd") countBoundary(child.id, "end");
82
- else visitRun(child);
82
+ for (const child of hyperlink.children) switch (child.type) {
83
+ case "bookmarkStart":
84
+ countBoundary(child.id, "start");
85
+ break;
86
+ case "bookmarkEnd":
87
+ countBoundary(child.id, "end");
88
+ break;
89
+ case "run":
90
+ visitRun(child);
91
+ break;
92
+ default: panic(`Unsupported hyperlink child: ${JSON.stringify(child)}`);
93
+ }
83
94
  };
84
95
  const visitParagraphContent = (content) => {
85
- if (content.type === "bookmarkStart") countBoundary(content.id, "start");
86
- else if (content.type === "bookmarkEnd") countBoundary(content.id, "end");
87
- else if (content.type === "run") visitRun(content);
88
- else if (content.type === "hyperlink") visitHyperlink(content);
89
- else if (content.type === "simpleField") for (const child of content.content) if (child.type === "hyperlink") visitHyperlink(child);
90
- else visitRun(child);
91
- else if (content.type === "complexField") for (const run of [...content.fieldCode ?? [], ...content.fieldResult]) visitRun(run);
92
- else if (content.type === "inlineSdt") for (const child of content.content) visitParagraphContent(child);
93
- else if (content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo") for (const child of content.content) visitParagraphContent(child);
96
+ switch (content.type) {
97
+ case "bookmarkStart":
98
+ countBoundary(content.id, "start");
99
+ return;
100
+ case "bookmarkEnd":
101
+ countBoundary(content.id, "end");
102
+ return;
103
+ case "run":
104
+ visitRun(content);
105
+ return;
106
+ case "hyperlink":
107
+ visitHyperlink(content);
108
+ return;
109
+ case "simpleField":
110
+ for (const child of content.content) if (child.type === "hyperlink") visitHyperlink(child);
111
+ else visitRun(child);
112
+ return;
113
+ case "complexField":
114
+ for (const run of [...content.fieldCode ?? [], ...content.fieldResult]) visitRun(run);
115
+ return;
116
+ case "inlineSdt":
117
+ case "insertion":
118
+ case "deletion":
119
+ case "moveFrom":
120
+ case "moveTo":
121
+ case "bidiWrapper":
122
+ for (const child of content.content) visitParagraphContent(child);
123
+ return;
124
+ case "commentRangeStart":
125
+ case "commentRangeEnd":
126
+ case "commentReference":
127
+ case "moveFromRangeStart":
128
+ case "moveFromRangeEnd":
129
+ case "moveToRangeStart":
130
+ case "moveToRangeEnd":
131
+ case "mathEquation": return;
132
+ default: panic(`Unsupported paragraph content: ${JSON.stringify(content)}`);
133
+ }
94
134
  };
95
135
  const visitBlocks = (nestedBlocks) => {
96
- for (const block of nestedBlocks) if (block.type === "paragraph") for (const content of block.content) visitParagraphContent(content);
97
- else if (block.type === "table") for (const row of block.rows) for (const cell of row.cells) visitBlocks(cell.content);
98
- else visitBlocks(block.content);
136
+ for (const block of nestedBlocks) switch (block.type) {
137
+ case "paragraph":
138
+ for (const content of block.content) visitParagraphContent(content);
139
+ break;
140
+ case "table":
141
+ for (const row of block.rows) for (const cell of row.cells) visitBlocks(cell.content);
142
+ break;
143
+ case "blockSdt":
144
+ visitBlocks(block.content);
145
+ break;
146
+ default: panic(`Unsupported block content: ${JSON.stringify(block)}`);
147
+ }
99
148
  };
100
149
  visitBlocks(blocks);
101
150
  return new Set([...counts].flatMap(([id, count]) => count.starts === 1 && count.ends === 1 && count.firstStart !== void 0 && count.firstEnd !== void 0 && count.firstStart < count.firstEnd ? [id] : []));
@@ -121,12 +170,21 @@ function toProseDoc(document, options) {
121
170
  };
122
171
  const convertBodyBlocks = (blocks) => {
123
172
  const out = [];
124
- for (const block of blocks) if (block.type === "paragraph") out.push(...convertParagraphWithTextBoxes(block, styleResolver, {
125
- textBoxGroupId: nextTextBoxGroupId(),
126
- context: conversionContext
127
- }));
128
- else if (block.type === "table") out.push(convertTable(block, styleResolver, conversionContext));
129
- else out.push(convertBlockSdt(block, convertBodyBlocks));
173
+ for (const block of blocks) switch (block.type) {
174
+ case "paragraph":
175
+ out.push(...convertParagraphWithTextBoxes(block, styleResolver, {
176
+ textBoxGroupId: nextTextBoxGroupId(),
177
+ context: conversionContext
178
+ }));
179
+ break;
180
+ case "table":
181
+ out.push(convertTable(block, styleResolver, conversionContext));
182
+ break;
183
+ case "blockSdt":
184
+ out.push(convertBlockSdt(block, convertBodyBlocks));
185
+ break;
186
+ default: panic(`Unsupported block content: ${JSON.stringify(block)}`);
187
+ }
130
188
  return out;
131
189
  };
132
190
  nodes.push(...convertBodyBlocks(paragraphs));
@@ -265,58 +323,95 @@ function convertParagraph(paragraph, styleResolver, context, activeCommentIds, e
265
323
  const emitTrackedChange = (change, markType, moveKind) => {
266
324
  emitInlineNodes(convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, moveKind, textBoxAnchors));
267
325
  };
268
- for (const content of paragraph.content) if (content.type === "commentRangeStart") commentIds.add(content.id);
269
- else if (content.type === "commentRangeEnd") commentIds.delete(content.id);
270
- else if (content.type === "commentReference") anchorPointComment(inlineNodes, content.id);
271
- else if (content.type === "run") emitInlineNodes(convertRun(content, getInheritedRunFormatting(content.formatting), nextPageBreakRunOwnerId, styleResolver, textBoxAnchors));
272
- else if (content.type === "hyperlink") {
273
- const linkNodes = convertHyperlink(content, {
274
- getInheritedRunFormatting,
275
- styleResolver,
276
- hyperlinkIndex: nextHyperlinkInstanceIndex(),
277
- textBoxAnchors,
278
- nextPageBreakRunOwnerId
279
- });
280
- if (linkNodes.length === 0) {
281
- emptyHyperlinks ??= [];
282
- emptyHyperlinks.push({
283
- offset: inlineOffset,
284
- ...content.href !== void 0 ? { href: content.href } : {},
285
- ...content.anchor !== void 0 ? { anchor: content.anchor } : {},
286
- ...content.tooltip !== void 0 ? { tooltip: content.tooltip } : {},
287
- ...content.rId !== void 0 ? { rId: content.rId } : {}
326
+ for (const content of withoutBidiWrappers(paragraph.content)) switch (content.type) {
327
+ case "commentRangeStart":
328
+ commentIds.add(content.id);
329
+ break;
330
+ case "commentRangeEnd":
331
+ commentIds.delete(content.id);
332
+ break;
333
+ case "commentReference":
334
+ anchorPointComment(inlineNodes, content.id);
335
+ break;
336
+ case "run":
337
+ emitInlineNodes(convertRun(content, getInheritedRunFormatting(content.formatting), nextPageBreakRunOwnerId, styleResolver, textBoxAnchors));
338
+ break;
339
+ case "hyperlink": {
340
+ const linkNodes = convertHyperlink(content, {
341
+ getInheritedRunFormatting,
342
+ styleResolver,
343
+ hyperlinkIndex: nextHyperlinkInstanceIndex(),
344
+ textBoxAnchors,
345
+ nextPageBreakRunOwnerId
288
346
  });
289
- continue;
347
+ if (linkNodes.length === 0) {
348
+ emptyHyperlinks ??= [];
349
+ emptyHyperlinks.push({
350
+ offset: inlineOffset,
351
+ ...content.href !== void 0 ? { href: content.href } : {},
352
+ ...content.anchor !== void 0 ? { anchor: content.anchor } : {},
353
+ ...content.tooltip !== void 0 ? { tooltip: content.tooltip } : {},
354
+ ...content.rId !== void 0 ? { rId: content.rId } : {}
355
+ });
356
+ break;
357
+ }
358
+ emitInlineNodes(linkNodes);
359
+ break;
290
360
  }
291
- emitInlineNodes(linkNodes);
292
- } else if (content.type === "simpleField" || content.type === "complexField") emitInlineNode(convertField(content, {
293
- getInheritedRunFormatting,
294
- styleResolver,
295
- nextHyperlinkInstanceIndex,
296
- nextPageBreakRunOwnerId,
297
- textBoxAnchors
298
- }));
299
- else if (content.type === "inlineSdt") emitInlineNode(convertInlineSdt(content, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors));
300
- else if (content.type === "insertion" || content.type === "moveTo") emitTrackedChange(content, "insertion", content.type === "moveTo" ? "moveTo" : null);
301
- else if (content.type === "deletion" || content.type === "moveFrom") emitTrackedChange(content, "deletion", content.type === "moveFrom" ? "moveFrom" : null);
302
- else if (content.type === "mathEquation") emitInlineNode(convertMathEquation(content));
303
- else if (content.type === "bookmarkStart" && pairedBookmarkIds.has(content.id)) emitInlineNode(schema.node("bookmarkBoundary", {
304
- type: "start",
305
- id: content.id,
306
- name: content.name,
307
- colFirst: content.colFirst,
308
- colLast: content.colLast
309
- }));
310
- else if (content.type === "bookmarkEnd" && pairedBookmarkIds.has(content.id)) emitInlineNode(schema.node("bookmarkBoundary", {
311
- type: "end",
312
- id: content.id
313
- }));
314
- else if (content.type === "bookmarkStart") {
315
- if (!bookmarksArr) bookmarksArr = [];
316
- bookmarksArr.push({
317
- id: content.id,
318
- name: content.name
319
- });
361
+ case "simpleField":
362
+ case "complexField":
363
+ emitInlineNode(convertField(content, {
364
+ getInheritedRunFormatting,
365
+ styleResolver,
366
+ nextHyperlinkInstanceIndex,
367
+ nextPageBreakRunOwnerId,
368
+ textBoxAnchors
369
+ }));
370
+ break;
371
+ case "inlineSdt":
372
+ emitInlineNode(convertInlineSdt(content, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors));
373
+ break;
374
+ case "insertion":
375
+ case "moveTo":
376
+ emitTrackedChange(content, "insertion", content.type === "moveTo" ? "moveTo" : null);
377
+ break;
378
+ case "deletion":
379
+ case "moveFrom":
380
+ emitTrackedChange(content, "deletion", content.type === "moveFrom" ? "moveFrom" : null);
381
+ break;
382
+ case "mathEquation":
383
+ emitInlineNode(convertMathEquation(content));
384
+ break;
385
+ case "bookmarkStart":
386
+ if (pairedBookmarkIds.has(content.id)) {
387
+ emitInlineNode(schema.node("bookmarkBoundary", {
388
+ type: "start",
389
+ id: content.id,
390
+ name: content.name,
391
+ colFirst: content.colFirst,
392
+ colLast: content.colLast,
393
+ displacedByCustomXml: content.displacedByCustomXml
394
+ }));
395
+ break;
396
+ }
397
+ if (!bookmarksArr) bookmarksArr = [];
398
+ bookmarksArr.push({
399
+ id: content.id,
400
+ name: content.name
401
+ });
402
+ break;
403
+ case "bookmarkEnd":
404
+ if (pairedBookmarkIds.has(content.id)) emitInlineNode(schema.node("bookmarkBoundary", {
405
+ type: "end",
406
+ id: content.id,
407
+ displacedByCustomXml: content.displacedByCustomXml
408
+ }));
409
+ break;
410
+ case "moveFromRangeStart":
411
+ case "moveFromRangeEnd":
412
+ case "moveToRangeStart":
413
+ case "moveToRangeEnd": break;
414
+ default: panic(`Unsupported paragraph content: ${JSON.stringify(content)}`);
320
415
  }
321
416
  if (bookmarksArr) attrs.bookmarks = bookmarksArr;
322
417
  if (emptyHyperlinks) attrs._emptyHyperlinks = emptyHyperlinks;
@@ -387,11 +482,13 @@ function convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, next
387
482
  id: item.id,
388
483
  name: item.name,
389
484
  colFirst: item.colFirst,
390
- colLast: item.colLast
485
+ colLast: item.colLast,
486
+ displacedByCustomXml: item.displacedByCustomXml
391
487
  }));
392
488
  else if (item.type === "bookmarkEnd") nodes.push(schema.node("bookmarkBoundary", {
393
489
  type: "end",
394
- id: item.id
490
+ id: item.id,
491
+ displacedByCustomXml: item.displacedByCustomXml
395
492
  }));
396
493
  else panic(`Unsupported tracked-run content: ${JSON.stringify(item)}`);
397
494
  const mark = schema.marks[markType].create({
@@ -435,6 +532,8 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
435
532
  let stylePpr;
436
533
  if (styleResolver) {
437
534
  stylePpr = styleResolver.resolveParagraphStyleInTable(styleId, tableParagraphOverlay).paragraphFormatting;
535
+ const resolvedFormatting = styleResolvedParagraphFormatting(stylePpr);
536
+ if (resolvedFormatting) attrs._resolvedFormatting = resolvedFormatting;
438
537
  set("alignment", formatting?.alignment ?? stylePpr?.alignment);
439
538
  set("alignmentFromStyle", stylePpr?.alignment);
440
539
  set("spaceBefore", formatting?.spaceBefore ?? stylePpr?.spaceBefore);
@@ -665,30 +764,33 @@ function calculateRowSpans(table) {
665
764
  const rowCells = row.cells.map((cell) => {
666
765
  const colspan = cell.formatting?.gridSpan ?? 1;
667
766
  const vMerge = cell.formatting?.vMerge;
668
- const startRow = vMerge === "continue" ? activeMerges.get(colIndex) : void 0;
767
+ const isMergeContinuation = isCellMergeContinuation(cell);
768
+ const startRow = isMergeContinuation ? activeMerges.get(colIndex) : void 0;
669
769
  const info = {
670
770
  cell,
671
771
  colIndex,
672
772
  colspan,
673
773
  vMerge,
774
+ isMergeContinuation,
674
775
  startRow,
675
776
  hasMeaningfulContent: tableCellHasMeaningfulContent(cell),
676
- shouldSkip: vMerge === "continue" && startRow !== void 0
777
+ shouldSkip: isMergeContinuation && startRow !== void 0
677
778
  };
678
779
  colIndex += colspan;
679
780
  return info;
680
781
  });
681
782
  const rowWouldBeEmpty = rowCells.length > 0 && rowCells.every((cell) => cell.shouldSkip);
682
783
  for (const cellInfo of rowCells) {
683
- const { colIndex: cellColIndex, vMerge, startRow, hasMeaningfulContent } = cellInfo;
784
+ const { colIndex: cellColIndex, vMerge, isMergeContinuation, startRow, hasMeaningfulContent } = cellInfo;
684
785
  const key = `${rowIndex}-${cellColIndex}`;
685
786
  if (vMerge === "restart") {
787
+ closeVerticalMerge(activeMerges, result, cellColIndex);
686
788
  activeMerges.set(cellColIndex, rowIndex);
687
789
  result.set(key, {
688
790
  rowSpan: 1,
689
791
  skip: false
690
792
  });
691
- } else if (vMerge === "continue") {
793
+ } else if (isMergeContinuation) {
692
794
  if (startRow === void 0 || rowWouldBeEmpty || hasMeaningfulContent) {
693
795
  result.set(key, {
694
796
  rowSpan: 1,
@@ -713,7 +815,7 @@ function calculateRowSpans(table) {
713
815
  skip: true
714
816
  });
715
817
  } else {
716
- activeMerges.delete(cellColIndex);
818
+ closeVerticalMerge(activeMerges, result, cellColIndex);
717
819
  result.set(key, {
718
820
  rowSpan: 1,
719
821
  skip: false
@@ -721,14 +823,28 @@ function calculateRowSpans(table) {
721
823
  }
722
824
  }
723
825
  }
826
+ clearActiveVerticalMerges(activeMerges, result);
724
827
  return result;
725
828
  }
829
+ /**
830
+ * End the vertical merge active in one column.
831
+ *
832
+ * A `w:vMerge="restart"` is carried through the editor by its cell's rowspan,
833
+ * which only exists once a continuation joins it. A merge that closes with a
834
+ * rowspan of 1 — a restart with no continuation, one interrupted by a plain
835
+ * cell, one whose continuation is in a row a revision removed — has nothing
836
+ * but this flag to say the cell was a merge origin, and dropping it changes
837
+ * the table's visible structure.
838
+ */
839
+ function closeVerticalMerge(activeMerges, result, colIndex) {
840
+ const startRow = activeMerges.get(colIndex);
841
+ if (startRow === void 0) return;
842
+ const restartCell = result.get(`${startRow}-${colIndex}`);
843
+ if (restartCell && restartCell.rowSpan === 1) restartCell.preserveVMergeRestart = true;
844
+ activeMerges.delete(colIndex);
845
+ }
726
846
  function clearActiveVerticalMerges(activeMerges, result) {
727
- for (const [colIndex, startRow] of activeMerges) {
728
- const restartCell = result.get(`${startRow}-${colIndex}`);
729
- if (restartCell) restartCell.preserveVMergeRestart = true;
730
- }
731
- activeMerges.clear();
847
+ for (const colIndex of [...activeMerges.keys()]) closeVerticalMerge(activeMerges, result, colIndex);
732
848
  }
733
849
  function tableCellHasMeaningfulContent(cell) {
734
850
  return cell.content.some(blockHasMeaningfulContent);
@@ -745,7 +861,7 @@ function paragraphContentHasMeaningfulContent(content) {
745
861
  }
746
862
  function convertTable(table, styleResolver, context) {
747
863
  for (const row of table.rows) for (const cell of row.cells) {
748
- if (cell.formatting?.vMerge === "continue" && context.pageBreakRunSourceDescendants.containsPageBreakRun(cell.content)) throw new UnsupportedDocxToProseMirrorConversionError({
864
+ if (isCellMergeContinuation(cell) && context.pageBreakRunSourceDescendants.containsPageBreakRun(cell.content)) throw new UnsupportedDocxToProseMirrorConversionError({
749
865
  message: `${PAGE_BREAK_CONTAINER_DESCRIPTIONS["table-cell"]} cannot be represented in the editor model`,
750
866
  owner: "table-cell",
751
867
  contentType: "break"
@@ -756,7 +872,7 @@ function convertTable(table, styleResolver, context) {
756
872
  const columnWidths = table.columnWidths;
757
873
  const totalWidth = columnWidths?.reduce((sum, w) => sum + w, 0) ?? 0;
758
874
  const tableStyleId = table.formatting?.styleId;
759
- const look = table.formatting?.look;
875
+ const look = resolveTableLook(table.formatting?.look);
760
876
  const tableStyle = tableStyleId ? styleResolver?.getStyle(tableStyleId) : void 0;
761
877
  const defaultTableStyle = styleResolver?.getDefaultTableStyle();
762
878
  const fallbackTableStyle = tableStyleId ? void 0 : defaultTableStyle;
@@ -792,7 +908,7 @@ function convertTable(table, styleResolver, context) {
792
908
  if (resolvedTableIndent) attrs._resolvedIndent = resolvedTableIndent;
793
909
  if (resolvedTableJustification) attrs._resolvedJustification = resolvedTableJustification;
794
910
  if (resolvedTableBidi !== void 0) attrs._resolvedBidi = resolvedTableBidi;
795
- if (table.formatting) attrs._originalFormatting = table.formatting;
911
+ attrs._originalFormatting = table.formatting ?? {};
796
912
  if (table.propertyChanges && table.propertyChanges.length > 0) attrs.tblPrChange = [...table.propertyChanges];
797
913
  const conditionalStyles = {};
798
914
  const setCS = (key, type) => {
@@ -814,15 +930,15 @@ function convertTable(table, styleResolver, context) {
814
930
  setCS("neCell", "neCell");
815
931
  setCS("swCell", "swCell");
816
932
  setCS("seCell", "seCell");
817
- const bandingEnabledH = look?.noHBand !== true;
818
- const bandingEnabledV = look?.noVBand !== true;
933
+ const bandingEnabledH = !look.noHBand;
934
+ const bandingEnabledV = !look.noVBand;
819
935
  let dataRowIndex = 0;
820
936
  const totalRows = table.rows.length;
821
937
  const gridColumnCount = columnWidths?.length ?? 0;
822
938
  const totalColumns = gridColumnCount > 0 ? gridColumnCount : countTableColumns(table.rows);
823
939
  const rows = table.rows.map((row, rowIndex) => {
824
- const isFirstRowStyled = rowIndex === 0 && !!look?.firstRow;
825
- const isLastRow = rowIndex === totalRows - 1 && !!look?.lastRow;
940
+ const isFirstRowStyled = rowIndex === 0 && look.firstRow;
941
+ const isLastRow = rowIndex === totalRows - 1 && look.lastRow;
826
942
  const rowBandStyle = (() => {
827
943
  if (bandingEnabledH && !isFirstRowStyled && !isLastRow) return (() => {
828
944
  if (dataRowIndex % 2 === 0) return conditionalStyles.band1Horz;
@@ -857,26 +973,34 @@ function convertTableRow(row, styleResolver, context, isHeaderRow, columnWidths,
857
973
  if (row.formatting) attrsWithoutStructuralChange._originalFormatting = row.formatting;
858
974
  if (row.propertyChanges && row.propertyChanges.length > 0) attrsWithoutStructuralChange.trPrChange = [...row.propertyChanges];
859
975
  let attrs = attrsWithoutStructuralChange;
860
- if (row.structuralChange?.type === "tableRowInsertion") attrs = {
861
- ...attrsWithoutStructuralChange,
862
- trIns: {
863
- revisionId: row.structuralChange.info.id,
864
- author: row.structuralChange.info.author,
865
- date: row.structuralChange.info.date ?? null,
866
- ...row.structuralChange.info.utcDate ? { utcDate: row.structuralChange.info.utcDate.value } : {},
867
- ...row.structuralChange.info.initials ? { initials: row.structuralChange.info.initials } : {}
868
- }
869
- };
870
- else if (row.structuralChange?.type === "tableRowDeletion") attrs = {
871
- ...attrsWithoutStructuralChange,
872
- trDel: {
873
- revisionId: row.structuralChange.info.id,
874
- author: row.structuralChange.info.author,
875
- date: row.structuralChange.info.date ?? null,
876
- ...row.structuralChange.info.utcDate ? { utcDate: row.structuralChange.info.utcDate.value } : {},
877
- ...row.structuralChange.info.initials ? { initials: row.structuralChange.info.initials } : {}
976
+ const rowStructuralChange = row.structuralChange;
977
+ if (rowStructuralChange) {
978
+ const revision = {
979
+ revisionId: rowStructuralChange.info.id,
980
+ author: rowStructuralChange.info.author,
981
+ date: rowStructuralChange.info.date ?? null,
982
+ ...rowStructuralChange.info.utcDate ? { utcDate: rowStructuralChange.info.utcDate.value } : {},
983
+ ...rowStructuralChange.info.initials ? { initials: rowStructuralChange.info.initials } : {}
984
+ };
985
+ switch (rowStructuralChange.type) {
986
+ case "tableRowInsertion":
987
+ attrs = {
988
+ ...attrsWithoutStructuralChange,
989
+ trIns: revision
990
+ };
991
+ break;
992
+ case "tableRowDeletion":
993
+ attrs = {
994
+ ...attrsWithoutStructuralChange,
995
+ trDel: revision
996
+ };
997
+ break;
998
+ case "tableCellInsertion":
999
+ case "tableCellDeletion":
1000
+ case "tableCellMerge": break;
1001
+ default: panic(`Unsupported table row structural change: ${JSON.stringify(rowStructuralChange)}`);
878
1002
  }
879
- };
1003
+ }
880
1004
  const numCells = row.cells.length;
881
1005
  const isFirstRow = rowIndex === 0;
882
1006
  const isLastRow = rowIndex === (totalRows ?? 1) - 1;
@@ -1006,38 +1130,41 @@ function convertTableCell({ cell, styleResolver, context, isHeader, gridWidthPer
1006
1130
  }
1007
1131
  if (formatting) attrs._originalFormatting = formatting;
1008
1132
  if (cell.propertyChanges && cell.propertyChanges.length > 0) attrs.tcPrChange = [...cell.propertyChanges];
1009
- if (cell.structuralChange?.type === "tableCellInsertion") attrs.cellMarker = {
1010
- kind: "ins",
1011
- info: {
1012
- revisionId: cell.structuralChange.info.id,
1013
- author: cell.structuralChange.info.author,
1014
- date: cell.structuralChange.info.date ?? null,
1015
- ...cell.structuralChange.info.utcDate ? { utcDate: cell.structuralChange.info.utcDate.value } : {},
1016
- ...cell.structuralChange.info.initials ? { initials: cell.structuralChange.info.initials } : {}
1017
- }
1018
- };
1019
- else if (cell.structuralChange?.type === "tableCellDeletion") attrs.cellMarker = {
1020
- kind: "del",
1021
- info: {
1022
- revisionId: cell.structuralChange.info.id,
1023
- author: cell.structuralChange.info.author,
1024
- date: cell.structuralChange.info.date ?? null,
1025
- ...cell.structuralChange.info.utcDate ? { utcDate: cell.structuralChange.info.utcDate.value } : {},
1026
- ...cell.structuralChange.info.initials ? { initials: cell.structuralChange.info.initials } : {}
1133
+ const cellStructuralChange = cell.structuralChange;
1134
+ if (cellStructuralChange) {
1135
+ const info = {
1136
+ revisionId: cellStructuralChange.info.id,
1137
+ author: cellStructuralChange.info.author,
1138
+ date: cellStructuralChange.info.date ?? null,
1139
+ ...cellStructuralChange.info.utcDate ? { utcDate: cellStructuralChange.info.utcDate.value } : {},
1140
+ ...cellStructuralChange.info.initials ? { initials: cellStructuralChange.info.initials } : {}
1141
+ };
1142
+ switch (cellStructuralChange.type) {
1143
+ case "tableCellInsertion":
1144
+ attrs.cellMarker = {
1145
+ kind: "ins",
1146
+ info
1147
+ };
1148
+ break;
1149
+ case "tableCellDeletion":
1150
+ attrs.cellMarker = {
1151
+ kind: "del",
1152
+ info
1153
+ };
1154
+ break;
1155
+ case "tableCellMerge":
1156
+ attrs.cellMarker = {
1157
+ kind: "merge",
1158
+ info,
1159
+ ...cellStructuralChange.verticalMerge !== void 0 ? { verticalMerge: cellStructuralChange.verticalMerge } : {},
1160
+ ...cellStructuralChange.verticalMergeOriginal !== void 0 ? { verticalMergeOriginal: cellStructuralChange.verticalMergeOriginal } : {}
1161
+ };
1162
+ break;
1163
+ case "tableRowInsertion":
1164
+ case "tableRowDeletion": break;
1165
+ default: panic(`Unsupported table cell structural change: ${JSON.stringify(cellStructuralChange)}`);
1027
1166
  }
1028
- };
1029
- else if (cell.structuralChange?.type === "tableCellMerge") attrs.cellMarker = {
1030
- kind: "merge",
1031
- info: {
1032
- revisionId: cell.structuralChange.info.id,
1033
- author: cell.structuralChange.info.author,
1034
- date: cell.structuralChange.info.date ?? null,
1035
- ...cell.structuralChange.info.utcDate ? { utcDate: cell.structuralChange.info.utcDate.value } : {},
1036
- ...cell.structuralChange.info.initials ? { initials: cell.structuralChange.info.initials } : {}
1037
- },
1038
- ...cell.structuralChange.verticalMerge !== void 0 ? { verticalMerge: cell.structuralChange.verticalMerge } : {},
1039
- ...cell.structuralChange.verticalMergeOriginal !== void 0 ? { verticalMergeOriginal: cell.structuralChange.verticalMergeOriginal } : {}
1040
- };
1167
+ }
1041
1168
  if (preserveVMergeRestart) attrs._preserveVMergeRestart = true;
1042
1169
  if (vMergeContinuationCells && vMergeContinuationCells.length > 0) attrs._docxVMergeContinuationCells = transportTableCellsWithParagraphPropertySources(vMergeContinuationCells);
1043
1170
  const contentNodes = [];
@@ -1136,6 +1263,22 @@ function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyp
1136
1263
  /**
1137
1264
  * Convert a MathEquation to a ProseMirror math node.
1138
1265
  */
1266
+ /**
1267
+ * Flatten `w:bdo`/`w:dir` out of an inline content list.
1268
+ *
1269
+ * The editor has no projection for a bidirectional wrapper yet, and the
1270
+ * alternative to flattening it is worse than losing the direction: every
1271
+ * inline loop below narrows by a chain of `else if` rather than by
1272
+ * exhaustion, so an unhandled member reaches whichever branch happens to be
1273
+ * last and is read as that. Flattening keeps the wrapper's content and loses
1274
+ * only its direction, which the survival census records as an
1275
+ * editor-projection loss until the mark that carries it exists.
1276
+ *
1277
+ * The save path is unaffected: a document opened and saved without being
1278
+ * edited replays its markup, and one that is edited keeps the wrapper because
1279
+ * `fromProseDoc` rebuilds from the source paragraph.
1280
+ */
1281
+ const withoutBidiWrappers = (content) => content.flatMap((item) => item.type === "bidiWrapper" ? withoutBidiWrappers(item.content) : [item]);
1139
1282
  function convertMathEquation(math) {
1140
1283
  return schema.node("math", {
1141
1284
  display: math.display,
@@ -1149,37 +1292,50 @@ function convertMathEquation(math) {
1149
1292
  function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors) {
1150
1293
  const props = sdt.properties;
1151
1294
  const inlineNodes = [];
1152
- for (const content of sdt.content) if (content.type === "run") {
1153
- const runNodes = convertRun(content, getInheritedRunFormatting(content.formatting), nextPageBreakRunOwnerId, styleResolver, textBoxAnchors);
1154
- inlineNodes.push(...runNodes);
1155
- } else if (content.type === "hyperlink") {
1156
- const linkNodes = convertHyperlink(content, {
1157
- getInheritedRunFormatting,
1158
- styleResolver,
1159
- hyperlinkIndex: nextHyperlinkInstanceIndex(),
1160
- textBoxAnchors,
1161
- nextPageBreakRunOwnerId
1162
- });
1163
- inlineNodes.push(...linkNodes);
1164
- } else if (content.type === "simpleField" || content.type === "complexField") {
1165
- const fieldNode = convertField(content, {
1166
- getInheritedRunFormatting,
1167
- styleResolver,
1168
- nextHyperlinkInstanceIndex,
1169
- nextPageBreakRunOwnerId,
1170
- textBoxAnchors
1171
- });
1172
- if (fieldNode) inlineNodes.push(fieldNode);
1173
- } else if (content.type === "inlineSdt") {
1174
- const nestedSdt = convertInlineSdt(content, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors);
1175
- if (nestedSdt) inlineNodes.push(nestedSdt);
1176
- } else if (content.type === "insertion") inlineNodes.push(...convertTrackedChange(content, "insertion", nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, null, textBoxAnchors));
1177
- else if (content.type === "deletion") inlineNodes.push(...convertTrackedChange(content, "deletion", nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, null, textBoxAnchors));
1178
- else if (content.type === "moveTo") inlineNodes.push(...convertTrackedChange(content, "insertion", nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, "moveTo", textBoxAnchors));
1179
- else if (content.type === "moveFrom") inlineNodes.push(...convertTrackedChange(content, "deletion", nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, "moveFrom", textBoxAnchors));
1180
- else {
1181
- const mathNode = convertMathEquation(content);
1182
- if (mathNode) inlineNodes.push(mathNode);
1295
+ for (const content of sdt.content) switch (content.type) {
1296
+ case "run":
1297
+ inlineNodes.push(...convertRun(content, getInheritedRunFormatting(content.formatting), nextPageBreakRunOwnerId, styleResolver, textBoxAnchors));
1298
+ break;
1299
+ case "hyperlink": {
1300
+ const currentHyperlinkIndex = nextHyperlinkInstanceIndex();
1301
+ inlineNodes.push(...convertHyperlink(content, {
1302
+ getInheritedRunFormatting,
1303
+ styleResolver,
1304
+ hyperlinkIndex: currentHyperlinkIndex,
1305
+ textBoxAnchors,
1306
+ nextPageBreakRunOwnerId
1307
+ }));
1308
+ break;
1309
+ }
1310
+ case "simpleField":
1311
+ case "complexField": {
1312
+ const fieldNode = convertField(content, {
1313
+ getInheritedRunFormatting,
1314
+ styleResolver,
1315
+ nextHyperlinkInstanceIndex,
1316
+ nextPageBreakRunOwnerId,
1317
+ textBoxAnchors
1318
+ });
1319
+ if (fieldNode) inlineNodes.push(fieldNode);
1320
+ break;
1321
+ }
1322
+ case "inlineSdt": {
1323
+ const nestedSdt = convertInlineSdt(content, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors);
1324
+ if (nestedSdt) inlineNodes.push(nestedSdt);
1325
+ break;
1326
+ }
1327
+ case "insertion":
1328
+ case "deletion":
1329
+ case "moveTo":
1330
+ case "moveFrom":
1331
+ inlineNodes.push(...convertTrackedChange(content, content.type === "insertion" || content.type === "moveTo" ? "insertion" : "deletion", nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, content.type === "moveTo" || content.type === "moveFrom" ? content.type : null, textBoxAnchors));
1332
+ break;
1333
+ case "mathEquation": {
1334
+ const mathNode = convertMathEquation(content);
1335
+ if (mathNode) inlineNodes.push(mathNode);
1336
+ break;
1337
+ }
1338
+ default: panic(`Unsupported inline SDT content: ${JSON.stringify(content)}`);
1183
1339
  }
1184
1340
  return schema.node("sdt", sdtAttrsFromProperties(props), inlineNodes.length > 0 ? inlineNodes : void 0);
1185
1341
  }
@@ -1196,7 +1352,7 @@ function convertRun(run, resolvedStyleFormatting, nextPageBreakRunOwnerId, style
1196
1352
  if (run.propertyChanges && run.propertyChanges.length > 0) marks.push(schema.mark("runPropertyChange", { changes: [...run.propertyChanges] }));
1197
1353
  if (run.content.some((content) => content.type === "break" && content.breakType === "page")) marks.push(schema.mark("pageBreakRunOwner", { id: nextPageBreakRunOwnerId() }));
1198
1354
  for (const content of run.content) {
1199
- const contentNodes = convertRunContent(content, marks, mergedFormatting, textBoxAnchors);
1355
+ const contentNodes = convertRunContent(content, marks, mergedFormatting, textBoxAnchors, run.formatting);
1200
1356
  nodes.push(...contentNodes);
1201
1357
  }
1202
1358
  return nodes;
@@ -1263,6 +1419,7 @@ const scanLeadingPageBreakContent = (content, scan) => {
1263
1419
  case "moveFrom":
1264
1420
  case "moveTo":
1265
1421
  case "inlineSdt":
1422
+ case "bidiWrapper":
1266
1423
  for (const child of content.content) scanLeadingPageBreakContent(child, scan);
1267
1424
  return;
1268
1425
  case "simpleField":
@@ -1458,7 +1615,7 @@ const canReconstructAuthoredRunFormatting = ({ directFormatting, effectiveFormat
1458
1615
  };
1459
1616
  function buildRunMarks(runFormatting, inherited, styleResolver) {
1460
1617
  if (runFormatting === void 0 && inherited.implicitCharacterStyleApplied === true && inherited.paragraphMarkOverrides === void 0) return {
1461
- marks: textFormattingToMarks(inherited.formatting, {
1618
+ marks: textFormattingToMarks(inherited.formatting, schema, {
1462
1619
  overrideFormatting: void 0,
1463
1620
  directFormatting: void 0,
1464
1621
  authoredCarrier: "reconstruct"
@@ -1502,7 +1659,7 @@ function buildRunMarks(runFormatting, inherited, styleResolver) {
1502
1659
  if (overrideFormatting?.bold === true) delete overrideFormatting.bold;
1503
1660
  if (overrideFormatting?.italic === true) delete overrideFormatting.italic;
1504
1661
  }
1505
- const marks = textFormattingToMarks(mergedFormatting, {
1662
+ const marks = textFormattingToMarks(mergedFormatting, schema, {
1506
1663
  overrideFormatting,
1507
1664
  directFormatting: runFormatting,
1508
1665
  authoredCarrier
@@ -1513,55 +1670,6 @@ function buildRunMarks(runFormatting, inherited, styleResolver) {
1513
1670
  mergedFormatting
1514
1671
  };
1515
1672
  }
1516
- const addDirectFontProvenance = (marks, directFormatting) => {
1517
- const directFontProperties = [];
1518
- if (directFormatting?.fontFamily !== void 0) directFontProperties.push("fontFamily");
1519
- if (directFormatting?.fontSize !== void 0) directFontProperties.push("fontSize");
1520
- if (directFormatting?.color !== void 0) directFontProperties.push("color");
1521
- if (directFontProperties.length === 0) return;
1522
- const index = marks.findIndex(({ type }) => type.name === "runFormattingOverride");
1523
- const existing = index >= 0 ? marks.at(index) : void 0;
1524
- const override = schema.mark("runFormattingOverride", {
1525
- ...existing?.attrs,
1526
- directFontProperties
1527
- });
1528
- if (index >= 0) {
1529
- marks[index] = override;
1530
- return;
1531
- }
1532
- marks.push(override);
1533
- };
1534
- const COMPLEX_SCRIPT_MIRRORS = [
1535
- {
1536
- ordinary: "bold",
1537
- complex: "boldCs"
1538
- },
1539
- {
1540
- ordinary: "italic",
1541
- complex: "italicCs"
1542
- },
1543
- {
1544
- ordinary: "fontSize",
1545
- complex: "fontSizeCs"
1546
- }
1547
- ];
1548
- const addComplexScriptAbsenceProvenance = (marks, directFormatting) => {
1549
- const absent = COMPLEX_SCRIPT_MIRRORS.filter(({ ordinary, complex }) => directFormatting?.[ordinary] !== void 0 && directFormatting[complex] === void 0).map(({ complex }) => complex);
1550
- if (absent.length === 0) return;
1551
- const index = marks.findIndex(({ type }) => type.name === "runFormattingOverride");
1552
- const existing = index >= 0 ? marks.at(index) : void 0;
1553
- const existingAbsences = new Set(existing?.attrs["complexScriptPropertyAbsences"] ?? []);
1554
- for (const property of absent) existingAbsences.add(property);
1555
- const override = schema.mark("runFormattingOverride", {
1556
- ...existing?.attrs,
1557
- complexScriptPropertyAbsences: COMPLEX_SCRIPT_RUN_PROPERTY_KEYS.filter((property) => existingAbsences.has(property))
1558
- });
1559
- if (index >= 0) {
1560
- marks[index] = override;
1561
- return;
1562
- }
1563
- marks.push(override);
1564
- };
1565
1673
  const ORDINARY_STYLE_TOGGLE_KEYS = [
1566
1674
  "strike",
1567
1675
  "allCaps",
@@ -1579,9 +1687,28 @@ function getRunFormattingOverrides({ directFormatting, effectiveStyleFormatting,
1579
1687
  return overrides;
1580
1688
  }
1581
1689
  /**
1690
+ * The vertical alignment a note reference mark carries.
1691
+ *
1692
+ * The mark models the two alignments a reference can sit at; `subscript` is not
1693
+ * one of them and drops to `null`. `baseline` is read through its owner, since
1694
+ * it is the reserved "no offset" value rather than an offset of its own.
1695
+ */
1696
+ const noteReferenceVertAlign = (vertAlign) => {
1697
+ if (isBaselineVertAlign(vertAlign)) return "baseline";
1698
+ return vertAlign === "superscript" ? "superscript" : null;
1699
+ };
1700
+ /**
1701
+ * The run properties an inline atom has to carry itself.
1702
+ *
1703
+ * `withRunBoundaryMarks` keeps the run's formatting marks off an image or
1704
+ * shape node, so the node is the only place left to record the `w:rPr` the
1705
+ * run was authored with.
1706
+ */
1707
+ const carriedRunFormatting = (formatting) => formatting && Object.keys(formatting).length > 0 ? formatting : void 0;
1708
+ /**
1582
1709
  * Convert RunContent to ProseMirror nodes
1583
1710
  */
1584
- function convertRunContent(content, marks, formatting, textBoxAnchors) {
1711
+ function convertRunContent(content, marks, formatting, textBoxAnchors, authoredFormatting) {
1585
1712
  switch (content.type) {
1586
1713
  case "text":
1587
1714
  if (content.text) return [schema.text(content.text, marks)];
@@ -1605,7 +1732,8 @@ function convertRunContent(content, marks, formatting, textBoxAnchors) {
1605
1732
  image: content.image,
1606
1733
  rawXml: content.rawXml,
1607
1734
  rawXmlMode: content.rawXmlMode,
1608
- rawImageFingerprint: content.rawXmlMode === DRAWING_RAW_XML_MODES.PRESERVE_ONLY ? void 0 : content.rawImageFingerprint
1735
+ rawImageFingerprint: content.rawXmlMode === DRAWING_RAW_XML_MODES.PRESERVE_ONLY ? void 0 : content.rawImageFingerprint,
1736
+ runFormatting: carriedRunFormatting(authoredFormatting)
1609
1737
  }), marks)];
1610
1738
  case "shape": {
1611
1739
  const shp = content.shape;
@@ -1613,13 +1741,13 @@ function convertRunContent(content, marks, formatting, textBoxAnchors) {
1613
1741
  const anchorId = textBoxAnchors?.get(shp);
1614
1742
  return anchorId ? [schema.node("textBoxAnchor", { anchorId }).mark(marks)] : [];
1615
1743
  }
1616
- return [withRunBoundaryMarks(convertShape(shp), marks)];
1744
+ return [withRunBoundaryMarks(convertShape(shp, carriedRunFormatting(authoredFormatting)), marks)];
1617
1745
  }
1618
1746
  case "footnoteRef": {
1619
1747
  const footnoteMark = schema.mark("footnoteRef", {
1620
1748
  id: content.id.toString(),
1621
1749
  noteType: "footnote",
1622
- vertAlign: formatting?.vertAlign === "baseline" || formatting?.vertAlign === "superscript" ? formatting.vertAlign : null
1750
+ vertAlign: noteReferenceVertAlign(formatting?.vertAlign)
1623
1751
  });
1624
1752
  return [schema.text(content.id.toString(), [...marks, footnoteMark])];
1625
1753
  }
@@ -1627,7 +1755,7 @@ function convertRunContent(content, marks, formatting, textBoxAnchors) {
1627
1755
  const endnoteMark = schema.mark("footnoteRef", {
1628
1756
  id: content.id.toString(),
1629
1757
  noteType: "endnote",
1630
- vertAlign: formatting?.vertAlign === "baseline" || formatting?.vertAlign === "superscript" ? formatting.vertAlign : null
1758
+ vertAlign: noteReferenceVertAlign(formatting?.vertAlign)
1631
1759
  });
1632
1760
  return [schema.text(content.id.toString(), [...marks, endnoteMark])];
1633
1761
  }
@@ -1645,7 +1773,19 @@ function withRunBoundaryMarks(node, marks) {
1645
1773
  if (!marks.some(({ type }) => type.name === "hyperlink" || type.name === "pageBreakRunOwner")) return node;
1646
1774
  return node.mark(marks);
1647
1775
  }
1648
- function convertImage({ image, rawXml, rawXmlMode, rawImageFingerprint }) {
1776
+ /**
1777
+ * The authored-EMU carrier for a drawing, or nothing when the source authored
1778
+ * none of these values and there is accordingly nothing to carry.
1779
+ */
1780
+ const authoredEmuAttrs = (values) => Object.values(values).some((value) => value !== void 0) ? values : void 0;
1781
+ /** The wrap insets of a drawing, keyed by the pixel attribute each becomes. */
1782
+ const wrapDistanceEmu = (wrap) => ({
1783
+ distTop: wrap?.distT,
1784
+ distBottom: wrap?.distB,
1785
+ distLeft: wrap?.distL,
1786
+ distRight: wrap?.distR
1787
+ });
1788
+ function convertImage({ image, rawXml, rawXmlMode, rawImageFingerprint, runFormatting }) {
1649
1789
  const imageSize = image.size;
1650
1790
  const widthPx = imageSize?.width ? emuToPixels(imageSize.width) : void 0;
1651
1791
  const heightPx = imageSize?.height ? emuToPixels(imageSize.height) : void 0;
@@ -1700,7 +1840,7 @@ function convertImage({ image, rawXml, rawXmlMode, rawImageFingerprint }) {
1700
1840
  let borderColor;
1701
1841
  let borderStyle;
1702
1842
  if (image.outline && image.outline.width) {
1703
- borderWidth = Math.round(image.outline.width / 914400 * 96 * 100) / 100;
1843
+ borderWidth = emuToStrokePixels(image.outline.width);
1704
1844
  if (image.outline.color?.rgb) borderColor = `#${image.outline.color.rgb}`;
1705
1845
  borderStyle = image.outline.style ? {
1706
1846
  solid: "solid",
@@ -1743,8 +1883,17 @@ function convertImage({ image, rawXml, rawXmlMode, rawImageFingerprint }) {
1743
1883
  paddingLeft: image.padding?.left,
1744
1884
  position,
1745
1885
  layoutInCell: image.layoutInCell,
1886
+ decorative: image.decorative,
1887
+ hidden: image.hidden,
1888
+ docPrExtensions: image.docPrExtensions ? [...image.docPrExtensions] : void 0,
1746
1889
  frameLocks: image.frameLocks ? { ...image.frameLocks } : void 0,
1747
1890
  borderWidth,
1891
+ _docxAuthoredEmu: authoredEmuAttrs({
1892
+ width: imageSize?.width,
1893
+ height: imageSize?.height,
1894
+ borderWidth: image.outline?.width,
1895
+ ...wrapDistanceEmu(image.wrap)
1896
+ }),
1748
1897
  borderColor,
1749
1898
  borderStyle,
1750
1899
  wrapText,
@@ -1753,7 +1902,8 @@ function convertImage({ image, rawXml, rawXmlMode, rawImageFingerprint }) {
1753
1902
  _docxRawXml: rawXml,
1754
1903
  _docxRawXmlMode: rawXmlMode,
1755
1904
  _docxRawImageFingerprint: rawImageFingerprint,
1756
- _docxObjectPreview: rawXml !== void 0 && /<(?:[A-Za-z_][\w.-]*:)?object(?:\s|>)/u.test(rawXml)
1905
+ _docxObjectPreview: rawXml !== void 0 && /<(?:[A-Za-z_][\w.-]*:)?object(?:\s|>)/u.test(rawXml),
1906
+ _docxRunFormatting: runFormatting
1757
1907
  });
1758
1908
  }
1759
1909
  function convertHyperlink(hyperlink, { getInheritedRunFormatting, styleResolver, hyperlinkIndex, textBoxAnchors, nextPageBreakRunOwnerId }) {
@@ -1772,14 +1922,16 @@ function convertHyperlink(hyperlink, { getInheritedRunFormatting, styleResolver,
1772
1922
  id: child.id,
1773
1923
  name: child.name,
1774
1924
  colFirst: child.colFirst,
1775
- colLast: child.colLast
1925
+ colLast: child.colLast,
1926
+ displacedByCustomXml: child.displacedByCustomXml
1776
1927
  }, void 0, [linkMark]));
1777
1928
  continue;
1778
1929
  }
1779
1930
  if (child.type === "bookmarkEnd") {
1780
1931
  nodes.push(schema.node("bookmarkBoundary", {
1781
1932
  type: "end",
1782
- id: child.id
1933
+ id: child.id,
1934
+ displacedByCustomXml: child.displacedByCustomXml
1783
1935
  }, void 0, [linkMark]));
1784
1936
  continue;
1785
1937
  }
@@ -1790,82 +1942,15 @@ function convertHyperlink(hyperlink, { getInheritedRunFormatting, styleResolver,
1790
1942
  if (child.propertyChanges && child.propertyChanges.length > 0) runMarks.push(schema.mark("runPropertyChange", { changes: [...child.propertyChanges] }));
1791
1943
  if (child.content.some((content) => content.type === "break" && content.breakType === "page")) runMarks.push(schema.mark("pageBreakRunOwner", { id: nextPageBreakRunOwnerId() }));
1792
1944
  const allMarks = [...runMarks, linkMark];
1793
- for (const content of child.content) nodes.push(...convertRunContent(content, allMarks, mergedFormatting, textBoxAnchors));
1945
+ for (const content of child.content) nodes.push(...convertRunContent(content, allMarks, mergedFormatting, textBoxAnchors, child.formatting));
1794
1946
  }
1795
1947
  }
1796
1948
  return nodes;
1797
1949
  }
1798
- function textFormattingToMarks(formatting, options) {
1799
- if (!formatting) return [];
1800
- const marks = [];
1801
- const overrideFormatting = options ? options.overrideFormatting : formatting;
1802
- let overrideAttrs;
1803
- if (options?.authoredCarrier === "reconstruct") overrideAttrs = buildRunFormattingOverrideAttrs(overrideFormatting, { type: "structural-only" });
1804
- else if (options) overrideAttrs = buildRunFormattingOverrideAttrs(overrideFormatting, {
1805
- type: "authored-baseline",
1806
- formatting: options.directFormatting
1807
- });
1808
- else overrideAttrs = buildRunFormattingOverrideAttrs(overrideFormatting);
1809
- if (overrideAttrs) marks.push(schema.mark("runFormattingOverride", overrideAttrs));
1810
- if (formatting.bold) marks.push(schema.mark("bold"));
1811
- if (formatting.italic) marks.push(schema.mark("italic"));
1812
- if (formatting.underline && formatting.underline.style !== "none") marks.push(schema.mark("underline", {
1813
- style: formatting.underline.style,
1814
- color: formatting.underline.color
1815
- }));
1816
- if (formatting.strike || formatting.doubleStrike) marks.push(schema.mark("strike", { double: formatting.doubleStrike || false }));
1817
- if (formatting.color && !formatting.color.auto) marks.push(schema.mark("textColor", {
1818
- rgb: formatting.color.rgb,
1819
- themeColor: formatting.color.themeColor,
1820
- themeTint: formatting.color.themeTint,
1821
- themeShade: formatting.color.themeShade
1822
- }));
1823
- if (formatting.highlight && formatting.highlight !== "none") marks.push(schema.mark("highlight", { color: formatting.highlight }));
1824
- const runShadingMarkAttrs = shadingToRunShadingAttrs(formatting.shading);
1825
- if (runShadingMarkAttrs) marks.push(schema.mark("runShading", runShadingMarkAttrs));
1826
- if (formatting.fontSize) marks.push(schema.mark("fontSize", { size: formatting.fontSize }));
1827
- if (formatting.fontFamily) marks.push(schema.mark("fontFamily", {
1828
- ascii: formatting.fontFamily.ascii,
1829
- hAnsi: formatting.fontFamily.hAnsi,
1830
- eastAsia: formatting.fontFamily.eastAsia,
1831
- cs: formatting.fontFamily.cs,
1832
- hint: formatting.fontFamily.hint,
1833
- asciiTheme: formatting.fontFamily.asciiTheme,
1834
- hAnsiTheme: formatting.fontFamily.hAnsiTheme,
1835
- eastAsiaTheme: formatting.fontFamily.eastAsiaTheme,
1836
- csTheme: formatting.fontFamily.csTheme
1837
- }));
1838
- if (formatting.language) marks.push(schema.mark("language", formatting.language));
1839
- if (formatting.vertAlign === "superscript") marks.push(schema.mark("superscript"));
1840
- else if (formatting.vertAlign === "subscript") marks.push(schema.mark("subscript"));
1841
- if (formatting.allCaps) marks.push(schema.mark("allCaps"));
1842
- if (formatting.smallCaps) marks.push(schema.mark("smallCaps"));
1843
- const spacing = typeof formatting.spacing === "number" ? formatting.spacing : null;
1844
- const position = typeof formatting.position === "number" ? formatting.position : null;
1845
- const scale = normalizeHorizontalScalePercent(formatting.scale) ?? null;
1846
- const kerning = typeof formatting.kerning === "number" ? formatting.kerning : null;
1847
- if (spacing !== null || position !== null || scale !== null || kerning !== null) marks.push(schema.mark("characterSpacing", {
1848
- spacing,
1849
- position,
1850
- scale,
1851
- kerning
1852
- }));
1853
- if (formatting.hidden === true) marks.push(schema.mark("hidden"));
1854
- if (formatting.emboss) marks.push(schema.mark("emboss"));
1855
- if (formatting.imprint) marks.push(schema.mark("imprint"));
1856
- if (formatting.shadow) marks.push(schema.mark("textShadow"));
1857
- if (formatting.emphasisMark && formatting.emphasisMark !== "none") marks.push(schema.mark("emphasisMark", { type: formatting.emphasisMark }));
1858
- if (formatting.outline) marks.push(schema.mark("textOutline"));
1859
- if (formatting.rtl) marks.push(schema.mark("rtl"));
1860
- if (formatting.effect && formatting.effect !== "none") marks.push(schema.mark("textEffect", { effect: formatting.effect }));
1861
- addDirectFontProvenance(marks, options?.directFormatting);
1862
- if (options?.authoredCarrier === "preserve") addComplexScriptAbsenceProvenance(marks, options.directFormatting);
1863
- return marks;
1864
- }
1865
1950
  /**
1866
1951
  * Convert a Shape to a ProseMirror shape node (inline SVG)
1867
1952
  */
1868
- function convertShape(shape) {
1953
+ function convertShape(shape, runFormatting) {
1869
1954
  const shapeSize = shape.size;
1870
1955
  const widthPx = shapeSize?.width ? emuToPixels(shapeSize.width) : 100;
1871
1956
  const heightPx = shapeSize?.height ? emuToPixels(shapeSize.height) : 80;
@@ -1897,16 +1982,18 @@ function convertShape(shape) {
1897
1982
  let outlineColorValue;
1898
1983
  let outlineStyle = "none";
1899
1984
  let outlineCap;
1985
+ let outlineJoin;
1900
1986
  let outlineHeadEnd;
1901
1987
  let outlineTailEnd;
1902
1988
  if (shape.outline) {
1903
- if (shape.outline.width) outlineWidth = Math.round(shape.outline.width / 914400 * 96 * 100) / 100;
1989
+ if (shape.outline.width) outlineWidth = emuToStrokePixels(shape.outline.width);
1904
1990
  if (shape.outline.color) {
1905
1991
  outlineColorValue = shape.outline.color;
1906
1992
  outlineColor = resolveColorValueToHex(shape.outline.color);
1907
1993
  }
1908
- outlineStyle = shape.outline.style || "solid";
1994
+ outlineStyle = shape.outline.style;
1909
1995
  outlineCap = shape.outline.cap;
1996
+ outlineJoin = shape.outline.join;
1910
1997
  outlineHeadEnd = shape.outline.headEnd;
1911
1998
  outlineTailEnd = shape.outline.tailEnd;
1912
1999
  } else outlineWidth = 0;
@@ -1937,11 +2024,21 @@ function convertShape(shape) {
1937
2024
  }
1938
2025
  };
1939
2026
  return schema.node("shape", {
2027
+ _docxRunFormatting: runFormatting,
1940
2028
  shapeType: shapeAttrs.shapeType ?? "rect",
1941
2029
  geometryAdjustments: shape.geometryAdjustments === void 0 ? void 0 : JSON.stringify(shape.geometryAdjustments),
1942
2030
  shapeId: shape.id,
2031
+ shapeName: shape.name,
2032
+ alt: shape.alt,
2033
+ title: shape.title,
1943
2034
  width: widthPx,
1944
2035
  height: heightPx,
2036
+ _docxAuthoredEmu: authoredEmuAttrs({
2037
+ width: shapeSize?.width,
2038
+ height: shapeSize?.height,
2039
+ outlineWidth: shape.outline?.width,
2040
+ ...wrapDistanceEmu(shape.wrap)
2041
+ }),
1945
2042
  fillColor,
1946
2043
  fillColorValue,
1947
2044
  fillType,
@@ -1953,6 +2050,7 @@ function convertShape(shape) {
1953
2050
  outlineColorValue,
1954
2051
  outlineStyle,
1955
2052
  outlineCap,
2053
+ outlineJoin,
1956
2054
  outlineHeadEnd,
1957
2055
  outlineTailEnd,
1958
2056
  transform,
@@ -2108,6 +2206,9 @@ function textBoxFromShape(shape, textBody) {
2108
2206
  content: textBody.content
2109
2207
  };
2110
2208
  if (shape.id) textBox.id = shape.id;
2209
+ if (shape.name !== void 0) textBox.name = shape.name;
2210
+ if (shape.alt !== void 0) textBox.alt = shape.alt;
2211
+ if (shape.title !== void 0) textBox.title = shape.title;
2111
2212
  if (shape.position) textBox.position = shape.position;
2112
2213
  if (shape.wrap) textBox.wrap = shape.wrap;
2113
2214
  if (shape.fill) textBox.fill = shape.fill;
@@ -2134,7 +2235,7 @@ function convertTextBox(textBox, styleResolver, options) {
2134
2235
  let outlineColor;
2135
2236
  let outlineStyle;
2136
2237
  if (textBox.outline && textBox.outline.width) {
2137
- outlineWidth = Math.round(textBox.outline.width / 914400 * 96 * 100) / 100;
2238
+ outlineWidth = emuToStrokePixels(textBox.outline.width);
2138
2239
  if (textBox.outline.color?.rgb) outlineColor = `#${textBox.outline.color.rgb}`;
2139
2240
  outlineStyle = textBox.outline.style || "solid";
2140
2241
  }
@@ -2146,10 +2247,10 @@ function convertTextBox(textBox, styleResolver, options) {
2146
2247
  if (textBox.transform.flipV) transforms.push("scaleY(-1)");
2147
2248
  if (transforms.length > 0) transform = transforms.join(" ");
2148
2249
  }
2149
- const marginTop = textBox.margins?.top !== void 0 ? emuToPixels(textBox.margins.top) : 4;
2150
- const marginBottom = textBox.margins?.bottom !== void 0 ? emuToPixels(textBox.margins.bottom) : 4;
2151
- const marginLeft = textBox.margins?.left !== void 0 ? emuToPixels(textBox.margins.left) : 7;
2152
- const marginRight = textBox.margins?.right !== void 0 ? emuToPixels(textBox.margins.right) : 7;
2250
+ const marginTop = textBox.margins?.top !== void 0 ? emuToPixels(textBox.margins.top) : void 0;
2251
+ const marginBottom = textBox.margins?.bottom !== void 0 ? emuToPixels(textBox.margins.bottom) : void 0;
2252
+ const marginLeft = textBox.margins?.left !== void 0 ? emuToPixels(textBox.margins.left) : void 0;
2253
+ const marginRight = textBox.margins?.right !== void 0 ? emuToPixels(textBox.margins.right) : void 0;
2153
2254
  const contentNodes = [];
2154
2255
  for (const block of textBox.content) {
2155
2256
  if (block.type === "paragraph") {
@@ -2193,18 +2294,31 @@ function convertTextBox(textBox, styleResolver, options) {
2193
2294
  else if (wrapType === "behind" || wrapType === "inFront") displayMode = "float";
2194
2295
  else if (cssFloat !== "none") displayMode = "float";
2195
2296
  else displayMode = "block";
2196
- const distTop = textBox.wrap?.distT ? emuToPixels(textBox.wrap.distT) : void 0;
2197
- const distBottom = textBox.wrap?.distB ? emuToPixels(textBox.wrap.distB) : void 0;
2198
- const distLeft = textBox.wrap?.distL ? emuToPixels(textBox.wrap.distL) : void 0;
2199
- const distRight = textBox.wrap?.distR ? emuToPixels(textBox.wrap.distR) : void 0;
2297
+ const distTop = textBox.wrap?.distT !== void 0 ? emuToPixels(textBox.wrap.distT) : void 0;
2298
+ const distBottom = textBox.wrap?.distB !== void 0 ? emuToPixels(textBox.wrap.distB) : void 0;
2299
+ const distLeft = textBox.wrap?.distL !== void 0 ? emuToPixels(textBox.wrap.distL) : void 0;
2300
+ const distRight = textBox.wrap?.distR !== void 0 ? emuToPixels(textBox.wrap.distR) : void 0;
2200
2301
  return schema.node("textBox", {
2201
2302
  width: widthPx,
2202
2303
  height: heightPx,
2304
+ _docxAuthoredEmu: authoredEmuAttrs({
2305
+ width: textBoxSize?.width,
2306
+ height: textBoxSize?.height,
2307
+ outlineWidth: textBox.outline?.width,
2308
+ marginTop: textBox.margins?.top,
2309
+ marginBottom: textBox.margins?.bottom,
2310
+ marginLeft: textBox.margins?.left,
2311
+ marginRight: textBox.margins?.right,
2312
+ ...wrapDistanceEmu(textBox.wrap)
2313
+ }),
2203
2314
  autoFit: textBox.autoFit,
2204
2315
  wordArt: textBox.wordArt,
2205
2316
  textWrap: textBox.textWrap,
2206
2317
  verticalAlign: textBox.verticalAlign,
2207
2318
  textBoxId: textBox.id,
2319
+ textBoxName: textBox.name,
2320
+ alt: textBox.alt,
2321
+ title: textBox.title,
2208
2322
  fillColor,
2209
2323
  outlineWidth,
2210
2324
  outlineColor,
@@ -2253,24 +2367,33 @@ function headerFooterToProseDoc(content, options) {
2253
2367
  };
2254
2368
  const convertBlocks = (blocks) => {
2255
2369
  const out = [];
2256
- for (const block of blocks) if (block.type === "paragraph") {
2257
- const isDetachedWatermarkHost = Reflect.get(block, DETACHED_WATERMARK_HOST) === true;
2258
- const paragraphNodes = convertParagraphWithTextBoxes(block, styleResolver, {
2259
- textBoxGroupId: nextTextBoxGroupId(),
2260
- context: conversionContext,
2261
- preserveEmptyWrapper: isDetachedWatermarkHost
2262
- });
2263
- if (isDetachedWatermarkHost) {
2264
- const paragraphNodeIndex = paragraphNodes.findIndex(({ type }) => type.name === "paragraph");
2265
- const paragraphNode = paragraphNodes[paragraphNodeIndex];
2266
- if (paragraphNode) paragraphNodes[paragraphNodeIndex] = recreateProseNodeWithParagraphPropertySource(paragraphNode, { attrs: {
2267
- ...paragraphNode.attrs,
2268
- _detachedWatermarkHost: true
2269
- } });
2370
+ for (const block of blocks) switch (block.type) {
2371
+ case "paragraph": {
2372
+ const isDetachedWatermarkHost = Reflect.get(block, DETACHED_WATERMARK_HOST) === true;
2373
+ const paragraphNodes = convertParagraphWithTextBoxes(block, styleResolver, {
2374
+ textBoxGroupId: nextTextBoxGroupId(),
2375
+ context: conversionContext,
2376
+ preserveEmptyWrapper: isDetachedWatermarkHost
2377
+ });
2378
+ if (isDetachedWatermarkHost) {
2379
+ const paragraphNodeIndex = paragraphNodes.findIndex(({ type }) => type.name === "paragraph");
2380
+ const paragraphNode = paragraphNodes[paragraphNodeIndex];
2381
+ if (paragraphNode) paragraphNodes[paragraphNodeIndex] = recreateProseNodeWithParagraphPropertySource(paragraphNode, { attrs: {
2382
+ ...paragraphNode.attrs,
2383
+ _detachedWatermarkHost: true
2384
+ } });
2385
+ }
2386
+ out.push(...paragraphNodes);
2387
+ break;
2270
2388
  }
2271
- out.push(...paragraphNodes);
2272
- } else if (block.type === "table") out.push(convertTable(block, styleResolver, conversionContext));
2273
- else out.push(convertBlockSdt(block, convertBlocks));
2389
+ case "table":
2390
+ out.push(convertTable(block, styleResolver, conversionContext));
2391
+ break;
2392
+ case "blockSdt":
2393
+ out.push(convertBlockSdt(block, convertBlocks));
2394
+ break;
2395
+ default: panic(`Unsupported block content: ${JSON.stringify(block)}`);
2396
+ }
2274
2397
  return out;
2275
2398
  };
2276
2399
  nodes.push(...convertBlocks(content));
@@ -2289,4 +2412,4 @@ function createEmptyDoc() {
2289
2412
  return schema.node("doc", null, [schema.node("paragraph", {}, [])]);
2290
2413
  }
2291
2414
  //#endregion
2292
- export { UnsupportedDocxToProseMirrorConversionError, createEmptyDoc, footnoteToProseDoc, headerFooterToProseDoc, resolveParagraphDefaultTextFormatting, standaloneTableCellToProseMirror, textFormattingToMarks, toProseDoc };
2415
+ export { UnsupportedDocxToProseMirrorConversionError, createEmptyDoc, footnoteToProseDoc, headerFooterToProseDoc, resolveParagraphDefaultTextFormatting, standaloneTableCellToProseMirror, toProseDoc };