@stll/folio-core 0.32.1 → 0.33.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 (222) hide show
  1. package/dist/ai-edits/__fixtures__/paragraphs.d.ts +17 -0
  2. package/dist/ai-edits/__fixtures__/paragraphs.js +33 -0
  3. package/dist/ai-edits/apply.d.ts +49 -3
  4. package/dist/ai-edits/apply.js +692 -75
  5. package/dist/ai-edits/blockRange.d.ts +7 -3
  6. package/dist/ai-edits/blockRange.js +14 -6
  7. package/dist/ai-edits/headless.d.ts +39 -5
  8. package/dist/ai-edits/headless.js +76 -17
  9. package/dist/ai-edits/index.d.ts +6 -6
  10. package/dist/ai-edits/index.js +3 -3
  11. package/dist/ai-edits/read.d.ts +4 -0
  12. package/dist/ai-edits/read.js +30 -6
  13. package/dist/ai-edits/scoped-reading.js +4 -3
  14. package/dist/ai-edits/snapshot.d.ts +42 -2
  15. package/dist/ai-edits/snapshot.js +127 -62
  16. package/dist/ai-edits/table-row-column-mutations.d.ts +24 -1
  17. package/dist/ai-edits/table-row-column-mutations.js +98 -6
  18. package/dist/ai-edits/table-targets.d.ts +12 -3
  19. package/dist/ai-edits/table-targets.js +15 -5
  20. package/dist/ai-edits/types.d.ts +235 -7
  21. package/dist/ai-edits/word-diff.d.ts +54 -11
  22. package/dist/ai-edits/word-diff.js +221 -61
  23. package/dist/compare/__fixtures__/body-sequence.d.ts +33 -0
  24. package/dist/compare/__fixtures__/body-sequence.js +67 -0
  25. package/dist/compare/__fixtures__/nested-table.d.ts +16 -0
  26. package/dist/compare/__fixtures__/nested-table.js +50 -0
  27. package/dist/compare/__fixtures__/numbered-list.d.ts +34 -0
  28. package/dist/compare/__fixtures__/numbered-list.js +85 -0
  29. package/dist/compare/column-alignment.d.ts +27 -0
  30. package/dist/compare/column-alignment.js +162 -0
  31. package/dist/compare/compare.d.ts +107 -0
  32. package/dist/compare/compare.js +406 -0
  33. package/dist/compare/formatting.d.ts +29 -0
  34. package/dist/compare/formatting.js +84 -0
  35. package/dist/compare/plan.d.ts +22 -0
  36. package/dist/compare/plan.js +1059 -0
  37. package/dist/compare/reproducible-package.d.ts +17 -0
  38. package/dist/compare/reproducible-package.js +30 -0
  39. package/dist/compare/scenario.d.ts +77 -0
  40. package/dist/compare/scenario.js +263 -0
  41. package/dist/compare/types.d.ts +282 -0
  42. package/dist/compare/types.js +32 -0
  43. package/dist/compare/verification.d.ts +55 -0
  44. package/dist/compare/verification.js +146 -0
  45. package/dist/compat/eigenpal.d.ts +9 -6
  46. package/dist/compat/eigenpal.js +6 -3
  47. package/dist/controller/layoutPipeline.d.ts +13 -0
  48. package/dist/controller/layoutPipeline.js +15 -1
  49. package/dist/display-list/build/buildContext.d.ts +39 -0
  50. package/dist/display-list/build/buildContext.js +31 -0
  51. package/dist/display-list/build/buildDisplayList.d.ts +79 -0
  52. package/dist/display-list/build/buildDisplayList.js +283 -0
  53. package/dist/display-list/build/colors.d.ts +20 -0
  54. package/dist/display-list/build/colors.js +136 -0
  55. package/dist/display-list/build/floatingImages.d.ts +24 -0
  56. package/dist/display-list/build/floatingImages.js +41 -0
  57. package/dist/display-list/build/fontTable.d.ts +31 -0
  58. package/dist/display-list/build/fontTable.js +97 -0
  59. package/dist/display-list/build/furniture.d.ts +86 -0
  60. package/dist/display-list/build/furniture.js +98 -0
  61. package/dist/display-list/build/glyphs.d.ts +55 -0
  62. package/dist/display-list/build/glyphs.js +103 -0
  63. package/dist/display-list/build/headerFooterPrimitives.d.ts +17 -0
  64. package/dist/display-list/build/headerFooterPrimitives.js +40 -0
  65. package/dist/display-list/build/imagePrimitives.d.ts +35 -0
  66. package/dist/display-list/build/imagePrimitives.js +264 -0
  67. package/dist/display-list/build/pageBorderPrimitives.d.ts +23 -0
  68. package/dist/display-list/build/pageBorderPrimitives.js +111 -0
  69. package/dist/display-list/build/pageFurniture.d.ts +35 -0
  70. package/dist/display-list/build/pageFurniture.js +141 -0
  71. package/dist/display-list/build/paragraphPrimitives.d.ts +19 -0
  72. package/dist/display-list/build/paragraphPrimitives.js +819 -0
  73. package/dist/display-list/build/regions.d.ts +46 -0
  74. package/dist/display-list/build/regions.js +56 -0
  75. package/dist/display-list/build/storyPrimitives.d.ts +29 -0
  76. package/dist/display-list/build/storyPrimitives.js +279 -0
  77. package/dist/display-list/build/strokes.d.ts +19 -0
  78. package/dist/display-list/build/strokes.js +78 -0
  79. package/dist/display-list/build/tablePrimitives.d.ts +38 -0
  80. package/dist/display-list/build/tablePrimitives.js +409 -0
  81. package/dist/display-list/build/textBoxPrimitives.d.ts +14 -0
  82. package/dist/display-list/build/textBoxPrimitives.js +129 -0
  83. package/dist/display-list/build/textDecorations.d.ts +29 -0
  84. package/dist/display-list/build/textDecorations.js +29 -0
  85. package/dist/display-list/build/unsupported.d.ts +57 -0
  86. package/dist/display-list/build/unsupported.js +0 -0
  87. package/dist/display-list/build/watermarkPrimitives.d.ts +15 -0
  88. package/dist/display-list/build/watermarkPrimitives.js +128 -0
  89. package/dist/display-list/dom/renderDisplayListToDom.d.ts +26 -0
  90. package/dist/display-list/dom/renderDisplayListToDom.js +620 -0
  91. package/dist/display-list/editor/displayListPagePainter.d.ts +22 -0
  92. package/dist/display-list/editor/displayListPagePainter.js +63 -0
  93. package/dist/display-list/editor/pageRenderer.d.ts +24 -0
  94. package/dist/display-list/editor/pageRenderer.js +36 -0
  95. package/dist/display-list/primitives.d.ts +95 -0
  96. package/dist/display-list/primitives.js +127 -0
  97. package/dist/display-list/types.d.ts +472 -0
  98. package/dist/display-list/types.js +0 -0
  99. package/dist/document-operations.d.ts +42 -9
  100. package/dist/document-operations.js +171 -16
  101. package/dist/document-stories.d.ts +16 -0
  102. package/dist/document-stories.js +48 -4
  103. package/dist/docx/packageParts.d.ts +35 -0
  104. package/dist/docx/packageParts.js +120 -0
  105. package/dist/docx/paragraphParser.js +22 -22
  106. package/dist/docx/paragraphTraversal.js +1 -1
  107. package/dist/docx/renderedPageBreakNormalization.js +3 -1
  108. package/dist/docx/revisionIdNormalization.d.ts +13 -0
  109. package/dist/docx/revisionIdNormalization.js +103 -0
  110. package/dist/docx/rezip.js +26 -17
  111. package/dist/docx/selectiveSave.js +2 -2
  112. package/dist/docx/serializer/commentSerializer.js +6 -1
  113. package/dist/docx/serializer/paragraphSerializer.js +33 -4
  114. package/dist/docx/serializer/runSerializer.js +11 -10
  115. package/dist/docx/serializer/tableSerializer.js +2 -1
  116. package/dist/docx/server/applyDocxXmlPatchProposal.js +1 -1
  117. package/dist/docx/server/createBilingualDocx.js +3 -2
  118. package/dist/docx/streamingXmlParser.d.ts +13 -1
  119. package/dist/docx/streamingXmlParser.js +49 -11
  120. package/dist/docx/unzip.d.ts +1 -2
  121. package/dist/docx/unzip.js +25 -6
  122. package/dist/docx/xmlParser.d.ts +3 -1
  123. package/dist/docx/xmlParser.js +33 -27
  124. package/dist/export-pdf.d.ts +43 -0
  125. package/dist/export-pdf.js +98 -0
  126. package/dist/fonts/headlessMeasure.d.ts +76 -0
  127. package/dist/fonts/headlessMeasure.js +0 -0
  128. package/dist/fonts/sfnt/parse.d.ts +51 -0
  129. package/dist/fonts/sfnt/parse.js +525 -0
  130. package/dist/fonts/sfnt/subset.d.ts +20 -0
  131. package/dist/fonts/sfnt/subset.js +350 -0
  132. package/dist/fonts/sfnt/tables.d.ts +88 -0
  133. package/dist/fonts/sfnt/tables.js +131 -0
  134. package/dist/fonts/sfnt/woff.d.ts +14 -0
  135. package/dist/fonts/sfnt/woff.js +163 -0
  136. package/dist/generated/text_shaper.js +324 -0
  137. package/dist/generated/text_shaper_bg.wasm +0 -0
  138. package/dist/headless-layout.d.ts +75 -0
  139. package/dist/headless-layout.js +350 -0
  140. package/dist/index.d.ts +9 -6
  141. package/dist/index.js +6 -3
  142. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  143. package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
  144. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  145. package/dist/layout-bridge/convert/headerFooterLayout.js +2 -0
  146. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +3 -1
  147. package/dist/layout-bridge/convert/toFlowBlocks.js +2 -0
  148. package/dist/layout-engine/measure/advanceComposition.d.ts +62 -0
  149. package/dist/layout-engine/measure/advanceComposition.js +148 -0
  150. package/dist/layout-engine/measure/cache.d.ts +2 -0
  151. package/dist/layout-engine/measure/measureContainer.js +13 -0
  152. package/dist/layout-engine/measure/measureParagraph.js +1 -2
  153. package/dist/layout-engine/measure/measureTypes.d.ts +12 -0
  154. package/dist/layout-engine/measure/tableInlinePlacement.d.ts +3 -2
  155. package/dist/layout-engine/measure/tableInlinePlacement.js +14 -1
  156. package/dist/layout-engine/tableIndentCompatibility.d.ts +5 -0
  157. package/dist/layout-engine/tableIndentCompatibility.js +15 -0
  158. package/dist/layout-engine/types.d.ts +8 -0
  159. package/dist/layout-painter/renderPage.d.ts +29 -1
  160. package/dist/layout-painter/renderPage.js +47 -19
  161. package/dist/layout-painter/renderParagraph.d.ts +21 -2
  162. package/dist/layout-painter/renderParagraph.js +1 -1
  163. package/dist/layout-painter/renderTable.js +37 -4
  164. package/dist/managers/editorShortcuts.d.ts +33 -1
  165. package/dist/managers/editorShortcuts.js +20 -1
  166. package/dist/markdown/renderRuns.js +7 -2
  167. package/dist/markdown/renderTable.js +5 -1
  168. package/dist/model.d.ts +3 -3
  169. package/dist/model.js +2 -2
  170. package/dist/pdf/contentStream.d.ts +73 -0
  171. package/dist/pdf/contentStream.js +143 -0
  172. package/dist/pdf/fonts.d.ts +96 -0
  173. package/dist/pdf/fonts.js +593 -0
  174. package/dist/pdf/images.d.ts +28 -0
  175. package/dist/pdf/images.js +439 -0
  176. package/dist/pdf/objects.d.ts +93 -0
  177. package/dist/pdf/objects.js +237 -0
  178. package/dist/pdf/pageSpace.d.ts +43 -0
  179. package/dist/pdf/pageSpace.js +57 -0
  180. package/dist/pdf/paint.d.ts +32 -0
  181. package/dist/pdf/paint.js +442 -0
  182. package/dist/pdf/writePdf.d.ts +56 -0
  183. package/dist/pdf/writePdf.js +345 -0
  184. package/dist/prosemirror/commands/comments.js +91 -6
  185. package/dist/prosemirror/conversion/fromProseDoc.js +41 -7
  186. package/dist/prosemirror/conversion/toProseDoc.js +41 -4
  187. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +15 -2
  188. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +35 -6
  189. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +2 -2
  190. package/dist/prosemirror/extensions/features/pasteCleanup.js +4 -3
  191. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.d.ts +3 -1
  192. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +5 -3
  193. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -1
  194. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +9 -3
  195. package/dist/prosemirror/extensions/nodes/TableExtension.js +56 -39
  196. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +3 -1
  197. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +5 -3
  198. package/dist/prosemirror/plugins/suggestionMode.js +1 -0
  199. package/dist/prosemirror/revisionCarriers.js +19 -2
  200. package/dist/prosemirror/utils/extractTrackedChanges.d.ts +2 -2
  201. package/dist/prosemirror/utils/extractTrackedChanges.js +59 -23
  202. package/dist/prosemirror/validation.js +55 -27
  203. package/dist/prosemirror/zeroWidthAnchors.d.ts +10 -0
  204. package/dist/prosemirror/zeroWidthAnchors.js +23 -0
  205. package/dist/redline.js +12 -79
  206. package/dist/server.d.ts +4 -4
  207. package/dist/server.js +2 -2
  208. package/dist/shaping/placeRun.d.ts +51 -0
  209. package/dist/shaping/placeRun.js +84 -0
  210. package/dist/shaping/shaper.d.ts +92 -0
  211. package/dist/shaping/shaper.js +102 -0
  212. package/dist/types/block-id.d.ts +18 -2
  213. package/dist/types/block-id.js +49 -5
  214. package/dist/types/content.d.ts +2 -2
  215. package/dist/utils/clipboard.js +4 -3
  216. package/dist/utils/fontResolver.js +183 -0
  217. package/dist/utils/htmlComments.d.ts +17 -0
  218. package/dist/utils/htmlComments.js +22 -0
  219. package/dist/utils/scriptSegments.d.ts +12 -1
  220. package/dist/utils/scriptSegments.js +17 -1
  221. package/dist/version-comparison.d.ts +1 -1
  222. package/package.json +7 -3
