@stll/folio-core 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/dist/ai-edits/apply.d.ts +2 -1
  2. package/dist/ai-edits/apply.js +693 -63
  3. package/dist/ai-edits/block-identity.d.ts +4 -0
  4. package/dist/ai-edits/block-identity.js +9 -0
  5. package/dist/ai-edits/blockRange.d.ts +28 -1
  6. package/dist/ai-edits/blockRange.js +68 -2
  7. package/dist/ai-edits/clean-text.js +2 -1
  8. package/dist/ai-edits/headless.d.ts +4 -4
  9. package/dist/ai-edits/headless.js +9 -9
  10. package/dist/ai-edits/index.d.ts +2 -2
  11. package/dist/ai-edits/index.js +2 -2
  12. package/dist/ai-edits/read.d.ts +7 -7
  13. package/dist/ai-edits/read.js +110 -3
  14. package/dist/ai-edits/snapshot.js +21 -3
  15. package/dist/ai-edits/table-cell-mutations.d.ts +48 -0
  16. package/dist/ai-edits/table-cell-mutations.js +238 -0
  17. package/dist/ai-edits/table-mutation-plan.d.ts +38 -0
  18. package/dist/ai-edits/table-mutation-plan.js +90 -0
  19. package/dist/ai-edits/table-row-column-mutations.d.ts +108 -0
  20. package/dist/ai-edits/table-row-column-mutations.js +300 -0
  21. package/dist/ai-edits/table-targets.d.ts +32 -0
  22. package/dist/ai-edits/table-targets.js +70 -0
  23. package/dist/ai-edits/types.d.ts +62 -1
  24. package/dist/ai-edits/word-diff.js +21 -0
  25. package/dist/compat/eigenpal.d.ts +3 -3
  26. package/dist/compat/eigenpal.js +3 -3
  27. package/dist/content-controls/mutateContentControls.js +1 -1
  28. package/dist/controller/activeEditorStory.d.ts +36 -0
  29. package/dist/controller/activeEditorStory.js +30 -0
  30. package/dist/controller/contentControlWidgetController.d.ts +55 -0
  31. package/dist/controller/contentControlWidgetController.js +141 -0
  32. package/dist/controller/fontReadiness.js +2 -0
  33. package/dist/controller/headerFooterEditorManager.js +2 -2
  34. package/dist/controller/hiddenEditorApi.js +2 -2
  35. package/dist/controller/hiddenEditorManager.d.ts +12 -1
  36. package/dist/controller/hiddenEditorManager.js +44 -12
  37. package/dist/controller/layoutPipeline.d.ts +2 -0
  38. package/dist/controller/layoutPipeline.js +17 -2
  39. package/dist/controller/noteEditorManager.d.ts +36 -0
  40. package/dist/controller/noteEditorManager.js +210 -0
  41. package/dist/document-operations.d.ts +43 -11
  42. package/dist/document-operations.js +186 -18
  43. package/dist/docx/blockContentParser.js +21 -7
  44. package/dist/docx/commentParser.js +6 -1
  45. package/dist/docx/commentRangeIntegrity.d.ts +6 -0
  46. package/dist/docx/commentRangeIntegrity.js +186 -0
  47. package/dist/docx/conformance.js +62 -1
  48. package/dist/docx/documentGrid.d.ts +8 -0
  49. package/dist/docx/documentGrid.js +8 -0
  50. package/dist/docx/drawingUtils.js +1 -1
  51. package/dist/docx/encryption/agileDecryption.js +12 -2
  52. package/dist/docx/encryption/compoundFile.js +6 -1
  53. package/dist/docx/encryption/encryptionInfo.d.ts +9 -1
  54. package/dist/docx/encryption/encryptionInfo.js +18 -2
  55. package/dist/docx/encryption/openEncryptedDocx.js +1 -1
  56. package/dist/docx/ensureParaIds.js +8 -7
  57. package/dist/docx/fieldParser.js +1 -1
  58. package/dist/docx/groupDrawingParser.js +10 -5
  59. package/dist/docx/hyperlinkParser.js +1 -1
  60. package/dist/docx/imageParser.js +10 -6
  61. package/dist/docx/metadataPrivacy.js +17 -3
  62. package/dist/docx/notePropertiesParser.js +1 -1
  63. package/dist/docx/numberingParser.js +1 -1
  64. package/dist/docx/paragraphParser.js +70 -61
  65. package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
  66. package/dist/docx/paragraphTextBoxEnrichment.js +67 -13
  67. package/dist/docx/paragraphTraversal.js +1 -1
  68. package/dist/docx/parser.js +3 -3
  69. package/dist/docx/rezip.js +20 -9
  70. package/dist/docx/runParser.js +17 -6
  71. package/dist/docx/sdtProperties.js +1 -1
  72. package/dist/docx/sectionParser.js +18 -5
  73. package/dist/docx/selectiveSave.js +5 -1
  74. package/dist/docx/selectiveXmlPatch.d.ts +22 -1
  75. package/dist/docx/selectiveXmlPatch.js +61 -1
  76. package/dist/docx/serializer/blockSdtSerializer.js +3 -2
  77. package/dist/docx/serializer/commentSerializer.js +5 -6
  78. package/dist/docx/serializer/documentSerializer.js +2 -2
  79. package/dist/docx/serializer/headerFooterSerializer.js +2 -2
  80. package/dist/docx/serializer/noteSerializer.js +2 -2
  81. package/dist/docx/serializer/paragraphSerializer.js +23 -15
  82. package/dist/docx/serializer/runSerializer.js +25 -21
  83. package/dist/docx/serializer/sectionPropertiesSerializer.js +2 -1
  84. package/dist/docx/serializer/stylesSerializer.js +1 -1
  85. package/dist/docx/serializer/tableSerializer.d.ts +5 -4
  86. package/dist/docx/serializer/tableSerializer.js +26 -20
  87. package/dist/docx/serializer/xmlUtils.d.ts +13 -1
  88. package/dist/docx/serializer/xmlUtils.js +27 -1
  89. package/dist/docx/server/applyDocxXmlPatchProposal.d.ts +87 -0
  90. package/dist/docx/server/applyDocxXmlPatchProposal.js +204 -0
  91. package/dist/docx/server/boundedArchive.d.ts +14 -3
  92. package/dist/docx/server/boundedArchive.js +56 -11
  93. package/dist/docx/server/evaluateDocxXmlPatchProposal.d.ts +85 -0
  94. package/dist/docx/server/evaluateDocxXmlPatchProposal.js +361 -0
  95. package/dist/docx/server/extractDocxText.js +55 -8
  96. package/dist/docx/server/inspectDocxPackage.d.ts +70 -0
  97. package/dist/docx/server/inspectDocxPackage.js +217 -0
  98. package/dist/docx/server/validateDocxConformance.d.ts +41 -0
  99. package/dist/docx/server/validateDocxConformance.js +336 -0
  100. package/dist/docx/settingsParser.js +26 -3
  101. package/dist/docx/shapeParser.js +1 -1
  102. package/dist/docx/styleParser.js +4 -2
  103. package/dist/docx/tableParser.d.ts +8 -1
  104. package/dist/docx/tableParser.js +31 -10
  105. package/dist/docx/textBoxParser.d.ts +3 -3
  106. package/dist/docx/textBoxParser.js +16 -12
  107. package/dist/docx/unzip.js +1 -0
  108. package/dist/docx/vmlImageParser.js +11 -3
  109. package/dist/docx/xmlParser.js +11 -1
  110. package/dist/docx/xmlSafety.d.ts +5 -0
  111. package/dist/docx/xmlSafety.js +9 -0
  112. package/dist/fields/resolveFieldValues.js +1 -3
  113. package/dist/fonts/embeddedFonts.d.ts +33 -5
  114. package/dist/fonts/embeddedFonts.js +37 -8
  115. package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
  116. package/dist/i18n/messages/catalogs.gen.js +17 -0
  117. package/dist/i18n/messages/messages.gen.d.ts +1 -0
  118. package/dist/index.d.ts +3 -3
  119. package/dist/index.js +3 -3
  120. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  121. package/dist/layout-bridge/convert/footnoteLayout.js +47 -40
  122. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  123. package/dist/layout-bridge/convert/headerFooterLayout.js +3 -0
  124. package/dist/layout-bridge/convert/templatePreviewFlow.js +3 -3
  125. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
  126. package/dist/layout-bridge/convert/toFlowBlocks.js +151 -20
  127. package/dist/layout-bridge/dom/noteStoryDom.d.ts +6 -0
  128. package/dist/layout-bridge/dom/noteStoryDom.js +17 -0
  129. package/dist/layout-bridge/engine/clickToPosition.js +2 -2
  130. package/dist/layout-engine/index.d.ts +2 -2
  131. package/dist/layout-engine/index.js +58 -30
  132. package/dist/layout-engine/justificationCompatibility.d.ts +6 -0
  133. package/dist/layout-engine/justificationCompatibility.js +8 -0
  134. package/dist/layout-engine/keep-together.js +11 -1
  135. package/dist/layout-engine/measure/cache.js +23 -5
  136. package/dist/layout-engine/measure/effectiveLineBreakPolicy.js +8 -2
  137. package/dist/layout-engine/measure/lineBreakProvider.d.ts +2 -2
  138. package/dist/layout-engine/measure/lineBreakProvider.js +56 -37
  139. package/dist/layout-engine/measure/measureBlocks.js +15 -4
  140. package/dist/layout-engine/measure/measureContainer.js +1 -1
  141. package/dist/layout-engine/measure/measureHelpers.d.ts +11 -2
  142. package/dist/layout-engine/measure/measureHelpers.js +15 -4
  143. package/dist/layout-engine/measure/measureParagraph.js +85 -25
  144. package/dist/layout-engine/measure/measureProvider.js +4 -3
  145. package/dist/layout-engine/measure/tableCellFlow.js +5 -2
  146. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +9 -7
  147. package/dist/layout-engine/measure/textBoxParagraphLayout.js +22 -6
  148. package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +3 -1
  149. package/dist/layout-engine/measure/textMeasurementPolicy.js +7 -1
  150. package/dist/layout-engine/paginator.js +5 -1
  151. package/dist/layout-engine/paragraphSpacing.d.ts +28 -4
  152. package/dist/layout-engine/paragraphSpacing.js +107 -13
  153. package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
  154. package/dist/layout-engine/types.d.ts +48 -15
  155. package/dist/layout-painter/registry/modules/textBox.js +5 -1
  156. package/dist/layout-painter/renderImage.d.ts +12 -1
  157. package/dist/layout-painter/renderImage.js +21 -4
  158. package/dist/layout-painter/renderPage.d.ts +5 -2
  159. package/dist/layout-painter/renderPage.js +59 -14
  160. package/dist/layout-painter/renderParagraph.js +55 -15
  161. package/dist/layout-painter/renderTable.d.ts +5 -1
  162. package/dist/layout-painter/renderTable.js +7 -3
  163. package/dist/layout-painter/renderTextBox.d.ts +2 -1
  164. package/dist/layout-painter/renderTextBox.js +25 -14
  165. package/dist/layout-painter/renderWatermark.d.ts +1 -1
  166. package/dist/layout-painter/renderWatermark.js +2 -1
  167. package/dist/managers/DocumentLoaderManager.js +1 -1
  168. package/dist/markdown/fromMarkdown.js +1 -1
  169. package/dist/model.js +1 -1
  170. package/dist/prosemirror/attrs/index.d.ts +4 -2
  171. package/dist/prosemirror/attrs/index.js +175 -18
  172. package/dist/prosemirror/commands/comments.d.ts +79 -4
  173. package/dist/prosemirror/commands/comments.js +622 -13
  174. package/dist/prosemirror/commands/contentControls.js +1 -1
  175. package/dist/prosemirror/commands/hyperlink.js +15 -1
  176. package/dist/prosemirror/commands/image.js +7 -1
  177. package/dist/prosemirror/commands/index.d.ts +2 -2
  178. package/dist/prosemirror/commands/index.js +2 -2
  179. package/dist/prosemirror/commands/pageBreak.js +1 -1
  180. package/dist/prosemirror/commands/propertyChangeScope.d.ts +2 -2
  181. package/dist/prosemirror/commands/propertyChangeScope.js +3 -2
  182. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +9 -0
  183. package/dist/prosemirror/commands/tableCellMergeResolution.js +181 -0
  184. package/dist/prosemirror/commentIdAllocator.js +13 -3
  185. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -1
  186. package/dist/prosemirror/conversion/fromProseDoc.js +404 -62
  187. package/dist/prosemirror/conversion/index.js +1 -1
  188. package/dist/prosemirror/conversion/sdtAttrs.d.ts +9 -0
  189. package/dist/prosemirror/conversion/sdtAttrs.js +49 -0
  190. package/dist/prosemirror/conversion/toProseDoc.d.ts +2 -1
  191. package/dist/prosemirror/conversion/toProseDoc.js +364 -103
  192. package/dist/prosemirror/extensions/ExtensionManager.js +1 -1
  193. package/dist/prosemirror/extensions/StarterKit.js +6 -1
  194. package/dist/prosemirror/extensions/core/ParagraphExtension.js +6 -2
  195. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -0
  196. package/dist/prosemirror/extensions/features/ImagePasteExtension.js +10 -2
  197. package/dist/prosemirror/extensions/features/ListExtension.js +1 -1
  198. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +10 -1
  199. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +93 -2
  200. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +2 -1
  201. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +6 -2
  202. package/dist/prosemirror/extensions/features/pasteCleanup.js +15 -2
  203. package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +1 -1
  204. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +30 -4
  205. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.d.ts +9 -1
  206. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +70 -11
  207. package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
  208. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -0
  209. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +22 -0
  210. package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
  211. package/dist/prosemirror/extensions/nodes/TableExtension.js +14 -6
  212. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +13 -0
  213. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +41 -0
  214. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +5 -2
  215. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +4 -1
  216. package/dist/prosemirror/index.d.ts +1 -1
  217. package/dist/prosemirror/index.js +1 -1
  218. package/dist/prosemirror/plugins/contentControlWidgets.d.ts +36 -10
  219. package/dist/prosemirror/plugins/contentControlWidgets.js +128 -94
  220. package/dist/prosemirror/plugins/revisionIds.d.ts +23 -0
  221. package/dist/prosemirror/plugins/revisionIds.js +57 -0
  222. package/dist/prosemirror/plugins/selectionTracker.js +1 -0
  223. package/dist/prosemirror/plugins/suggestionMode.js +5 -4
  224. package/dist/prosemirror/runFormattingMarkNames.d.ts +14 -0
  225. package/dist/prosemirror/runFormattingMarkNames.js +40 -0
  226. package/dist/prosemirror/schema/index.d.ts +2 -2
  227. package/dist/prosemirror/schema/marks.d.ts +30 -3
  228. package/dist/prosemirror/schema/marks.js +15 -0
  229. package/dist/prosemirror/schema/nodes.d.ts +111 -6
  230. package/dist/prosemirror/selectionState.js +1 -0
  231. package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
  232. package/dist/prosemirror/tableCellMergeRevision.d.ts +9 -0
  233. package/dist/prosemirror/tableCellMergeRevision.js +22 -0
  234. package/dist/prosemirror/textBoxAnchorAttrs.d.ts +9 -0
  235. package/dist/prosemirror/textBoxAnchorAttrs.js +33 -0
  236. package/dist/prosemirror/utils/extractTrackedChanges.d.ts +1 -1
  237. package/dist/prosemirror/utils/extractTrackedChanges.js +62 -14
  238. package/dist/prosemirror/validation.js +9 -1
  239. package/dist/render-dom/RemoteSelectionOverlay.js +2 -2
  240. package/dist/server.d.ts +7 -3
  241. package/dist/server.js +7 -3
  242. package/dist/utils/clipboard.d.ts +10 -1
  243. package/dist/utils/clipboard.js +33 -6
  244. package/dist/utils/colorResolver.d.ts +8 -1
  245. package/dist/utils/colorResolver.js +35 -5
  246. package/dist/utils/fontFamilyMerge.js +9 -11
  247. package/dist/utils/fontLoader.js +3 -2
  248. package/dist/utils/fontResolver.d.ts +2 -1
  249. package/dist/utils/fontResolver.js +31 -7
  250. package/dist/utils/fontWeights.d.ts +1 -1
  251. package/dist/utils/fontWeights.js +1 -1
  252. package/dist/utils/hexId.d.ts +11 -1
  253. package/dist/utils/hexId.js +11 -1
  254. package/dist/utils/paragraphFormattingMerge.js +1 -0
  255. package/dist/utils/sanitizeImageSrc.d.ts +21 -0
  256. package/dist/utils/sanitizeImageSrc.js +30 -0
  257. package/dist/utils/stripXmlDeclarations.d.ts +11 -0
  258. package/dist/utils/stripXmlDeclarations.js +35 -0
  259. package/dist/version-comparison.d.ts +22 -2
  260. package/dist/version-comparison.js +41 -12
  261. package/dist/watermark/index.d.ts +11 -1
  262. package/dist/watermark/index.js +22 -1
  263. package/package.json +3 -2