@@ -0,0 +1,350 @@
1
+ import { parseDocx } from "./docx/parser.js";
2
+ import { buildHeaderFooterFieldValues } from "./fields/resolveFieldValues.js";
3
+ import { extractEmbeddedFonts } from "./fonts/embeddedFonts.js";
4
+ import { buildFontAlternates } from "./fonts/fontAlternates.js";
5
+ import { buildFootnoteContentMap, collectEndnoteRefs, collectFootnoteRefs, computeNoteDisplayNumbers, remapNoteMarkerText } from "./layout-bridge/convert/footnoteLayout.js";
6
+ import { convertHeaderFooterToContent } from "./layout-bridge/convert/headerFooterLayout.js";
7
+ import { toFlowBlocks } from "./layout-bridge/convert/toFlowBlocks.js";
8
+ import { resolveSectionHeaderFooterRefs } from "./layout-engine/headerFooterRefs.js";
9
+ import { layoutDocument } from "./layout-engine/index.js";
10
+ import { measureBlocks } from "./layout-engine/measure/measureBlocks.js";
11
+ import { getMeasureProvider } from "./layout-engine/measure/measureProvider.js";
12
+ import "./layout-engine/types.js";
13
+ import { getMargins, getPageNumbering, getPageSize } from "./paged-layout/sectionGeometry.js";
14
+ import { toProseDoc } from "./prosemirror/conversion/toProseDoc.js";
15
+ import { needsShaping } from "./shaping/placeRun.js";
16
+ import { getShaper } from "./shaping/shaper.js";
17
+ import { toArrayBuffer } from "./utils/docxInput.js";
18
+ import { getDocumentWatermark } from "./watermark/index.js";
19
+ import { Result, TaggedError } from "better-result";
20
+ import { createHash } from "node:crypto";
21
+ //#region src/headless-layout.ts
22
+ /**
23
+ * Lay a `.docx` package out to pages without a browser.
24
+ *
25
+ * The editor reaches `Layout` through `controller/layoutPipeline.ts`, which
26
+ * needs an `EditorState`, a painter and a live DOM. Export, agents and CI need
27
+ * the same pages with none of that, so this module walks the four pure stages
28
+ * directly: parse, project to ProseMirror, flatten to flow blocks, measure and
29
+ * paginate.
30
+ *
31
+ * ## The other stories
32
+ *
33
+ * Headers, footers and footnotes are separate OOXML stories that the editor
34
+ * lays out as their own ProseMirror views, but neither conversion needs a view:
35
+ * `convertHeaderFooterToContent` and `buildFootnoteContentMap` run the same
36
+ * `toFlowBlocks → measureBlocks` chain the body runs. This entry therefore lays
37
+ * them out too and returns them as {@link HeadlessLayoutResult.furniture}, so
38
+ * an export paints the pages the editor paints rather than bare bodies.
39
+ * Endnotes remain unpaginated and say so through
40
+ * {@link HeadlessLayoutResult.unsupported}.
41
+ *
42
+ * What this entry still does not do is push the body down for a header taller
43
+ * than its margin: the editor's margin clearing lives in the controller, and a
44
+ * document whose header overflows its margin will overlap the body here.
45
+ *
46
+ * ## Measurement
47
+ *
48
+ * Layout is only as good as the installed `MeasureProvider`. The caller
49
+ * installs one (`installHeadlessMeasureProvider` for a server, the canvas
50
+ * backend in a browser); this module refuses to guess, because a silently
51
+ * wrong provider produces a plausible layout that is wrong everywhere.
52
+ */
53
+ var HeadlessLayoutError = class extends TaggedError("HeadlessLayoutError") {};
54
+ /**
55
+ * Stories this entry still does not lay out, keyed by story so the map is total
56
+ * over the union. `satisfies Record<…>` is what makes it total in both
57
+ * directions: a story left unpaginated but unlisted is a compile error rather
58
+ * than content that vanishes without a report, and a story that starts being
59
+ * laid out cannot keep a stale entry, because removing it narrows the union.
60
+ *
61
+ * Endnotes are the only one left. They are collected at the end of the
62
+ * document rather than reserved per page, so nothing in `Layout` and nothing in
63
+ * the painter's render options places them yet; laying them out is a
64
+ * pagination feature, not a conversion this entry could call.
65
+ */
66
+ const UNPAGINATED_STORIES = { endnote: {
67
+ story: "endnote",
68
+ detail: "Endnotes are their own story and are not paginated with the body."
69
+ } };
70
+ const hasStory = (document, story) => {
71
+ switch (story) {
72
+ case "endnote": return (document.package.endnotes?.length ?? 0) > 0;
73
+ default: return false;
74
+ }
75
+ };
76
+ const readPaintFeatures = (document) => ({
77
+ pageBorders: (document.package.document.sections ?? []).some((section) => Object.values(section.properties.pageBorders ?? {}).some((border) => border !== void 0)),
78
+ watermark: getDocumentWatermark(document) !== void 0
79
+ });
80
+ const buildFlowOptions = (document, pageContentHeight) => {
81
+ const settings = document.package.settings;
82
+ const options = {
83
+ pageContentHeight,
84
+ fontAlternates: buildFontAlternates(document.package.fontTable)
85
+ };
86
+ const theme = document.package.theme;
87
+ if (theme) options.theme = theme;
88
+ if (settings?.defaultTabStop !== void 0) options.defaultTabStopTwips = settings.defaultTabStop;
89
+ if (settings?.lineBreakRules) options.lineBreakRules = settings.lineBreakRules;
90
+ if (settings?.autoHyphenation === true) options.automaticHyphenation = {
91
+ enabled: true,
92
+ ...settings.doNotHyphenateCaps === void 0 ? {} : { doNotHyphenateCaps: settings.doNotHyphenateCaps },
93
+ ...settings.consecutiveHyphenLimit === void 0 ? {} : { consecutiveLineLimit: settings.consecutiveHyphenLimit },
94
+ ...settings.hyphenationZoneTwips === void 0 ? {} : { hyphenationZoneTwips: settings.hyphenationZoneTwips }
95
+ };
96
+ return options;
97
+ };
98
+ /**
99
+ * `w:mirrorMargins` is parsed into `FolioDocumentSettings` but the shared
100
+ * `Document` model types `settings` as the narrower `DocumentSettings`, so the
101
+ * flag is present at runtime and invisible to the type. The React paged editor
102
+ * narrows the same way; both should stop once the model carries the field.
103
+ */
104
+ const readsMirrorMargins = (settings) => settings !== void 0 && "mirrorMargins" in settings && settings.mirrorMargins === true;
105
+ const buildBlockLookup = (blocks, measures) => new Map(blocks.flatMap((block, index) => {
106
+ const measure = measures.at(index);
107
+ return measure === void 0 ? [] : [[String(block.id), {
108
+ block,
109
+ measure
110
+ }]];
111
+ }));
112
+ const buildStoryOptions = (document, flowOptions) => ({
113
+ ...document.package.styles ? { styles: document.package.styles } : {},
114
+ ...flowOptions.theme === void 0 ? {} : { theme: flowOptions.theme },
115
+ ...flowOptions.fontAlternates === void 0 ? {} : { fontAlternates: flowOptions.fontAlternates },
116
+ ...flowOptions.defaultTabStopTwips === void 0 ? {} : { defaultTabStopTwips: flowOptions.defaultTabStopTwips },
117
+ ...flowOptions.lineBreakRules === void 0 ? {} : { lineBreakRules: flowOptions.lineBreakRules },
118
+ ...flowOptions.automaticHyphenation === void 0 ? {} : { automaticHyphenation: flowOptions.automaticHyphenation }
119
+ });
120
+ const convertStories = ({ parts, contentWidth, metrics, storyOptions, pageCount, now }) => {
121
+ const contentByRId = /* @__PURE__ */ new Map();
122
+ for (const [rId, part] of parts ?? []) {
123
+ const content = convertHeaderFooterToContent(part, contentWidth, metrics, {
124
+ ...storyOptions,
125
+ measureBlocks: (blocks, width) => measureBlocks(blocks, width, void 0, void 0, buildHeaderFooterFieldValues(blocks, pageCount, now)),
126
+ rId
127
+ });
128
+ if (content) contentByRId.set(rId, content);
129
+ }
130
+ return contentByRId;
131
+ };
132
+ /**
133
+ * Watermarks by the header part that carries them. Word puts the shape in a
134
+ * header, so a document with `w:titlePg` or even/odd headers can carry a
135
+ * different watermark per page; the per-rId map is what lets the builder pick
136
+ * the right one instead of painting one header's watermark behind every page.
137
+ */
138
+ const collectWatermarks = (headers) => {
139
+ const byRId = /* @__PURE__ */ new Map();
140
+ for (const [rId, header] of headers ?? []) if (header.watermark) byRId.set(rId, header.watermark);
141
+ return byRId;
142
+ };
143
+ /**
144
+ * A picture watermark's bytes, as a `data:` URL.
145
+ *
146
+ * Resolved through `imageTarget`, the package path the relationship pointed at
147
+ * when the watermark was parsed: relationship ids are scoped per header part
148
+ * and repeat across parts, so the path is the only unambiguous handle. A linked
149
+ * (external) image resolves to nothing, and the builder reports it.
150
+ */
151
+ const resolveWatermarkImageSrc = (document, watermark) => {
152
+ if (watermark?.kind !== "picture" || watermark.imageTargetExternal === true) return;
153
+ const target = watermark.imageTarget;
154
+ return target === void 0 ? void 0 : document.package.media?.get(target)?.dataUrl;
155
+ };
156
+ /**
157
+ * The scope every extracted face's family carries here.
158
+ *
159
+ * `getEmbeddedFontFaces` defaults it to a fresh random id, which would make two
160
+ * layouts of one package differ in a field the caller compares. Deriving it
161
+ * from the package's own bytes keeps two layouts of one package identical
162
+ * while keeping two *different* packages apart: a DOM backend registers each
163
+ * face under this name, so a fixed scope would let two documents on one page
164
+ * define competing faces under one name and leave which one wins to font
165
+ * matching.
166
+ */
167
+ const embeddedFontNonce = (source) => createHash("sha256").update(new Uint8Array(source)).digest("hex").slice(0, NONCE_LENGTH);
168
+ /** Enough of the digest to separate packages without bloating every family. */
169
+ const NONCE_LENGTH = 12;
170
+ /** Whether `fontTable.xml` declares any embedded face at all. */
171
+ const declaresEmbeddedFonts = (document) => (document.package.fontTable?.fonts ?? []).some((font) => font.embedRegular !== void 0 || font.embedBold !== void 0 || font.embedItalic !== void 0 || font.embedBoldItalic !== void 0);
172
+ /**
173
+ * Parse and paginate a package. Fails rather than defaulting when no
174
+ * measurement backend is installed: a layout measured by the wrong provider
175
+ * is wrong in a way no downstream check catches.
176
+ */
177
+ /**
178
+ * Whether any text in the package needs shaping.
179
+ *
180
+ * Every story a page paints, body, headers, footers and notes alike, is built
181
+ * from the same content model, so one walk over the package answers for all of
182
+ * them. The answer decides whether the shaper is fetched at all: a document in
183
+ * Latin, Cyrillic or Greek must not pay for an artifact it has no use for.
184
+ */
185
+ const packageNeedsShaping = (value) => {
186
+ if (Array.isArray(value)) return value.some(packageNeedsShaping);
187
+ if (typeof value !== "object" || value === null) return false;
188
+ const entries = Object.entries(value);
189
+ const text = entries.find(([key]) => key === "text")?.[1];
190
+ if (entries.find(([key]) => key === "type")?.[1] === "text" && typeof text === "string" && needsShaping(text)) return true;
191
+ return entries.some(([, child]) => packageNeedsShaping(child));
192
+ };
193
+ const layoutDocxHeadless = async (input, options = {}) => {
194
+ const providerCheck = Result.try(() => getMeasureProvider().getFontMetrics({ fontSize: 11 }));
195
+ if (providerCheck.isErr()) return Result.err(new HeadlessLayoutError({
196
+ message: "No MeasureProvider is installed. Install one (installHeadlessMeasureProvider on a server, installCanvasMeasureProvider in a browser) before laying out.",
197
+ cause: providerCheck.error
198
+ }));
199
+ const parsed = await Result.tryPromise({
200
+ try: () => parseDocx(input, { preloadFonts: false }),
201
+ catch: (cause) => new HeadlessLayoutError({
202
+ message: "The package could not be parsed.",
203
+ cause
204
+ })
205
+ });
206
+ if (parsed.isErr()) return Result.err(parsed.error);
207
+ const document = parsed.value;
208
+ const projected = Result.try(() => toProseDoc(document, {
209
+ ...document.package.styles ? { styles: document.package.styles } : {},
210
+ ...document.package.theme ? { theme: document.package.theme } : {}
211
+ }));
212
+ if (projected.isErr()) return Result.err(new HeadlessLayoutError({
213
+ message: "The document could not be projected to the editable model.",
214
+ cause: projected.error
215
+ }));
216
+ if (packageNeedsShaping(document.package)) {
217
+ const loaded = await Result.tryPromise({
218
+ try: () => getShaper(),
219
+ catch: (cause) => new HeadlessLayoutError({
220
+ message: "The text shaper could not be loaded, and this document needs one.",
221
+ cause
222
+ })
223
+ });
224
+ if (loaded.isErr()) return Result.err(loaded.error);
225
+ }
226
+ const sections = document.package.document.sections ?? [];
227
+ const firstSection = sections.at(0)?.properties;
228
+ const finalSection = sections.at(-1)?.properties ?? firstSection;
229
+ const pageSize = getPageSize(firstSection);
230
+ const margins = getMargins(firstSection);
231
+ const contentWidth = pageSize.w - margins.left - margins.right;
232
+ const pageContentHeight = pageSize.h - margins.top - margins.bottom;
233
+ const flowOptions = buildFlowOptions(document, pageContentHeight);
234
+ const storyOptions = buildStoryOptions(document, flowOptions);
235
+ const laidOut = Result.try(() => {
236
+ const authored = toFlowBlocks(projected.value, flowOptions);
237
+ const footnotes = document.package.footnotes ?? [];
238
+ const endnotes = document.package.endnotes ?? [];
239
+ const footnoteRefs = collectFootnoteRefs(authored);
240
+ const blocks = remapNoteMarkerText(authored, {
241
+ footnoteNumbers: computeNoteDisplayNumbers(footnotes, footnoteRefs.map((ref) => ref.footnoteId)),
242
+ endnoteNumbers: computeNoteDisplayNumbers(endnotes, collectEndnoteRefs(authored).map((ref) => ref.endnoteId))
243
+ });
244
+ const measures = measureBlocks(blocks, contentWidth);
245
+ const footnoteContentById = footnoteRefs.length === 0 ? void 0 : buildFootnoteContentMap(footnotes, footnoteRefs, contentWidth, {
246
+ ...storyOptions,
247
+ measureBlocks
248
+ });
249
+ const footnoteHeightById = /* @__PURE__ */ new Map();
250
+ for (const [id, content] of footnoteContentById ?? []) footnoteHeightById.set(id, content.height + 0);
251
+ const sectionHeaderFooterRefs = resolveSectionHeaderFooterRefs(document);
252
+ const finalPageSize = getPageSize(finalSection);
253
+ const finalMargins = getMargins(finalSection);
254
+ return {
255
+ layout: layoutDocument(blocks, measures, {
256
+ pageSize,
257
+ margins,
258
+ pageNumbering: getPageNumbering(firstSection),
259
+ finalPageSize,
260
+ finalMargins,
261
+ finalPageNumbering: getPageNumbering(finalSection),
262
+ pageGap: options.pageGap ?? 0,
263
+ titlePage: firstSection?.titlePg === true,
264
+ evenAndOddHeaders: document.package.settings?.evenAndOddHeaders === true,
265
+ mirrorMargins: readsMirrorMargins(document.package.settings),
266
+ ...footnoteHeightById.size === 0 ? {} : { footnoteHeightById },
267
+ ...sectionHeaderFooterRefs === void 0 ? {} : { sectionHeaderFooterRefs }
268
+ }),
269
+ blockLookup: buildBlockLookup(blocks, measures),
270
+ footnoteContentById
271
+ };
272
+ });
273
+ if (laidOut.isErr()) return Result.err(new HeadlessLayoutError({
274
+ message: "The document could not be paginated.",
275
+ cause: laidOut.error
276
+ }));
277
+ const { layout, blockLookup, footnoteContentById } = laidOut.value;
278
+ const stories = Result.try(() => {
279
+ const pageCount = layout.pages.length;
280
+ const now = options.now ?? /* @__PURE__ */ new Date();
281
+ const shared = {
282
+ contentWidth,
283
+ storyOptions,
284
+ pageCount,
285
+ now
286
+ };
287
+ return {
288
+ headerContentByRId: convertStories({
289
+ ...shared,
290
+ parts: document.package.headers,
291
+ metrics: {
292
+ section: "header",
293
+ pageSize,
294
+ margins
295
+ }
296
+ }),
297
+ footerContentByRId: convertStories({
298
+ ...shared,
299
+ parts: document.package.footers,
300
+ metrics: {
301
+ section: "footer",
302
+ pageSize,
303
+ margins
304
+ }
305
+ })
306
+ };
307
+ });
308
+ if (stories.isErr()) return Result.err(new HeadlessLayoutError({
309
+ message: "The header and footer stories could not be laid out.",
310
+ cause: stories.error
311
+ }));
312
+ const embeddedFonts = declaresEmbeddedFonts(document) ? await Result.tryPromise({
313
+ try: async () => {
314
+ const source = await toArrayBuffer(input);
315
+ return extractEmbeddedFonts(source, embeddedFontNonce(source));
316
+ },
317
+ catch: (cause) => new HeadlessLayoutError({
318
+ message: "The package's embedded fonts could not be extracted.",
319
+ cause
320
+ })
321
+ }) : Result.ok([]);
322
+ if (embeddedFonts.isErr()) return Result.err(embeddedFonts.error);
323
+ const watermark = getDocumentWatermark(document);
324
+ const watermarksByHeader = collectWatermarks(document.package.headers);
325
+ const pictureSources = new Set([watermark, ...watermarksByHeader.values()].filter((candidate) => candidate !== void 0).map((candidate) => resolveWatermarkImageSrc(document, candidate)));
326
+ const watermarkImageSrc = pictureSources.size === 1 ? [...pictureSources].at(0) : void 0;
327
+ const pageBorders = firstSection?.pageBorders;
328
+ return Result.ok({
329
+ layout,
330
+ blockLookup,
331
+ document,
332
+ proseDoc: projected.value,
333
+ documentFeatures: readPaintFeatures(document),
334
+ furniture: {
335
+ ...pageBorders === void 0 ? {} : { pageBorders },
336
+ ...document.package.theme === void 0 ? {} : { theme: document.package.theme },
337
+ ...watermark === void 0 ? {} : { watermark },
338
+ watermarkByHeaderRId: watermarksByHeader,
339
+ ...watermarkImageSrc === void 0 ? {} : { watermarkImageSrc },
340
+ headerContentByRId: stories.value.headerContentByRId,
341
+ footerContentByRId: stories.value.footerContentByRId,
342
+ titlePg: firstSection?.titlePg === true,
343
+ ...footnoteContentById === void 0 ? {} : { footnoteContentById }
344
+ },
345
+ embeddedFonts: embeddedFonts.value,
346
+ unsupported: Object.values(UNPAGINATED_STORIES).filter((gap) => hasStory(document, gap.story))
347
+ });
348
+ };
349
+ //#endregion
350
+ export { HeadlessLayoutError, layoutDocxHeadless };
package/dist/index.d.ts CHANGED
@@ -1,15 +1,18 @@
1
- import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAISignatureParty } from "./ai-edits/types.js";
2
- import { applyFolioAIEditOperations } from "./ai-edits/apply.js";
1
+ import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIBlockTableLocation, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditNormalization, FolioAIEditNormalizationCode, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAISignatureParty } from "./ai-edits/types.js";
2
+ import { WORD_DIFF_GRANULARITIES, WordDiffGranularity, WordDiffNormalization, WordDiffOptions, WordDiffSegment, diffWordSegments } from "./ai-edits/word-diff.js";
3
+ import { FolioAIEditApplyOutcome, FolioRevisionStamp, FolioWordDiffOptions, applyFolioAIEditOperations } from "./ai-edits/apply.js";
3
4
  import { document_d_exports } from "./types/document.js";