@@ -71,7 +71,8 @@ declare const getDefaultPageFontFamily: () => string;
71
71
  * A single footnote item ready for rendering at page bottom.
72
72
  */
73
73
  type FootnoteRenderItem = {
74
- /** Display number (e.g. "1", "2") */displayNumber: string; /** Plain text content */
74
+ /** Stable story id used by editable-note surfaces. */noteId?: number; /** Display number (e.g. "1", "2") */
75
+ displayNumber: string; /** Plain text content */
75
76
  text?: string; /** Pre-measured structured footnote content. */
76
77
  content?: Pick<FootnoteContent, "blocks" | "measures" | "height">;
77
78
  };
@@ -87,7 +88,9 @@ type RenderPageOptions = {
87
88
  pageClassName?: string; /** Show page borders (for debugging) */
88
89
  showBorders?: boolean; /** Background color for pages */
89
90
  backgroundColor?: string; /** Drop shadow on pages */
90
- showShadow?: boolean; /** Header content to render (used for all pages, or pages 2+ when titlePg is set). */
91
+ showShadow?: boolean; /** Show the effective body-content boundary for each page. */
92
+ showMarginGuides?: boolean; /** CSS color used for margin guides. */
93
+ marginGuideColor?: string; /** Header content to render (used for all pages, or pages 2+ when titlePg is set). */
91
94
  headerContent?: HeaderFooterContent; /** Footer content to render (used for all pages, or pages 2+ when titlePg is set). */
92
95
  footerContent?: HeaderFooterContent; /** Header content by part relationship id, used for section-scoped rendering. */
93
96
  headerContentByRId?: ReadonlyMap<string, HeaderFooterContent>; /** Footer content by part relationship id, used for section-scoped rendering. */
@@ -1,6 +1,7 @@
1
1
  import { eighthsToPixels, pointsToPixels } from "../utils/units.js";
2
2
  import { resolveFontFamily } from "../utils/fontResolver.js";
3
3
  import { borderToStyle } from "../utils/formatToStyle.js";
4
+ import { applySanitizedImageSrc } from "../utils/sanitizeImageSrc.js";
4
5
  import { floatingTextBoxReservesBand, floatingTextBoxWrapsText, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun } from "../layout-engine/types.js";
5
6
  import { rectsToFloatingZones } from "../layout-engine/measure/floatingZones.js";
6
7
  import { measureParagraph } from "../layout-engine/measure/measureParagraph.js";
@@ -10,7 +11,7 @@ import { renderFragment } from "./renderFragment.js";
10
11
  import { applyImageVisualAttrs, hasImageVisualAttrs, renderImageFragment } from "./renderImage.js";
11
12
  import { renderParagraphFragment } from "./renderParagraph.js";
12
13
  import { renderTextBoxFragment } from "./renderTextBox.js";
13
- import { renderTableFragment } from "./renderTable.js";
14
+ import { renderNestedTable, renderTableFragment } from "./renderTable.js";
14
15
  import { renderWatermarkLayer } from "./renderWatermark.js";
15
16
  import { panic } from "better-result";
16
17
  //#region src/layout-painter/renderPage.ts
@@ -35,6 +36,7 @@ const PAGE_CLASS_NAMES = {
35
36
  footer: "layout-page-footer"
36
37
  };
37
38
  const HEADER_FOOTER_RULE_CLIP_MARGIN = pointsToPixels(1.5);
39
+ const DEFAULT_MARGIN_GUIDE_COLOR = "#c0c0c0";
38
40
  const getDefaultPageFontFamily = () => resolveFontFamily("Calibri").cssFallback;
39
41
  /**
40
42
  * Apply page styles to an element
@@ -127,6 +129,26 @@ function renderPageBorderOverlay(page, options, doc) {
127
129
  applyPageBorderSide(overlay, pb.right, "Right", options.theme);
128
130
  return overlay;
129
131
  }
132
+ function renderPageMarginGuideOverlay(page, options, doc) {
133
+ if (options.showMarginGuides !== true) return null;
134
+ const overlay = doc.createElement("div");
135
+ overlay.className = "layout-page-margin-guide";
136
+ overlay.style.position = "absolute";
137
+ overlay.style.top = `${page.margins.top}px`;
138
+ overlay.style.right = `${page.margins.right}px`;
139
+ overlay.style.bottom = `${page.margins.bottom}px`;
140
+ overlay.style.left = `${page.margins.left}px`;
141
+ overlay.style.boxSizing = "border-box";
142
+ overlay.style.border = `1px dashed ${options.marginGuideColor ?? DEFAULT_MARGIN_GUIDE_COLOR}`;
143
+ overlay.style.pointerEvents = "none";
144
+ overlay.style.zIndex = "19";
145
+ return overlay;
146
+ }
147
+ function syncPageMarginGuideOverlay(pageEl, page, options, doc) {
148
+ for (const stale of Array.from(pageEl.querySelectorAll(":scope > .layout-page-margin-guide"))) stale.remove();
149
+ const overlay = renderPageMarginGuideOverlay(page, options, doc);
150
+ if (overlay) pageEl.append(overlay);
151
+ }
130
152
  /**
131
153
  * Refresh the page-border overlay on an existing page shell so incremental
132
154
  * rerenders pick up changes to size, margins, page number, or pageBorders
@@ -359,7 +381,7 @@ function renderFloatingImagesLayer(floatingImages, doc, layerMode = "front") {
359
381
  if (floatImg.pmStart !== void 0) container.dataset["pmStart"] = String(floatImg.pmStart);
360
382
  if (floatImg.pmEnd !== void 0) container.dataset["pmEnd"] = String(floatImg.pmEnd);
361
383
  const img = doc.createElement("img");
362
- img.src = floatImg.src;
384
+ applySanitizedImageSrc(img, floatImg.src);
363
385
  img.style.width = `${floatImg.width}px`;
364
386
  img.style.height = `${floatImg.height}px`;
365
387
  img.style.display = "block";
@@ -470,7 +492,10 @@ function renderHeaderFooterContent(content, context, options, layout) {
470
492
  height: measure.height,
471
493
  ...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
472
494
  ...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {}
473
- }, block, measure, hfContext, { document: doc });
495
+ }, block, measure, hfContext, {
496
+ document: doc,
497
+ renderTable: renderNestedTable
498
+ });
474
499
  const textBoxTop = block.position ? resolveHeaderFooterFloatTop({
475
500
  height: measure.height,
476
501
  paragraphY: cursorY,
@@ -485,7 +510,7 @@ function renderHeaderFooterContent(content, context, options, layout) {
485
510
  }
486
511
  for (const floatImg of floatingImages) {
487
512
  const img = doc.createElement("img");
488
- img.src = floatImg.src;
513
+ applySanitizedImageSrc(img, floatImg.src);
489
514
  img.width = floatImg.width;
490
515
  img.height = floatImg.height;
491
516
  if (floatImg.alt) img.alt = floatImg.alt;
@@ -546,6 +571,10 @@ function renderFootnoteArea(footnotes, contentWidth, doc, context) {
546
571
  container.append(separator);
547
572
  for (const fn of footnotes) {
548
573
  const fnEl = doc.createElement("div");
574
+ if (fn.noteId !== void 0) {
575
+ fnEl.dataset["noteKind"] = "footnote";
576
+ fnEl.dataset["noteId"] = String(fn.noteId);
577
+ }
549
578
  fnEl.style.marginBottom = `0px`;
550
579
  fnEl.style.color = "var(--doc-canvas-text, #000)";
551
580
  if (fn.content) {
@@ -642,7 +671,10 @@ function renderFootnoteBlock(block, measure, contentWidth, y, context, doc) {
642
671
  y,
643
672
  width: measure.width,
644
673
  height: measure.height
645
- }, renderBlock, measure, context, { document: doc });
674
+ }, renderBlock, measure, context, {
675
+ document: doc,
676
+ renderTable: renderNestedTable
677
+ });
646
678
  positionFootnoteBlock(element, y, measure.width, measure.height);
647
679
  return element;
648
680
  }
@@ -651,13 +683,7 @@ function renderFootnoteBlock(block, measure, contentWidth, y, context, doc) {
651
683
  function stripFootnotePmAnchors(block) {
652
684
  switch (block.kind) {
653
685
  case "paragraph": return stripFootnoteParagraphPmAnchors(block);
654
- case "table": {
655
- const { pmStart: _pmStart, pmEnd: _pmEnd, ...table } = block;
656
- return {
657
- ...table,
658
- rows: table.rows.map(stripFootnoteTableRowPmAnchors)
659
- };
660
- }
686
+ case "table": return stripFootnoteTablePmAnchors(block);
661
687
  case "image": {
662
688
  const { pmStart: _pmStart, pmEnd: _pmEnd, ...image } = block;
663
689
  return image;
@@ -666,7 +692,7 @@ function stripFootnotePmAnchors(block) {
666
692
  const { pmStart: _pmStart, pmEnd: _pmEnd, ...textBox } = block;
667
693
  return {
668
694
  ...textBox,
669
- content: textBox.content.map(stripFootnoteParagraphPmAnchors)
695
+ content: textBox.content.map((contentBlock) => contentBlock.kind === "table" ? stripFootnoteTablePmAnchors(contentBlock) : stripFootnoteParagraphPmAnchors(contentBlock))
670
696
  };
671
697
  }
672
698
  case "pageBreak":
@@ -678,6 +704,13 @@ function stripFootnotePmAnchors(block) {
678
704
  default: return block;
679
705
  }
680
706
  }
707
+ function stripFootnoteTablePmAnchors(block) {
708
+ const { pmStart: _pmStart, pmEnd: _pmEnd, ...table } = block;
709
+ return {
710
+ ...table,
711
+ rows: table.rows.map(stripFootnoteTableRowPmAnchors)
712
+ };
713
+ }
681
714
  function stripFootnoteTableRowPmAnchors(row) {
682
715
  return {
683
716
  ...row,
@@ -931,7 +964,10 @@ function renderPage(page, context, options = {}) {
931
964
  fragmentEl = renderImageFragment(fragment, blockData.block, blockData.measure, fragmentContext, { document: doc });
932
965
  prevParagraphBorders = void 0;
933
966
  } else if (fragment.kind === "textBox" && blockData?.block.kind === "textBox" && blockData.measure.kind === "textBox") {
934
- fragmentEl = renderTextBoxFragment(fragment, blockData.block, blockData.measure, fragmentContext, { document: doc });
967
+ fragmentEl = renderTextBoxFragment(fragment, blockData.block, blockData.measure, fragmentContext, {
968
+ document: doc,
969
+ renderTable: renderNestedTable
970
+ });
935
971
  prevParagraphBorders = void 0;
936
972
  } else {
937
973
  fragmentEl = renderFragment(fragment, fragmentContext, { document: doc });
@@ -1074,6 +1110,8 @@ function renderPage(page, context, options = {}) {
1074
1110
  moveBehindHeaderFooterElementsToPage(footerEl, pageEl, footerNaturalTop, page.margins.left);
1075
1111
  }
1076
1112
  if (pageBorderEl && options.pageBorders?.zOrder !== "back") pageEl.append(pageBorderEl);
1113
+ const marginGuideEl = renderPageMarginGuideOverlay(page, options, doc);
1114
+ if (marginGuideEl) pageEl.append(marginGuideEl);
1077
1115
  return pageEl;
1078
1116
  }
1079
1117
  function moveBehindHeaderFooterElementsToPage(slotEl, pageEl, contentTop, contentLeft) {
@@ -1265,6 +1303,7 @@ function collectContentFingerprint(block, parts) {
1265
1303
  }
1266
1304
  function runContentKey(run) {
1267
1305
  if (run.kind === "lineBreak") return "lb";
1306
+ if (run.kind === "renderedPageBreak") return "rpb";
1268
1307
  if (run.kind === "image") return `img:${run.src}|${run.width}x${run.height}${run.transform ? `|tr:${run.transform}` : ""}`;
1269
1308
  const parts = [run.kind];
1270
1309
  if (run.kind === "text") {
@@ -1324,6 +1363,7 @@ function computeOptionsHash(options) {
1324
1363
  if (options.titlePg) parts.push("titlePg");
1325
1364
  if (options.theme) parts.push(`thm:${options.theme.name ?? "default"}`);
1326
1365
  if (options.pageBorders) parts.push(`pb:${JSON.stringify(options.pageBorders)}`);
1366
+ if (options.showMarginGuides === true) parts.push(`mg:${options.marginGuideColor ?? DEFAULT_MARGIN_GUIDE_COLOR}`);
1327
1367
  if (options.headerDistance !== void 0) parts.push(`hd:${options.headerDistance}`);
1328
1368
  if (options.footerDistance !== void 0) parts.push(`fd:${options.footerDistance}`);
1329
1369
  if (options.watermark) parts.push(`wm:${JSON.stringify(options.watermark)}`);
@@ -1412,6 +1452,7 @@ function renderPages(pages, container, options = {}) {
1412
1452
  prev.renderFingerprint = null;
1413
1453
  applyPageStyles(prev.element, page.size.w, page.size.h, options);
1414
1454
  syncPageBorderOverlay(prev.element, page, options, options.document ?? document);
1455
+ syncPageMarginGuideOverlay(prev.element, page, options, options.document ?? document);
1415
1456
  prev.element.dataset["pageNumber"] = String(page.number);
1416
1457
  continue;
1417
1458
  }
@@ -1424,6 +1465,7 @@ function renderPages(pages, container, options = {}) {
1424
1465
  populatePageShell(shell, prevDataMap, totalPages, options);
1425
1466
  applyPageStyles(shell, page.size.w, page.size.h, options);
1426
1467
  syncPageBorderOverlay(shell, page, options, options.document ?? document);
1468
+ syncPageMarginGuideOverlay(shell, page, options, options.document ?? document);
1427
1469
  shell.dataset["pageNumber"] = String(page.number);
1428
1470
  continue;
1429
1471
  }
@@ -1433,6 +1475,7 @@ function renderPages(pages, container, options = {}) {
1433
1475
  prev.renderFingerprint = newRenderFp;
1434
1476
  applyPageStyles(shell, page.size.w, page.size.h, options);
1435
1477
  syncPageBorderOverlay(shell, page, options, options.document ?? document);
1478
+ syncPageMarginGuideOverlay(shell, page, options, options.document ?? document);
1436
1479
  shell.dataset["pageNumber"] = String(page.number);
1437
1480
  }
1438
1481
  if (pages.length > prevShells.length) {
@@ -1445,6 +1488,7 @@ function renderPages(pages, container, options = {}) {
1445
1488
  pageEl.dataset["pageIndex"] = String(i);
1446
1489
  applyPageStyles(pageEl, page.size.w, page.size.h, options);
1447
1490
  syncPageBorderOverlay(pageEl, page, options, doc);
1491
+ syncPageMarginGuideOverlay(pageEl, page, options, doc);
1448
1492
  container.append(pageEl);
1449
1493
  prevShells.push({
1450
1494
  element: pageEl,
@@ -1505,6 +1549,7 @@ function renderPages(pages, container, options = {}) {
1505
1549
  pageEl.dataset["pageIndex"] = String(i);
1506
1550
  applyPageStyles(pageEl, page.size.w, page.size.h, options);
1507
1551
  syncPageBorderOverlay(pageEl, page, options, doc);
1552
+ syncPageMarginGuideOverlay(pageEl, page, options, doc);
1508
1553
  }
1509
1554
  container.insertBefore(pageEl, overlayBefore);
1510
1555
  pageShells.push(pageEl);
@@ -1,9 +1,10 @@
1
1
  import { parseXmlDocument } from "../docx/xmlParser.js";
2
2
  import { resolveFontFamily } from "../utils/fontResolver.js";
3
3
  import { detectBaseDirection } from "../utils/baseDirection.js";
4
+ import { applySanitizedImageSrc } from "../utils/sanitizeImageSrc.js";
4
5
  import { AUTHOR_COLORS, getAuthorColorIdx } from "../utils/authorColors.js";
6
+ import { FONT_KERNING_MODE, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode, toPaintedText } from "../layout-engine/measure/textMeasurementPolicy.js";
5
7
  import "../utils/fontWeights.js";
6
- import { FONT_KERNING_MODE, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode } from "../layout-engine/measure/textMeasurementPolicy.js";
7
8
  import "../layout-engine/measure/measureHelpers.js";
8
9
  import { calculateTabWidth } from "../prosemirror/utils/tabCalculator.js";
9
10
  import { inlineImageBoundingBox, parseRotationDegrees, rotatedBoundingBox } from "../utils/rotationBoundingBox.js";
@@ -12,7 +13,7 @@ import { isFloatingImageRun } from "../layout-engine/types.js";
12
13
  import { getListMarkerInlineWidth, getListMarkerVisualOffset } from "../layout-engine/measure/listMarkerWidth.js";
13
14
  import { resolveImageLineAlign } from "./renderUtils.js";
14
15
  import { applySdtDataAttrs } from "./sdtBoundary.js";
15
- import { applyImageVisualAttrs, hasImageVisualAttrs, wrapImageWithCrop } from "./renderImage.js";
16
+ import { applyImageBorder, applyImageVisualAttrs, hasImageCrop, hasImageVisualAttrs, wrapImageWithCrop } from "./renderImage.js";
16
17
  import { ommlToMathml } from "../docx/mathToMathml.js";
17
18
  import { evaluateFieldInstruction } from "../fields/evaluateField.js";
18
19
  import { borderStrokeToCss, resolveParagraphBorderHorizontalOutsets } from "./borderStroke.js";
@@ -75,6 +76,9 @@ function isMathRun(run) {
75
76
  const AUTOMATIC_TEXT_COLOR_VALUES = /* @__PURE__ */ new Set(["auto", "windowtext"]);
76
77
  const DEFAULT_BLACK_TEXT_COLOR_VALUES = /* @__PURE__ */ new Set(["000000", "000"]);
77
78
  const DOCX_SUPERSCRIPT_SCALE = .75;
79
+ const SUGGESTION_COLOR_CSS = "var(--suggestion-color, #6d3bd6)";
80
+ const SUGGESTION_TINT_CSS = "var(--suggestion-bg, color-mix(in oklch, #6d3bd6 12%, transparent))";
81
+ const SUGGESTION_TINT_LAYER_CSS = `linear-gradient(${SUGGESTION_TINT_CSS}, ${SUGGESTION_TINT_CSS})`;
78
82
  function normalizeTextColorValue(color) {
79
83
  return color.trim().toLowerCase().replace(/^#/u, "");
80
84
  }
@@ -113,7 +117,7 @@ function getRaisedRunFontSize(run) {
113
117
  function applyRunStyles(element, run) {
114
118
  if (run.fontFamily) element.style.fontFamily = resolveFontFamily(run.fontFamily).cssFallback;
115
119
  if (run.fontSize) element.style.fontSize = `${fontSizePtToPx(run.fontSize)}px`;
116
- if (run.bold) element.style.fontWeight = "800";
120
+ if (run.bold) element.style.fontWeight = "700";
117
121
  if (run.italic) element.style.fontStyle = "italic";
118
122
  let hasExplicitTextColor = false;
119
123
  const textColor = getRenderableTextColor(run);
@@ -192,13 +196,21 @@ function applyRunStyles(element, run) {
192
196
  if (run.isInsertion) {
193
197
  const authorIdx = getAuthorColorIdx(run.changeAuthor ?? "");
194
198
  const authorColor = AUTHOR_COLORS[authorIdx];
195
- element.style.color = authorColor;
199
+ const strokeColor = run.isSuggestion ? SUGGESTION_COLOR_CSS : authorColor;
200
+ element.style.color = strokeColor;
196
201
  if (!decorations.includes("underline")) decorations.push("underline");
197
- element.style.textDecorationColor = authorColor;
202
+ element.style.textDecorationColor = strokeColor;
198
203
  element.classList.add("docx-insertion");
199
204
  element.dataset["tcAuthorIdx"] = String(authorIdx);
205
+ if (run.isSuggestion) {
206
+ element.classList.add("docx-insertion--suggested");
207
+ element.style.textDecorationStyle = "dotted";
208
+ element.style.backgroundImage = SUGGESTION_TINT_LAYER_CSS;
209
+ element.dataset["provenance"] = "suggested";
210
+ if (run.suggestionId) element.dataset["suggestionId"] = run.suggestionId;
211
+ }
200
212
  const insertionParts = [run.changeAuthor, run.changeDate ? new Date(run.changeDate).toLocaleDateString() : ""].filter(Boolean);
201
- if (insertionParts.length > 0) element.title = `Inserted: ${insertionParts.join(", ")}`;
213
+ if (insertionParts.length > 0) element.title = `${run.isSuggestion ? "Suggested" : "Inserted"}: ${insertionParts.join(", ")}`;
202
214
  if (run.changeAuthor) element.dataset["changeAuthor"] = run.changeAuthor;
203
215
  if (run.changeDate) element.dataset["changeDate"] = run.changeDate;
204
216
  if (run.changeRevisionId !== void 0) element.dataset["revisionId"] = String(run.changeRevisionId);
@@ -206,13 +218,21 @@ function applyRunStyles(element, run) {
206
218
  if (run.isDeletion) {
207
219
  const authorIdx = getAuthorColorIdx(run.changeAuthor ?? "");
208
220
  const authorColor = AUTHOR_COLORS[authorIdx];
209
- element.style.color = authorColor;
221
+ const strokeColor = run.isSuggestion ? SUGGESTION_COLOR_CSS : authorColor;
222
+ element.style.color = strokeColor;
210
223
  if (!decorations.includes("line-through")) decorations.push("line-through");
211
- element.style.textDecorationColor = authorColor;
224
+ element.style.textDecorationColor = strokeColor;
212
225
  element.classList.add("docx-deletion");
213
226
  element.dataset["tcAuthorIdx"] = String(authorIdx);
227
+ if (run.isSuggestion) {
228
+ element.classList.add("docx-deletion--suggested");
229
+ element.style.textDecorationStyle = "dotted";
230
+ element.style.backgroundImage = SUGGESTION_TINT_LAYER_CSS;
231
+ element.dataset["provenance"] = "suggested";
232
+ if (run.suggestionId) element.dataset["suggestionId"] = run.suggestionId;
233
+ }
214
234
  const deletionParts = [run.changeAuthor, run.changeDate ? new Date(run.changeDate).toLocaleDateString() : ""].filter(Boolean);
215
- if (deletionParts.length > 0) element.title = `Deleted: ${deletionParts.join(", ")}`;
235
+ if (deletionParts.length > 0) element.title = `${run.isSuggestion ? "Suggested deletion" : "Deleted"}: ${deletionParts.join(", ")}`;
216
236
  if (run.changeAuthor) element.dataset["changeAuthor"] = run.changeAuthor;
217
237
  if (run.changeDate) element.dataset["changeDate"] = run.changeDate;
218
238
  if (run.changeRevisionId !== void 0) element.dataset["revisionId"] = String(run.changeRevisionId);
@@ -246,12 +266,20 @@ function applyPmPositions(element, pmStart, pmEnd) {
246
266
  function renderTextRun(run, doc) {
247
267
  const span = doc.createElement("span");
248
268
  span.className = `${PARAGRAPH_CLASS_NAMES.run} ${PARAGRAPH_CLASS_NAMES.text}`;
269
+ if (run.footnoteRefId !== void 0) {
270
+ span.dataset["noteKind"] = "footnote";
271
+ span.dataset["noteId"] = String(run.footnoteRefId);
272
+ } else if (run.endnoteRefId !== void 0) {
273
+ span.dataset["noteKind"] = "endnote";
274
+ span.dataset["noteId"] = String(run.endnoteRefId);
275
+ }
249
276
  if (run.templatePreview) {
250
277
  span.classList.add("folio-template-preview-run");
251
278
  if (run.templatePreview === "highlighted") span.classList.add("folio-template-preview-run--highlighted");
252
279
  }
253
280
  applyRunStyles(span, run);
254
281
  applyPmPositions(span, run.pmStart, run.pmEnd);
282
+ const paintedText = toPaintedText(run.text);
255
283
  if (run.hyperlink) {
256
284
  const anchor = doc.createElement("a");
257
285
  anchor.href = run.hyperlink.href;
@@ -260,7 +288,7 @@ function renderTextRun(run, doc) {
260
288
  anchor.rel = "noopener noreferrer";
261
289
  }
262
290
  if (run.hyperlink.tooltip) anchor.title = run.hyperlink.tooltip;
263
- anchor.textContent = run.text;
291
+ anchor.textContent = paintedText;
264
292
  if (!run.hyperlink.noDefaultStyle) {
265
293
  const hyperlinkColor = getHyperlinkTextColor(run, span.style.color);
266
294
  anchor.style.color = hyperlinkColor;
@@ -270,7 +298,7 @@ function renderTextRun(run, doc) {
270
298
  span.style.setProperty("--doc-run-color", hyperlinkColor);
271
299
  }
272
300
  span.append(anchor);
273
- } else span.textContent = run.text;
301
+ } else span.textContent = paintedText;
274
302
  applyWhitespaceUnderline(span, run);
275
303
  return span;
276
304
  }
@@ -360,7 +388,7 @@ function getLeaderChar(leader) {
360
388
  function renderInlineImageRun(run, doc) {
361
389
  const img = doc.createElement("img");
362
390
  img.className = `${PARAGRAPH_CLASS_NAMES.run} ${PARAGRAPH_CLASS_NAMES.image}`;
363
- img.src = run.src;
391
+ applySanitizedImageSrc(img, run.src);
364
392
  img.width = run.width;
365
393
  img.height = run.height;
366
394
  img.style.width = `${run.width}px`;
@@ -370,6 +398,7 @@ function renderInlineImageRun(run, doc) {
370
398
  img.style.transform = run.transform;
371
399
  img.style.transformOrigin = "center center";
372
400
  }
401
+ if (!hasImageCrop(run)) applyImageBorder(img, run);
373
402
  const rotation = parseRotationDegrees(run.transform);
374
403
  if (rotation !== 0) {
375
404
  const bbox = rotatedBoundingBox(run.width, run.height, rotation);
@@ -399,6 +428,7 @@ function renderInlineImageRun(run, doc) {
399
428
  wrapper.style.verticalAlign = "middle";
400
429
  if (run.distTop) wrapper.style.marginTop = `${run.distTop}px`;
401
430
  if (run.distBottom) wrapper.style.marginBottom = `${run.distBottom}px`;
431
+ if (hasImageCrop(run)) applyImageBorder(wrapper, run);
402
432
  applyPmPositions(wrapper, run.pmStart, run.pmEnd);
403
433
  return wrapper;
404
434
  }
@@ -426,7 +456,7 @@ function renderBlockImage(run, doc) {
426
456
  container.style.marginTop = `${run.distTop ?? 6}px`;
427
457
  container.style.marginBottom = `${run.distBottom ?? 6}px`;
428
458
  const img = doc.createElement("img");
429
- img.src = run.src;
459
+ applySanitizedImageSrc(img, run.src);
430
460
  img.width = run.width;
431
461
  img.height = run.height;
432
462
  if (run.alt) img.alt = run.alt;
@@ -434,6 +464,7 @@ function renderBlockImage(run, doc) {
434
464
  img.style.transform = run.transform;
435
465
  img.style.transformOrigin = "center center";
436
466
  }
467
+ if (!hasImageCrop(run)) applyImageBorder(img, run);
437
468
  const rotation = parseRotationDegrees(run.transform);
438
469
  if (rotation !== 0) {
439
470
  const bbox = rotatedBoundingBox(run.width, run.height, rotation);
@@ -455,6 +486,7 @@ function renderBlockImage(run, doc) {
455
486
  widthPx: run.width,
456
487
  heightPx: run.height
457
488
  });
489
+ if (hasImageCrop(run)) applyImageBorder(wrapper, run);
458
490
  applyPmPositions(container, run.pmStart, run.pmEnd);
459
491
  container.append(wrapper);
460
492
  return container;
@@ -642,6 +674,14 @@ function renderRun(run, doc, context) {
642
674
  if (isLineBreakRun(run)) return renderLineBreakRun(run, doc);
643
675
  if (isFieldRun(run) && context) return renderFieldRun(run, doc, context);
644
676
  if (isMathRun(run)) return renderMathRun(run, doc);
677
+ if (run.kind === "renderedPageBreak") {
678
+ const span = doc.createElement("span");
679
+ span.className = PARAGRAPH_CLASS_NAMES.run;
680
+ span.style.display = "none";
681
+ span.dataset["docxRenderedPageBreak"] = "true";
682
+ applyPmPositions(span, run.pmStart, run.pmEnd);
683
+ return span;
684
+ }
645
685
  const span = doc.createElement("span");
646
686
  span.className = PARAGRAPH_CLASS_NAMES.run;
647
687
  return span;
@@ -913,7 +953,7 @@ function createTextMeasurer(doc) {
913
953
  const fontPrefixParts = [];
914
954
  if (style.italic) fontPrefixParts.push("italic");
915
955
  if (style.smallCaps) fontPrefixParts.push("small-caps");
916
- if (style.bold) fontPrefixParts.push("800");
956
+ if (style.bold) fontPrefixParts.push("700");
917
957
  fontPrefixParts.push(`${fontSizePx}px`);
918
958
  const fontPrefix = fontPrefixParts.join(" ");
919
959
  if (style.eastAsiaFontFamily && !style.letterSpacing && hasCjk(text)) {
@@ -1391,7 +1431,7 @@ function renderListMarker(marker, inlineWidth, visualOffset, doc, fontFamily, fo
1391
1431
  span.style.display = "inline-block";
1392
1432
  if (fontFamily) span.style.fontFamily = resolveFontFamily(fontFamily).cssFallback;
1393
1433
  if (fontSize) span.style.fontSize = `${fontSize * 96 / 72}px`;
1394
- if (bold !== void 0) span.style.fontWeight = bold ? "800" : "normal";
1434
+ if (bold !== void 0) span.style.fontWeight = bold ? "700" : "normal";
1395
1435
  span.style.textAlign = "left";
1396
1436
  span.style.textAlignLast = "left";
1397
1437
  span.style.boxSizing = "border-box";
@@ -23,6 +23,10 @@ type RenderTableFragmentOptions = {
23
23
  document?: Document;
24
24
  pageGeometry?: PageGeometry;
25
25
  };
26
+ /**
27
+ * Render a nested table (within a cell)
28
+ */
29
+ declare function renderNestedTable(block: TableBlock, measure: TableMeasure, context: RenderContext, doc: Document): HTMLElement;
26
30
  /**
27
31
  * Render a table fragment to DOM
28
32
  *
@@ -35,4 +39,4 @@ type RenderTableFragmentOptions = {
35
39
  */
36
40
  declare function renderTableFragment(fragment: TableFragment, block: TableBlock, measure: TableMeasure, context: RenderContext, options?: RenderTableFragmentOptions): HTMLElement;
37
41
  //#endregion
38
- export { RenderTableFragmentOptions, TABLE_CLASS_NAMES, renderTableFragment };
42
+ export { RenderTableFragmentOptions, TABLE_CLASS_NAMES, renderNestedTable, renderTableFragment };
@@ -1,4 +1,5 @@
1
1
  import { emuToPixels } from "../utils/units.js";
2
+ import { applySanitizedImageSrc } from "../utils/sanitizeImageSrc.js";
2
3
  import { getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, tableColumnsArePinned } from "../layout-engine/types.js";
3
4
  import { measureParagraph } from "../layout-engine/measure/measureParagraph.js";
4
5
  import { resolveAnchoredImagePosition } from "./anchoredImagePosition.js";
@@ -70,7 +71,7 @@ function renderCellContent({ cell, cellMeasure, context, doc, contentWidthOverri
70
71
  if (img.pmStart !== void 0) imgContainer.dataset["pmStart"] = String(img.pmStart);
71
72
  if (img.pmEnd !== void 0) imgContainer.dataset["pmEnd"] = String(img.pmEnd);
72
73
  const imgEl = doc.createElement("img");
73
- imgEl.src = img.src;
74
+ applySanitizedImageSrc(imgEl, img.src);
74
75
  imgEl.style.width = `${img.width}px`;
75
76
  imgEl.style.height = `${img.height}px`;
76
77
  imgEl.style.display = "block";
@@ -156,7 +157,10 @@ function renderCellContent({ cell, cellMeasure, context, doc, contentWidthOverri
156
157
  }, textBoxBlock, textBoxMeasure, {
157
158
  ...context,
158
159
  insideTableCell: true
159
- }, { document: doc });
160
+ }, {
161
+ document: doc,
162
+ renderTable: renderNestedTable
163
+ });
160
164
  if (isFloatingTextBoxBlock(textBoxBlock)) {
161
165
  floatingTextBoxesLayer ??= createCellFloatingTextBoxesLayer(doc);
162
166
  textBoxEl.style.left = `${resolveCellTextBoxX(textBoxBlock, contentWidth)}px`;
@@ -737,4 +741,4 @@ function renderTableFragment(fragment, block, measure, context, options = {}) {
737
741
  return tableEl;
738
742
  }
739
743
  //#endregion
740
- export { TABLE_CLASS_NAMES, renderTableFragment };
744
+ export { TABLE_CLASS_NAMES, renderNestedTable, renderTableFragment };
@@ -1,4 +1,4 @@
1
- import { TextBoxBlock, TextBoxFragment, TextBoxMeasure } from "../layout-engine/types.js";
1
+ import { TableBlock, TableMeasure, TextBoxBlock, TextBoxFragment, TextBoxMeasure } from "../layout-engine/types.js";
2
2
  import { RenderContext } from "./renderUtils.js";
3
3
 
4
4
  //#region src/layout-painter/renderTextBox.d.ts
@@ -13,6 +13,7 @@ declare const TEXTBOX_CLASS_NAMES: {
13
13
  */
14
14
  type RenderTextBoxFragmentOptions = {
15
15
  document?: Document;
16
+ renderTable?: (block: TableBlock, measure: TableMeasure, context: RenderContext, document: Document) => HTMLElement;
16
17
  };
17
18
  /**
18
19
  * Render a text box fragment to DOM
@@ -1,6 +1,7 @@
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
+ import { layoutTextBoxContent } from "../layout-engine/measure/textBoxParagraphLayout.js";
4
+ import { panic } from "better-result";
4
5
  //#region src/layout-painter/renderTextBox.ts
5
6
  /**
6
7
  * Text Box Renderer
@@ -9,7 +10,7 @@ import { layoutTextBoxParagraphs } from "../layout-engine/measure/textBoxParagra
9
10
  * - Background fill color
10
11
  * - Border/outline
11
12
  * - Internal padding (margins)
12
- * - Paragraph content inside the box (using pre-measured data)
13
+ * - Block content inside the box (using pre-measured data)
13
14
  */
14
15
  /**
15
16
  * CSS class names for text box elements
@@ -39,27 +40,37 @@ function renderTextBoxFragment(fragment, block, measure, context, options = {})
39
40
  if (fragment.pmStart !== void 0) containerEl.dataset["pmStart"] = String(fragment.pmStart);
40
41
  if (fragment.pmEnd !== void 0) containerEl.dataset["pmEnd"] = String(fragment.pmEnd);
41
42
  const innerWidth = fragment.width - margins.left - margins.right;
42
- const paragraphLayout = layoutTextBoxParagraphs(block.content, measure.innerMeasures);
43
+ const contentLayout = layoutTextBoxContent(block.content, measure.innerMeasures);
43
44
  for (let i = 0; i < block.content.length; i++) {
44
- const paraBlock = block.content[i];
45
- const paraMeasure = measure.innerMeasures[i];
46
- const placement = paragraphLayout.placements[i];
47
- if (!paraBlock || !paraMeasure || !placement) continue;
45
+ const contentBlock = block.content[i];
46
+ const contentMeasure = measure.innerMeasures[i];
47
+ const placement = contentLayout.placements[i];
48
+ if (!contentBlock || !contentMeasure || !placement) continue;
49
+ if (contentBlock.kind === "table" && contentMeasure.kind === "table") {
50
+ if (!options.renderTable) panic("renderTextBoxFragment: a nested table renderer is required for table content");
51
+ const tableEl = options.renderTable(contentBlock, contentMeasure, context, doc);
52
+ tableEl.style.marginTop = `${placement.leadingSpacing}px`;
53
+ containerEl.append(tableEl);
54
+ continue;
55
+ }
56
+ if (contentBlock.kind !== "paragraph" || contentMeasure.kind !== "paragraph") continue;
48
57
  const paraFragment = {
49
58
  kind: "paragraph",
50
- blockId: paraBlock.id,
59
+ blockId: contentBlock.id,
51
60
  x: 0,
52
61
  y: 0,
53
62
  width: innerWidth,
54
63
  height: placement.contentHeight,
55
- ...paraBlock.pmStart !== void 0 ? { pmStart: paraBlock.pmStart } : {},
56
- ...paraBlock.pmEnd !== void 0 ? { pmEnd: paraBlock.pmEnd } : {},
64
+ ...contentBlock.pmStart !== void 0 ? { pmStart: contentBlock.pmStart } : {},
65
+ ...contentBlock.pmEnd !== void 0 ? { pmEnd: contentBlock.pmEnd } : {},
57
66
  fromLine: 0,
58
- toLine: paraMeasure.lines.length
67
+ toLine: contentMeasure.lines.length
59
68
  };
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, {
69
+ const previousBlock = block.content[i - 1];
70
+ const nextBlock = block.content[i + 1];
71
+ const prevBorders = previousBlock?.kind === "paragraph" ? previousBlock.attrs?.borders : void 0;
72
+ const nextBorders = nextBlock?.kind === "paragraph" ? nextBlock.attrs?.borders : void 0;
73
+ const paraEl = renderParagraphFragment(paraFragment, contentBlock, contentMeasure, context, {
63
74
  document: doc,
64
75
  ...prevBorders !== void 0 ? { prevBorders } : {},
65
76
  ...nextBorders !== void 0 ? { nextBorders } : {}
@@ -3,7 +3,7 @@ import { Page } from "../layout-engine/types.js";
3
3
 
4
4
  //#region src/layout-painter/renderWatermark.d.ts
5
5
  type RenderWatermarkOptions = {
6
- /** Resolved image src for picture watermarks (data URL or http URL). */imageSrc?: string;
6
+ /** Resolved image src for picture watermarks (`data:` / `blob:` only). */imageSrc?: string;
7
7
  };
8
8
  /**
9
9
  * Build the watermark overlay element for a page. Returns `null` when
@@ -1,4 +1,5 @@
1
1
  import { resolveFontFamily } from "../utils/fontResolver.js";
2
+ import { applySanitizedImageSrc } from "../utils/sanitizeImageSrc.js";
2
3
  //#region src/layout-painter/renderWatermark.ts
3
4
  const WATERMARK_CLASS = "layout-page-watermark";
4
5
  /**
@@ -47,7 +48,7 @@ function renderTextWatermark(watermark, doc) {
47
48
  }
48
49
  function renderPictureWatermark(watermark, imageSrc, doc) {
49
50
  const img = doc.createElement("img");
50
- img.src = imageSrc;
51
+ applySanitizedImageSrc(img, imageSrc);
51
52
  img.alt = "";
52
53
  img.setAttribute("aria-hidden", "true");
53
54
  const scalePct = (watermark.scale ?? 1) * 100;
@@ -1,7 +1,7 @@
1
+ import { resetAuthorColors } from "../utils/authorColors.js";
1
2
  import { loadFontsWithMapping } from "../utils/fontLoader.js";
2
3
  import { parseDocx } from "../docx/parser.js";
3
4
  import { inspectDocxCompatibility } from "../docx/compatibility.js";
4
- import { resetAuthorColors } from "../utils/authorColors.js";
5
5
  import { recordDocumentLoadPhase } from "../layout-engine/layoutInstrumentation.js";
6
6
  //#region src/managers/DocumentLoaderManager.ts
7
7
  /**
@@ -1,5 +1,5 @@
1
- import { createEmptyDocument } from "../utils/createDocument.js";
2
1
  import { sanitizeExternalUrl } from "../utils/urlSecurity.js";
2
+ import { createEmptyDocument } from "../utils/createDocument.js";
3
3
  import { marked } from "marked";
4
4
  //#region src/markdown/fromMarkdown.ts
5
5
  /**
package/dist/model.js CHANGED
@@ -1,4 +1,4 @@
1
- import { deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
2
1
  import { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, PARAGRAPH_ALIGNMENT_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES } from "./types/documentEnumValues.js";
2
+ import { deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
3
3
  import { types_exports } from "./layout-engine/types.js";
4
4
  export { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, types_exports as Layout, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, PARAGRAPH_ALIGNMENT_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isFolioBlockId, isSequentialFolioBlockId };