4
- import { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "./document-operations.js";
5
- import { createFolioAIEditSnapshot, hashFolioAIBlockText, normalizeFolioAIBlockText } from "./ai-edits/snapshot.js";
5
+ import { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationResultBase, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "./document-operations.js";
6
+ import { createFolioAIEditSnapshot, hashFolioAIBlockText, isFolioAIContentBlock, normalizeFolioAIBlockText } from "./ai-edits/snapshot.js";
6
7
  import { DeriveBlockIdInput, FolioBlockId, deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
7
- import { WordDiffSegment, diffWordSegments } from "./ai-edits/word-diff.js";
8
8
  import "./ai-edits/index.js";
9
9
  import { AIBarStatus, AIChatMode, AICitation, AICitationSource, AIGenerateInput, AISuggestion, AISuggestionApplyMode, AISuggestionPreset, AISuggestionSeverity, AISuggestionStatus, DEFAULT_AI_SUGGESTION_PRESETS } from "./ai-suggestions/types.js";
10
10
  import { ApplyResult, applySuggestions } from "./ai-suggestions/apply.js";
11
11
  import { ResolvedAnchor, isSuggestionStale, resolveSuggestionAnchor } from "./ai-suggestions/conflict.js";
12
12
  import { PositionalText, buildPositionalText } from "./ai-suggestions/text-positions.js";
13
+ import { COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, CompareVerification, CompareVerificationCause, CompareVerificationFailure, CompareVerificationInvariant } from "./compare/verification.js";
14
+ import { COMPARE_UNSUPPORTED_REASONS, CompareChange, CompareChangeLocation, CompareDocxApplyError, CompareDocxError, CompareDocxOperationLimitError, CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, CompareFormatRange, CompareResult, CompareUnsupportedPart, CompareUnsupportedReason, InvalidCompareDocxOptionsError } from "./compare/types.js";
15
+ import { MAX_COMPARE_OPERATIONS, compareDocx } from "./compare/compare.js";
13
16
  import { DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DocumentPreset, DocumentStyleSet } from "./style-sets/types.js";
14
17
  import { CreateEmptyDocumentOptions, createEmptyDocument } from "./utils/createDocument.js";
15
18
  import { mergeDocumentContent } from "./utils/mergeDocumentContent.js";
@@ -35,4 +38,4 @@ import { getGoogleFontsEnabled, setEmbeddedFontFamilyMap, setGoogleFontsEnabled
35
38
  import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
36
39
  type Document = document_d_exports.Document;
37
40
  type DocxConformanceClass = document_d_exports.DocxConformanceClass;
38
- export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type BlockRect, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAISignatureParty, type FolioBlockId, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidFolioDocumentOperationBatchError, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
41
+ export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type BlockRect, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, type CompareChange, type CompareChangeLocation, CompareDocxApplyError, type CompareDocxError, CompareDocxOperationLimitError, type CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, type CompareFormatRange, type CompareResult, type CompareUnsupportedPart, type CompareUnsupportedReason, type CompareVerification, type CompareVerificationCause, type CompareVerificationFailure, type CompareVerificationInvariant, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAISignatureParty, type FolioBlockId, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type FolioRevisionStamp, type FolioWordDiffOptions, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidCompareDocxOptionsError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
package/dist/index.js CHANGED
@@ -1,10 +1,13 @@
1
1
  import { applyFolioAIEditOperations } from "./ai-edits/apply.js";
2
- import { createFolioAIEditSnapshot, hashFolioAIBlockText, normalizeFolioAIBlockText } from "./ai-edits/snapshot.js";
3
- import { diffWordSegments } from "./ai-edits/word-diff.js";
2
+ import { createFolioAIEditSnapshot, hashFolioAIBlockText, isFolioAIContentBlock, normalizeFolioAIBlockText } from "./ai-edits/snapshot.js";
3
+ import { WORD_DIFF_GRANULARITIES, diffWordSegments } from "./ai-edits/word-diff.js";
4
4
  import { applySuggestions } from "./ai-suggestions/apply.js";
5
5
  import { isSuggestionStale, resolveSuggestionAnchor } from "./ai-suggestions/conflict.js";
6
6
  import { buildPositionalText } from "./ai-suggestions/text-positions.js";
7
7
  import { DEFAULT_AI_SUGGESTION_PRESETS } from "./ai-suggestions/types.js";
8
+ import { MAX_COMPARE_OPERATIONS, compareDocx } from "./compare/compare.js";
9
+ import { COMPARE_UNSUPPORTED_REASONS, CompareDocxApplyError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, InvalidCompareDocxOptionsError } from "./compare/types.js";
10
+ import { COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS } from "./compare/verification.js";
8
11
  import { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "./document-operations.js";
9
12
  import { inspectDocxCompatibility } from "./docx/compatibility.js";
10
13
  import { createDocx } from "./docx/rezip.js";
@@ -28,4 +31,4 @@ import { createEmptyDocument } from "./utils/createDocument.js";
28
31
  import { getGoogleFontsEnabled, setEmbeddedFontFamilyMap, setGoogleFontsEnabled } from "./utils/fontResolver.js";
29
32
  import { mergeDocumentContent } from "./utils/mergeDocumentContent.js";
30
33
  import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
31
- export { DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, InvalidFolioDocumentOperationBatchError, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
34
+ export { COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, CompareDocxApplyError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, InvalidCompareDocxOptionsError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
@@ -12,6 +12,7 @@ type ConvertFootnoteOptions = {
12
12
  defaultTabStopTwips?: number;
13
13
  lineBreakRules?: ToFlowBlocksOptions["lineBreakRules"];
14
14
  justificationCompatibility?: ToFlowBlocksOptions["justificationCompatibility"];
15
+ tableIndentCompatibility?: ToFlowBlocksOptions["tableIndentCompatibility"];
15
16
  automaticHyphenation?: ToFlowBlocksOptions["automaticHyphenation"];
16
17
  };
17
18
  /**
@@ -200,6 +200,7 @@ function convertFootnoteToContent(footnote, displayNumber, contentWidth, options
200
200
  if (options.defaultTabStopTwips !== void 0) flowOptions.defaultTabStopTwips = options.defaultTabStopTwips;
201
201
  if (options.lineBreakRules) flowOptions.lineBreakRules = options.lineBreakRules;
202
202
  if (options.justificationCompatibility) flowOptions.justificationCompatibility = options.justificationCompatibility;
203
+ if (options.tableIndentCompatibility) flowOptions.tableIndentCompatibility = options.tableIndentCompatibility;
203
204
  if (options.automaticHyphenation) flowOptions.automaticHyphenation = options.automaticHyphenation;
204
205
  const blocks = applyFootnotePresentation(toFlowBlocks(pmDoc, flowOptions), displayNumber);
205
206
  const measures = options.measureBlocks ? options.measureBlocks(blocks, contentWidth) : measureFootnoteBlocks(blocks, contentWidth);
@@ -47,6 +47,7 @@ type ConvertHeaderFooterOptions = {
47
47
  defaultTabStopTwips?: number;
48
48
  lineBreakRules?: ToFlowBlocksOptions["lineBreakRules"];
49
49
  justificationCompatibility?: ToFlowBlocksOptions["justificationCompatibility"];
50
+ tableIndentCompatibility?: ToFlowBlocksOptions["tableIndentCompatibility"];
50
51
  automaticHyphenation?: ToFlowBlocksOptions["automaticHyphenation"];
51
52
  /**
52
53
  * Relationship id of the source HF part. Stamped onto the returned
@@ -337,6 +337,7 @@ function convertHeaderFooterToContent(headerFooter, contentWidth, metrics, optio
337
337
  if (options.defaultTabStopTwips !== void 0) flowOptions.defaultTabStopTwips = options.defaultTabStopTwips;
338
338
  if (options.lineBreakRules) flowOptions.lineBreakRules = options.lineBreakRules;
339
339
  if (options.justificationCompatibility) flowOptions.justificationCompatibility = options.justificationCompatibility;
340
+ if (options.tableIndentCompatibility) flowOptions.tableIndentCompatibility = options.tableIndentCompatibility;
340
341
  if (options.automaticHyphenation) flowOptions.automaticHyphenation = options.automaticHyphenation;
341
342
  return finalizeHeaderFooterContent(toFlowBlocks(pmDoc, flowOptions), contentWidth, metrics, options);
342
343
  }
@@ -359,6 +360,7 @@ function convertHeaderFooterPmDocToContent(pmDoc, contentWidth, metrics, options
359
360
  if (options.defaultTabStopTwips !== void 0) flowOptions.defaultTabStopTwips = options.defaultTabStopTwips;
360
361
  if (options.lineBreakRules) flowOptions.lineBreakRules = options.lineBreakRules;
361
362
  if (options.justificationCompatibility) flowOptions.justificationCompatibility = options.justificationCompatibility;
363
+ if (options.tableIndentCompatibility) flowOptions.tableIndentCompatibility = options.tableIndentCompatibility;
362
364
  if (options.automaticHyphenation) flowOptions.automaticHyphenation = options.automaticHyphenation;
363
365
  return finalizeHeaderFooterContent(toFlowBlocks(pmDoc, flowOptions), contentWidth, metrics, options);
364
366
  }
@@ -1,5 +1,5 @@
1
1
  import { document_d_exports } from "../../types/document.js";
2
- import { BorderStyle, FlowBlock, ParagraphAttrs } from "../../layout-engine/types.js";
2
+ import { BorderStyle, FlowBlock, ParagraphAttrs, TableBlock } from "../../layout-engine/types.js";
3
3
  import { FontAlternates } from "../../fonts/fontAlternates.js";
4
4
  import { formatCounter, resolveListTemplate } from "../../prosemirror/listMarker.js";
5
5
  import { Node } from "prosemirror-model";
@@ -58,6 +58,8 @@ type ToFlowBlocksOptions = {
58
58
  };
59
59
  /** Document-generation policy for justified line fitting. */
60
60
  justificationCompatibility?: NonNullable<ParagraphAttrs["justificationCompatibility"]>;
61
+ /** Document-generation policy for where `w:tblInd` is measured from. */
62
+ tableIndentCompatibility?: NonNullable<TableBlock["indentCompatibility"]>;
61
63
  /** Document-wide automatic hyphenation policy. */
62
64
  automaticHyphenation?: NonNullable<ParagraphAttrs["automaticHyphenation"]>;
63
65
  /** Line pitch for the final body section, whose properties live outside the PM body. */
@@ -1148,6 +1148,7 @@ function convertTable(node, startPos, options) {
1148
1148
  const originalFormatting = attrs._originalFormatting;
1149
1149
  const effectiveIndent = attrs._resolvedIndent ?? originalFormatting?.indent;
1150
1150
  const indentPx = effectiveIndent?.value !== void 0 && effectiveIndent?.type === "dxa" ? twipsToPixels(effectiveIndent.value) : void 0;
1151
+ const dropsAuthoredIndent = effectiveIndent?.value !== void 0 && indentPx === void 0;
1151
1152
  const floating = attrs.floating;
1152
1153
  let floatingPx;
1153
1154
  if (floating) {
@@ -1177,6 +1178,7 @@ function convertTable(node, startPos, options) {
1177
1178
  if (originalFormatting?.layout !== void 0) tableBlock.layout = originalFormatting.layout;
1178
1179
  if (justification) tableBlock.justification = justification;
1179
1180
  if (indentPx !== void 0) tableBlock.indent = indentPx;
1181
+ if (options.tableIndentCompatibility && !dropsAuthoredIndent) tableBlock.indentCompatibility = options.tableIndentCompatibility;
1180
1182
  if (floatingPx) tableBlock.floating = floatingPx;
1181
1183
  if (attrs._resolvedBidi ?? originalFormatting?.bidi) tableBlock.bidi = true;
1182
1184
  return tableBlock;
@@ -0,0 +1,62 @@
1
+ import { FontMetrics, FontStyle, RunMeasurement } from "./measureTypes.js";
2
+ import { ScriptClass } from "../../utils/scriptSegments.js";
3
+ //#region src/layout-engine/measure/advanceComposition.d.ts
4
+ /**
5
+ * Raw advance of `text` in `style`, with no letter spacing, no horizontal
6
+ * scale and no text transform applied: those are this module's job.
7
+ */
8
+ type GlyphAdvanceFn = (text: string, style: FontStyle) => number;
9
+ /**
10
+ * Advances for a whole string, one per code point, from a backend that shapes.
11
+ *
12
+ * Separate from {@link GlyphAdvanceFn} because a shaped run has no per-code-point
13
+ * width to ask for: two letters that ligate have one advance between them, and
14
+ * a letter's width depends on the neighbours it joins to. A backend that can
15
+ * answer for the string as a whole supplies this; one that cannot leaves it out
16
+ * and every code point is measured alone.
17
+ */
18
+ type GlyphAdvancesFn = (text: string, style: FontStyle) => readonly number[];
19
+ declare const applyMeasurementTextTransform: (text: string, style: FontStyle) => string;
20
+ /** Resolve the independent font/style slot a script segment selects. */
21
+ declare const scriptStyle: (style: FontStyle, script: ScriptClass) => FontStyle;
22
+ /**
23
+ * Keep only the properties that change a glyph's advance, so a per-script
24
+ * segment takes the single-font path: letter spacing and horizontal scale are
25
+ * applied once over the whole string, never per segment.
26
+ */
27
+ declare const glyphAdvanceStyle: (style: FontStyle) => FontStyle;
28
+ type ComposeTextWidthOptions = {
29
+ readonly text: string;
30
+ readonly style: FontStyle;
31
+ readonly advanceOf: GlyphAdvanceFn;
32
+ };
33
+ /**
34
+ * Width of a string: the advance width used for line breaking, never the
35
+ * painted (ink) extent, which includes overhang a break decision must ignore.
36
+ */
37
+ declare const composeTextWidth: ({ text, style: source, advanceOf }: ComposeTextWidthOptions) => number;
38
+ type ComposeRunMeasurementOptions = {
39
+ readonly text: string;
40
+ readonly style: FontStyle;
41
+ readonly metrics: FontMetrics;
42
+ readonly advanceOf: GlyphAdvanceFn;
43
+ /**
44
+ * Used for the stretches of text whose glyphs shaping chooses. Absent when
45
+ * the backend has no shaper, which leaves those stretches measured one code
46
+ * point at a time: visibly wrong for the script, and the reason a document
47
+ * that contains one resolves a shaper before it is laid out.
48
+ */
49
+ readonly advancesOf?: GlyphAdvancesFn | undefined;
50
+ };
51
+ /**
52
+ * Per-code-point advances for click positioning.
53
+ *
54
+ * Iterates whole code points so an astral ideograph takes the East-Asian font
55
+ * and gets a real width, while `charWidths` stays one entry per UTF-16 unit
56
+ * (the trailing unit of a surrogate pair carries 0) so it keeps aligning with
57
+ * ProseMirror offsets. The total differs from {@link composeTextWidth} for a
58
+ * kerned or ligature-forming run, because each code point is measured alone.
59
+ */
60
+ declare const composeRunMeasurement: ({ text, style: source, metrics, advanceOf, advancesOf }: ComposeRunMeasurementOptions) => RunMeasurement;
61
+ //#endregion
62
+ export { GlyphAdvanceFn, GlyphAdvancesFn, applyMeasurementTextTransform, composeRunMeasurement, composeTextWidth, glyphAdvanceStyle, scriptStyle };