@stll/folio-core 0.49.0 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/dist/ai-edits/headless.d.ts +37 -1
  2. package/dist/ai-edits/headless.js +47 -16
  3. package/dist/ai-edits/word-diff.js +134 -1
  4. package/dist/compare/content-alignment.js +220 -5
  5. package/dist/compare/content.d.ts +1 -0
  6. package/dist/compare/content.js +51 -20
  7. package/dist/display-list/dom/renderDisplayListToDom.d.ts +10 -1
  8. package/dist/display-list/dom/renderDisplayListToDom.js +8 -7
  9. package/dist/display-list/html/renderDisplayListToHtml.d.ts +21 -0
  10. package/dist/display-list/html/renderDisplayListToHtml.js +126 -0
  11. package/dist/display-list/selectDisplayPages.d.ts +6 -0
  12. package/dist/display-list/selectDisplayPages.js +44 -0
  13. package/dist/docx/selectiveSave.d.ts +1 -1
  14. package/dist/docx/selectiveSave.js +16 -6
  15. package/dist/docx/structuralXmlPatch.d.ts +14 -0
  16. package/dist/docx/structuralXmlPatch.js +209 -0
  17. package/dist/export-pdf.d.ts +35 -2
  18. package/dist/export-pdf.js +51 -20
  19. package/dist/fonts/fontsourceFaces.d.ts +47 -0
  20. package/dist/fonts/fontsourceFaces.js +230 -0
  21. package/dist/layout-bridge/convert/flowBlockPostprocessing.d.ts +8 -0
  22. package/dist/layout-bridge/convert/flowBlockPostprocessing.js +106 -0
  23. package/dist/layout-bridge/convert/flowBorders.d.ts +17 -0
  24. package/dist/layout-bridge/convert/flowBorders.js +31 -0
  25. package/dist/layout-bridge/convert/flowConversionShared.d.ts +111 -0
  26. package/dist/layout-bridge/convert/flowConversionShared.js +23 -0
  27. package/dist/layout-bridge/convert/footnoteLayout.d.ts +2 -1
  28. package/dist/layout-bridge/convert/footnoteLayout.js +2 -1
  29. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +2 -1
  30. package/dist/layout-bridge/convert/imageConversion.d.ts +33 -0
  31. package/dist/layout-bridge/convert/imageConversion.js +118 -0
  32. package/dist/layout-bridge/convert/listMarkers.d.ts +28 -0
  33. package/dist/layout-bridge/convert/listMarkers.js +98 -0
  34. package/dist/layout-bridge/convert/noteReferences.d.ts +14 -0
  35. package/dist/layout-bridge/convert/noteReferences.js +29 -0
  36. package/dist/layout-bridge/convert/pageBreakSplitting.d.ts +16 -0
  37. package/dist/layout-bridge/convert/pageBreakSplitting.js +140 -0
  38. package/dist/layout-bridge/convert/paragraphAttrs.d.ts +20 -0
  39. package/dist/layout-bridge/convert/paragraphAttrs.js +234 -0
  40. package/dist/layout-bridge/convert/paragraphConversion.d.ts +12 -0
  41. package/dist/layout-bridge/convert/paragraphConversion.js +112 -0
  42. package/dist/layout-bridge/convert/paragraphRuns.d.ts +15 -0
  43. package/dist/layout-bridge/convert/paragraphRuns.js +281 -0
  44. package/dist/layout-bridge/convert/runFormattingMerge.d.ts +19 -0
  45. package/dist/layout-bridge/convert/runFormattingMerge.js +167 -0
  46. package/dist/layout-bridge/convert/runMarkFormatting.d.ts +11 -0
  47. package/dist/layout-bridge/convert/runMarkFormatting.js +291 -0
  48. package/dist/layout-bridge/convert/sectionBoundaries.d.ts +35 -0
  49. package/dist/layout-bridge/convert/sectionBoundaries.js +114 -0
  50. package/dist/layout-bridge/convert/tableConversion.d.ts +12 -0
  51. package/dist/layout-bridge/convert/tableConversion.js +303 -0
  52. package/dist/layout-bridge/convert/textBoxFill.d.ts +20 -0
  53. package/dist/layout-bridge/convert/textBoxFill.js +42 -0
  54. package/dist/layout-bridge/convert/textFormattingConversion.d.ts +22 -0
  55. package/dist/layout-bridge/convert/textFormattingConversion.js +120 -0
  56. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +5 -113
  57. package/dist/layout-bridge/convert/toFlowBlocks.js +11 -2082
  58. package/dist/layout-bridge/engine/hitTest.js +1 -1
  59. package/dist/layout-bridge/engine/measuring/index.d.ts +2 -1
  60. package/dist/layout-bridge/engine/measuring/index.js +2 -1
  61. package/dist/layout-bridge/engine/selectionRects.js +1 -1
  62. package/dist/layout-engine/imageLayout.d.ts +9 -0
  63. package/dist/layout-engine/imageLayout.js +48 -0
  64. package/dist/layout-engine/index.d.ts +3 -18
  65. package/dist/layout-engine/index.js +12 -937
  66. package/dist/layout-engine/layoutFlowShared.d.ts +6 -0
  67. package/dist/layout-engine/layoutFlowShared.js +8 -0
  68. package/dist/layout-engine/measure/crossRunWords.d.ts +45 -0
  69. package/dist/layout-engine/measure/crossRunWords.js +172 -0
  70. package/dist/layout-engine/measure/floatingLineClearance.d.ts +24 -0
  71. package/dist/layout-engine/measure/floatingLineClearance.js +44 -0
  72. package/dist/layout-engine/measure/inlineObjectMetrics.d.ts +6 -0
  73. package/dist/layout-engine/measure/inlineObjectMetrics.js +52 -0
  74. package/dist/layout-engine/measure/justification.d.ts +32 -0
  75. package/dist/layout-engine/measure/justification.js +83 -0
  76. package/dist/layout-engine/measure/lineFitting.d.ts +48 -0
  77. package/dist/layout-engine/measure/lineFitting.js +145 -0
  78. package/dist/layout-engine/measure/lineTypography.d.ts +44 -0
  79. package/dist/layout-engine/measure/lineTypography.js +124 -0
  80. package/dist/layout-engine/measure/measureBlocks.js +2 -1
  81. package/dist/layout-engine/measure/measureParagraph.d.ts +1 -20
  82. package/dist/layout-engine/measure/measureParagraph.js +32 -734
  83. package/dist/layout-engine/measure/paragraphMeasureShared.d.ts +109 -0
  84. package/dist/layout-engine/measure/paragraphMeasureShared.js +82 -0
  85. package/dist/layout-engine/measure/paragraphTabs.d.ts +18 -0
  86. package/dist/layout-engine/measure/paragraphTabs.js +78 -0
  87. package/dist/layout-engine/paragraphLayout.d.ts +23 -0
  88. package/dist/layout-engine/paragraphLayout.js +204 -0
  89. package/dist/layout-engine/sectionLayout.d.ts +45 -0
  90. package/dist/layout-engine/sectionLayout.js +216 -0
  91. package/dist/layout-engine/tableLayout.d.ts +17 -0
  92. package/dist/layout-engine/tableLayout.js +369 -0
  93. package/dist/layout-engine/textBoxLayout.d.ts +15 -0
  94. package/dist/layout-engine/textBoxLayout.js +137 -0
  95. package/dist/layout-painter/renderPage.js +1 -0
  96. package/dist/managers/editorShortcuts.d.ts +16 -3
  97. package/dist/managers/editorShortcuts.js +6 -4
  98. package/dist/paged-layout/sectionBlockWidths.d.ts +2 -1
  99. package/dist/paged-layout/sectionBlockWidths.js +1 -1
  100. package/dist/prosemirror/extensions/StarterKit.d.ts +3 -0
  101. package/dist/prosemirror/extensions/StarterKit.js +2 -1
  102. package/dist/prosemirror/extensions/core/HistoryExtension.d.ts +9 -1
  103. package/dist/prosemirror/extensions/core/HistoryExtension.js +4 -3
  104. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +12 -5
  105. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +91 -33
  106. package/dist/server.d.ts +3 -2
  107. package/package.json +2 -2
@@ -0,0 +1,20 @@
1
+ import { document_d_exports } from "../../types/document.js";
2
+ import { ParagraphAttrs } from "../../prosemirror/schema/nodes.js";
3
+ import { ParagraphAttrs as ParagraphAttrs$1 } from "../../layout-engine/types.js";
4
+ import { FontAlternates } from "../../fonts/fontAlternates.js";
5
+ import { ListCounterStreams } from "../../prosemirror/listMarker.js";
6
+ //#region src/layout-bridge/convert/paragraphAttrs.d.ts
7
+ type ConvertParagraphAttrsOptions = {
8
+ theme: document_d_exports.Theme | null | undefined;
9
+ fontAlternates: FontAlternates | undefined;
10
+ listCounterStreams: ListCounterStreams;
11
+ defaultTabStopTwips: number | undefined;
12
+ /**
13
+ * Resolved paragraph-mark run properties, the base of the list marker's
14
+ * typography. Lazy: only paragraphs that paint a marker resolve them.
15
+ */
16
+ paragraphMarkFormatting: () => document_d_exports.TextFormatting | undefined;
17
+ };
18
+ declare function convertParagraphAttrs(pmAttrs: ParagraphAttrs, { theme, fontAlternates, listCounterStreams, defaultTabStopTwips, paragraphMarkFormatting }: ConvertParagraphAttrsOptions): ParagraphAttrs$1;
19
+ //#endregion
20
+ export { convertParagraphAttrs };
@@ -0,0 +1,234 @@
1
+ import { bulletMarkerFontName, convertBulletToUnicode } from "../../docx/bulletMarkers.js";
2
+ import { getFontAlternate } from "../../fonts/fontAlternates.js";
3
+ import { autospacingMatchesBase } from "../../prosemirror/autospacingBase.js";
4
+ import { advanceVisibleListMarker } from "../../prosemirror/listMarker.js";
5
+ import { directionIsRtl, directionToBidi } from "../../prosemirror/paragraphDirection.js";
6
+ import { mergeParagraphTabStops } from "../../utils/paragraphFormattingMerge.js";
7
+ import { AUTO_PARAGRAPH_SPACING_PX } from "../../utils/units.js";
8
+ import { convertBorderSpecToLayout } from "./flowBorders.js";
9
+ import { twipsToPixels } from "./flowConversionShared.js";
10
+ import { applyDeletedListMarkerAttrs, applyMarkerAllCaps, isAddedNumberingChange, isChangedNumberingChange, isRemovedNumberingChange, toListMarkerRevision } from "./listMarkers.js";
11
+ import { listMarkerFormattingFor, resolveWesternThemeFont } from "./textFormattingConversion.js";
12
+ import { paragraphNumberingLevel, paragraphNumberingReferenceId } from "@stll/docx-core/model";
13
+ //#region src/layout-bridge/convert/paragraphAttrs.ts
14
+ /**
15
+ * Converts ProseMirror paragraph attributes to layout ParagraphAttrs:
16
+ * alignment, spacing, indentation, borders, tab stops, and list markers.
17
+ */
18
+ /**
19
+ * Every `ST_Jc` member, and the flow alignment that paints it.
20
+ *
21
+ * `start` and `end` stay unresolved here: they name an edge of the writing
22
+ * direction, not a side of the page, so which of `left` and `right` paints
23
+ * them depends on the paragraph's direction. `numTab` has no flow alignment at
24
+ * all — it aligns the paragraph to the list number's tab stop, which the flow
25
+ * engine does not model — so it falls back to the start edge, which is where a
26
+ * numbered paragraph without one sits.
27
+ */
28
+ const FLOW_ALIGNMENT_BY_PARAGRAPH_ALIGNMENT = {
29
+ start: "start",
30
+ end: "end",
31
+ numTab: "start",
32
+ left: "left",
33
+ center: "center",
34
+ right: "right",
35
+ both: "justify",
36
+ distribute: "justify",
37
+ mediumKashida: "justify",
38
+ highKashida: "justify",
39
+ lowKashida: "justify",
40
+ thaiDistribute: "justify"
41
+ };
42
+ /** Resolve a logical alignment against the direction the paragraph runs in. */
43
+ const resolveFlowAlignment = (alignment, rightToLeft) => {
44
+ const flow = FLOW_ALIGNMENT_BY_PARAGRAPH_ALIGNMENT[alignment];
45
+ switch (flow) {
46
+ case "start": return rightToLeft ? "right" : "left";
47
+ case "end": return rightToLeft ? "left" : "right";
48
+ default: return flow;
49
+ }
50
+ };
51
+ function convertParagraphAttrs(pmAttrs, { theme, fontAlternates, listCounterStreams, defaultTabStopTwips, paragraphMarkFormatting }) {
52
+ const attrs = {};
53
+ if (pmAttrs.alignment) attrs.alignment = resolveFlowAlignment(pmAttrs.alignment, directionIsRtl(pmAttrs.direction));
54
+ if (pmAttrs.outlineLevel !== void 0) attrs.outlineLevel = pmAttrs.outlineLevel;
55
+ const spaceBefore = pmAttrs.spaceBefore;
56
+ const spaceAfter = pmAttrs.spaceAfter;
57
+ const lineSpacing = pmAttrs.lineSpacing;
58
+ if (typeof pmAttrs.snapToGrid === "boolean") attrs.snapToGrid = pmAttrs.snapToGrid;
59
+ const autoBefore = autospacingMatchesBase(pmAttrs._autospacingBase, "before", spaceBefore);
60
+ const autoAfter = autospacingMatchesBase(pmAttrs._autospacingBase, "after", spaceAfter);
61
+ if (autoBefore || autoAfter || typeof spaceBefore === "number" || typeof spaceAfter === "number" || typeof lineSpacing === "number") {
62
+ attrs.spacing = {};
63
+ if (autoBefore) attrs.spacing.before = AUTO_PARAGRAPH_SPACING_PX;
64
+ else if (typeof spaceBefore === "number") attrs.spacing.before = twipsToPixels(spaceBefore);
65
+ if (autoAfter) attrs.spacing.after = AUTO_PARAGRAPH_SPACING_PX;
66
+ else if (typeof spaceAfter === "number") attrs.spacing.after = twipsToPixels(spaceAfter);
67
+ if (autoBefore || autoAfter) attrs.automaticSpacing = {
68
+ ...autoBefore ? { before: true } : {},
69
+ ...autoAfter ? { after: true } : {}
70
+ };
71
+ const pmSpacingExplicit = pmAttrs.spacingExplicit;
72
+ const spacingFromDocDefaults = pmAttrs.spacingFromDocDefaults;
73
+ const spacingFromImplicitDefaultStyle = pmAttrs.spacingFromImplicitDefaultStyle;
74
+ const explicit = {};
75
+ if (autoBefore || pmSpacingExplicit?.before || spacingFromDocDefaults?.before || spacingFromImplicitDefaultStyle?.before) explicit.before = true;
76
+ if (autoAfter || pmSpacingExplicit?.after || spacingFromDocDefaults?.after || spacingFromImplicitDefaultStyle?.after) explicit.after = true;
77
+ if (explicit.before !== void 0 || explicit.after !== void 0) attrs.spacingExplicit = explicit;
78
+ if (typeof lineSpacing === "number") if (pmAttrs.lineSpacingRule === "exact" || pmAttrs.lineSpacingRule === "atLeast") {
79
+ attrs.spacing.line = twipsToPixels(lineSpacing);
80
+ attrs.spacing.lineUnit = "px";
81
+ attrs.spacing.lineRule = pmAttrs.lineSpacingRule;
82
+ } else {
83
+ attrs.spacing.line = lineSpacing / 240;
84
+ attrs.spacing.lineUnit = "multiplier";
85
+ attrs.spacing.lineRule = "auto";
86
+ }
87
+ }
88
+ let indentLeft = typeof pmAttrs.indentLeft === "number" ? pmAttrs.indentLeft : void 0;
89
+ let indentFirstLine = typeof pmAttrs.indentFirstLine === "number" ? pmAttrs.indentFirstLine : void 0;
90
+ let hangingIndent = pmAttrs.hangingIndent;
91
+ if (paragraphNumberingReferenceId(pmAttrs.numPr) !== void 0 && indentLeft === void 0 && indentFirstLine === void 0) {
92
+ indentLeft = ((paragraphNumberingLevel(pmAttrs.numPr) ?? 0) + 1) * 720;
93
+ if (indentFirstLine === void 0) {
94
+ indentFirstLine = -360;
95
+ hangingIndent = true;
96
+ }
97
+ }
98
+ if (indentLeft !== void 0 || typeof pmAttrs.indentRight === "number" || indentFirstLine !== void 0) {
99
+ attrs.indent = {};
100
+ if (indentLeft !== void 0) attrs.indent.left = twipsToPixels(indentLeft);
101
+ if (typeof pmAttrs.indentRight === "number") attrs.indent.right = twipsToPixels(pmAttrs.indentRight);
102
+ if (indentFirstLine !== void 0) if (hangingIndent) attrs.indent.hanging = Math.abs(twipsToPixels(indentFirstLine));
103
+ else attrs.indent.firstLine = twipsToPixels(indentFirstLine);
104
+ }
105
+ if (pmAttrs.styleId) attrs.styleId = pmAttrs.styleId;
106
+ if (pmAttrs.borders) {
107
+ const borders = pmAttrs.borders;
108
+ attrs.borders = {};
109
+ const convertBorder = (border) => border ? convertBorderSpecToLayout(border, theme) : void 0;
110
+ const topBorder = borders.top ? convertBorder(borders.top) : void 0;
111
+ if (topBorder) attrs.borders.top = topBorder;
112
+ const bottomBorder = borders.bottom ? convertBorder(borders.bottom) : void 0;
113
+ if (bottomBorder) attrs.borders.bottom = bottomBorder;
114
+ const leftBorder = borders.left ? convertBorder(borders.left) : void 0;
115
+ if (leftBorder) attrs.borders.left = leftBorder;
116
+ const rightBorder = borders.right ? convertBorder(borders.right) : void 0;
117
+ if (rightBorder) attrs.borders.right = rightBorder;
118
+ const betweenBorder = borders.between ? convertBorder(borders.between) : void 0;
119
+ if (betweenBorder) attrs.borders.between = betweenBorder;
120
+ const barBorder = borders.bar ? convertBorder(borders.bar) : void 0;
121
+ if (barBorder) attrs.borders.bar = barBorder;
122
+ if (!attrs.borders.top && !attrs.borders.bottom && !attrs.borders.left && !attrs.borders.right && !attrs.borders.between && !attrs.borders.bar) delete attrs.borders;
123
+ }
124
+ const shadingRgb = pmAttrs.shading?.fill?.rgb?.toUpperCase();
125
+ if (shadingRgb && shadingRgb !== "FFFFFF" && shadingRgb !== "FFFFFE") attrs.shading = `#${pmAttrs.shading?.fill?.rgb}`;
126
+ const tabs = mergeParagraphTabStops(pmAttrs.listLevelTabs ?? void 0, pmAttrs.tabs ?? void 0);
127
+ if (tabs && tabs.length > 0) {
128
+ const rightToLeft = directionIsRtl(pmAttrs.direction);
129
+ attrs.tabs = tabs.map((tab) => {
130
+ const tabStop = {
131
+ val: resolveTabAlignment(tab.alignment, rightToLeft),
132
+ pos: tab.position
133
+ };
134
+ if (tab.leader) tabStop.leader = tab.leader;
135
+ return tabStop;
136
+ });
137
+ }
138
+ if (pmAttrs.pageBreakBefore) attrs.pageBreakBefore = true;
139
+ if (pmAttrs.kinsoku !== void 0 && pmAttrs.kinsoku !== null) attrs.kinsoku = pmAttrs.kinsoku;
140
+ if (pmAttrs.overflowPunctuation !== void 0 && pmAttrs.overflowPunctuation !== null) attrs.overflowPunctuation = pmAttrs.overflowPunctuation;
141
+ if (pmAttrs.suppressAutoHyphens !== void 0 && pmAttrs.suppressAutoHyphens !== null) attrs.suppressAutoHyphens = pmAttrs.suppressAutoHyphens;
142
+ if (pmAttrs.renderedPageBreakBefore) attrs.renderedPageBreakBefore = true;
143
+ if (pmAttrs.keepNext) attrs.keepNext = true;
144
+ if (pmAttrs.keepLines) attrs.keepLines = true;
145
+ if (pmAttrs.widowControl === false) attrs.widowControl = false;
146
+ if (pmAttrs.contextualSpacing) attrs.contextualSpacing = true;
147
+ if (pmAttrs.runInWithNext) attrs.runInWithNext = true;
148
+ const bidi = directionToBidi(pmAttrs.direction);
149
+ if (bidi !== void 0) attrs.bidi = bidi;
150
+ if (pmAttrs.styleId) attrs.styleId = pmAttrs.styleId;
151
+ const propertyChanges = pmAttrs._propertyChanges ?? [];
152
+ let changedNumberingChange;
153
+ if (pmAttrs.numPr) {
154
+ attrs.numPr = pmAttrs.numPr;
155
+ if (pmAttrs.pPrMark?.kind === "del") attrs.listMarkerRevision = toListMarkerRevision("del", pmAttrs.pPrMark.info);
156
+ else if (pmAttrs.pPrMark?.kind === "ins") attrs.listMarkerRevision = toListMarkerRevision("ins", pmAttrs.pPrMark.info);
157
+ else {
158
+ const numberingAddedChange = propertyChanges.find(isAddedNumberingChange);
159
+ const currentNumPr = pmAttrs.numPr;
160
+ changedNumberingChange = propertyChanges.find((change) => isChangedNumberingChange(currentNumPr, change));
161
+ const numberingInsertionChange = numberingAddedChange ?? changedNumberingChange;
162
+ if (numberingInsertionChange) attrs.listMarkerRevision = toListMarkerRevision("ins", numberingInsertionChange.info);
163
+ }
164
+ }
165
+ const resolvedMarker = applyMarkerAllCaps(advanceVisibleListMarker(pmAttrs, listCounterStreams).marker, pmAttrs.listMarkerAllCaps);
166
+ if (resolvedMarker !== null) attrs.listMarker = resolvedMarker;
167
+ else if (pmAttrs.listMarker) attrs.listMarker = pmAttrs.listIsBullet ? convertBulletToUnicode(pmAttrs.listMarker, bulletMarkerFontName(pmAttrs.listMarkerFormatting)) : pmAttrs.listMarker;
168
+ if (pmAttrs.listIsBullet !== void 0) attrs.listIsBullet = pmAttrs.listIsBullet;
169
+ if (pmAttrs.listMarkerHidden) attrs.listMarkerHidden = true;
170
+ if (attrs.listMarker !== void 0 || pmAttrs.listMarkerFormatting) {
171
+ const listMarkerFormatting = listMarkerFormattingFor(pmAttrs.listMarkerFormatting, paragraphMarkFormatting(), theme, fontAlternates);
172
+ if (listMarkerFormatting) attrs.listMarkerFormatting = listMarkerFormatting;
173
+ }
174
+ if (pmAttrs.listMarkerAlignment) attrs.listMarkerAlignment = pmAttrs.listMarkerAlignment;
175
+ if (pmAttrs.listMarkerSuffix) attrs.listMarkerSuffix = pmAttrs.listMarkerSuffix;
176
+ if (pmAttrs.listMarkerSecondSlotOffsetTwips !== void 0) attrs.listMarkerSecondSlotOffsetTwips = pmAttrs.listMarkerSecondSlotOffsetTwips;
177
+ if (!pmAttrs.numPr) {
178
+ const numberingRemovedChange = propertyChanges.find(isRemovedNumberingChange);
179
+ if (numberingRemovedChange) {
180
+ applyDeletedListMarkerAttrs(attrs, numberingRemovedChange, void 0, theme, fontAlternates, paragraphMarkFormatting);
181
+ if (resolvedMarker !== null) {
182
+ attrs.listMarker = resolvedMarker;
183
+ attrs.listMarkerRevision = toListMarkerRevision("del", numberingRemovedChange.info);
184
+ }
185
+ }
186
+ }
187
+ if (defaultTabStopTwips !== void 0) attrs.defaultTabStopTwips = defaultTabStopTwips;
188
+ const dtf = pmAttrs.defaultTextFormatting;
189
+ if (dtf) {
190
+ if (dtf.fontSize !== void 0) attrs.defaultFontSize = dtf.fontSize / 2;
191
+ if (attrs.listMarker !== void 0 && !attrs.listMarkerHidden && (attrs.bidi === true || dtf.rtl === true || dtf.cs === true) && dtf.fontSizeCs !== void 0 && (dtf.fontSize === void 0 || dtf.fontSizeCs > dtf.fontSize)) attrs.listParagraphMarkFontSize = dtf.fontSizeCs / 2;
192
+ if (dtf.fontFamily) {
193
+ const resolvedFamily = resolveWesternThemeFont(dtf.fontFamily, theme);
194
+ if (resolvedFamily) {
195
+ attrs.defaultFontFamily = resolvedFamily;
196
+ const alternate = getFontAlternate(resolvedFamily, fontAlternates);
197
+ if (alternate) attrs.defaultAlternateFontFamily = alternate;
198
+ }
199
+ }
200
+ }
201
+ return attrs;
202
+ }
203
+ /**
204
+ * Every `ST_TabJc` member, and the layout engine's name for it.
205
+ *
206
+ * The engine's `start` and `end` are its names for the left and right edges of
207
+ * the line, not direction-aware ones, so `w:tab w:val="left"` maps to `start`
208
+ * unconditionally. `ST_TabJc`'s own `start` and `end` *are* direction-aware
209
+ * and stay unresolved here, under names the engine has no member for.
210
+ * `num`, the tab a numbered paragraph's text hangs from, has no engine
211
+ * alignment of its own and falls back to the line's start edge.
212
+ */
213
+ const TAB_ALIGNMENT_BY_TAB_STOP_ALIGNMENT = {
214
+ start: "logicalStart",
215
+ end: "logicalEnd",
216
+ num: "logicalStart",
217
+ left: "start",
218
+ right: "end",
219
+ center: "center",
220
+ decimal: "decimal",
221
+ bar: "bar",
222
+ clear: "clear"
223
+ };
224
+ /** Resolve a logical tab alignment against the direction the paragraph runs in. */
225
+ const resolveTabAlignment = (alignment, rightToLeft) => {
226
+ const mapped = TAB_ALIGNMENT_BY_TAB_STOP_ALIGNMENT[alignment];
227
+ switch (mapped) {
228
+ case "logicalStart": return rightToLeft ? "end" : "start";
229
+ case "logicalEnd": return rightToLeft ? "start" : "end";
230
+ default: return mapped;
231
+ }
232
+ };
233
+ //#endregion
234
+ export { convertParagraphAttrs };
@@ -0,0 +1,12 @@
1
+ import { ParagraphAttrs } from "../../prosemirror/schema/nodes.js";
2
+ import { ParagraphAttrs as ParagraphAttrs$1, ParagraphBlock } from "../../layout-engine/types.js";
3
+ import { FlowConversionOptions } from "./flowConversionShared.js";
4
+ import { PageBreakRunProjection } from "./paragraphRuns.js";
5
+ import { Node } from "prosemirror-model";
6
+ //#region src/layout-bridge/convert/paragraphConversion.d.ts
7
+ declare function hasVisibleParagraphPayload(attrs: ParagraphAttrs$1): boolean;
8
+ /** Whether any `w:pBdr` side draws a rule (`none`/`nil` sides draw nothing). */
9
+ declare function drawsParagraphBorder(borders: ParagraphAttrs["borders"]): boolean;
10
+ declare function convertParagraph(node: Node, startPos: number, options: FlowConversionOptions, pageBreaks?: PageBreakRunProjection[]): ParagraphBlock;
11
+ //#endregion
12
+ export { convertParagraph, drawsParagraphBorder, hasVisibleParagraphPayload };
@@ -0,0 +1,112 @@
1
+ import { getFontAlternate } from "../../fonts/fontAlternates.js";
2
+ import { setParagraphFrame } from "../../layout-engine/paragraphFrame.js";
3
+ import { expectParagraphAttrs } from "../../prosemirror/attrs/index.js";
4
+ import { expectTextBoxAnchorAttrs } from "../../prosemirror/textBoxAnchorAttrs.js";
5
+ import { nextBlockId, twipsToPixels } from "./flowConversionShared.js";
6
+ import { convertParagraphAttrs } from "./paragraphAttrs.js";
7
+ import { resolveParagraphMarkFormatting, resolveParagraphMarkOwnFormatting } from "./paragraphMarkFormatting.js";
8
+ import { paragraphToRuns } from "./paragraphRuns.js";
9
+ import { resolveWesternThemeFont } from "./textFormattingConversion.js";
10
+ import { panic } from "better-result";
11
+ import { statesNoBorder } from "@stll/docx-core/model";
12
+ //#region src/layout-bridge/convert/paragraphConversion.ts
13
+ const DETACHED_WATERMARK_HOST_ATTR = "_detachedWatermarkHost";
14
+ const expectDetachedWatermarkHostAttr = (attrs) => {
15
+ const value = Reflect.get(attrs, DETACHED_WATERMARK_HOST_ATTR);
16
+ if (value === null || value === void 0) return false;
17
+ if (typeof value !== "boolean") panic("Invalid ProseMirror detached watermark host attrs:\nparagraph.attrs._detachedWatermarkHost: Expected a boolean.");
18
+ return value;
19
+ };
20
+ /**
21
+ * Convert a paragraph node to a ParagraphBlock.
22
+ */
23
+ function hasOnlyVisuallyEmptyTextRuns(runs) {
24
+ return runs.length > 0 && runs.every((run) => run.kind === "text" && run.text.replace(/\u00a0/gu, " ").trim().length === 0);
25
+ }
26
+ function hasOnlyHiddenTextRuns(runs) {
27
+ return runs.length > 0 && runs.every((run) => run.kind === "text" && run.hidden === true);
28
+ }
29
+ function hasVisibleParagraphPayload(attrs) {
30
+ return attrs.listMarker !== void 0 && !attrs.listMarkerHidden || attrs.borders?.top !== void 0 || attrs.borders?.bottom !== void 0 || attrs.borders?.left !== void 0 || attrs.borders?.right !== void 0 || attrs.borders?.between !== void 0 || attrs.borders?.bar !== void 0 || attrs.shading !== void 0;
31
+ }
32
+ /** Whether any `w:pBdr` side draws a rule (`none`/`nil` sides draw nothing). */
33
+ function drawsParagraphBorder(borders) {
34
+ if (!borders) return false;
35
+ return [
36
+ borders.top,
37
+ borders.bottom,
38
+ borders.left,
39
+ borders.right,
40
+ borders.between,
41
+ borders.bar
42
+ ].some((border) => border?.style !== void 0 && !statesNoBorder(border.style));
43
+ }
44
+ function convertParagraph(node, startPos, options, pageBreaks) {
45
+ const pmAttrs = expectParagraphAttrs(node);
46
+ const runs = paragraphToRuns(node, startPos, options, pageBreaks);
47
+ const attrs = convertParagraphAttrs(pmAttrs, {
48
+ theme: options.theme,
49
+ fontAlternates: options.fontAlternates,
50
+ listCounterStreams: options.listCounterStreams,
51
+ defaultTabStopTwips: options.defaultTabStopTwips,
52
+ paragraphMarkFormatting: () => resolveParagraphMarkFormatting(pmAttrs, options.styleResolver)
53
+ });
54
+ if (options.numberingTabIgnoresIndent && attrs.listMarker !== void 0) attrs.listNumberingTabIgnoresIndent = true;
55
+ if (options.defaultFont !== void 0) attrs.defaultFontFamily ??= options.defaultFont;
56
+ if (options.defaultSize !== void 0) attrs.defaultFontSize ??= options.defaultSize;
57
+ if (options.lineBreakRules) attrs.lineBreakRules = options.lineBreakRules;
58
+ if (options.justificationCompatibility) attrs.justificationCompatibility = options.justificationCompatibility;
59
+ if (options.automaticHyphenation) attrs.automaticHyphenation = options.automaticHyphenation;
60
+ const defaultTextFormatting = pmAttrs.defaultTextFormatting;
61
+ if (runs.length === 0 || hasOnlyVisuallyEmptyTextRuns(runs)) {
62
+ if (pmAttrs._originalFormatting && Object.entries(pmAttrs._originalFormatting).some(([key, value]) => key !== "runProperties" && value !== void 0 && value !== null)) attrs.hasDirectParagraphFormatting = true;
63
+ const directParagraphMarkFormatting = pmAttrs._originalFormatting?.runProperties;
64
+ if (directParagraphMarkFormatting && Object.values(directParagraphMarkFormatting).some((value) => value !== void 0 && value !== null)) attrs.hasDirectParagraphMarkFormatting = true;
65
+ const paragraphMarkFormatting = resolveParagraphMarkOwnFormatting(directParagraphMarkFormatting, options.styleResolver);
66
+ if (paragraphMarkFormatting?.fontSize !== void 0) attrs.defaultFontSize = paragraphMarkFormatting.fontSize / 2;
67
+ const paragraphMarkFontFamily = paragraphMarkFormatting?.fontFamily ? resolveWesternThemeFont(paragraphMarkFormatting.fontFamily, options.theme) : void 0;
68
+ if (paragraphMarkFontFamily) {
69
+ attrs.defaultFontFamily = paragraphMarkFontFamily;
70
+ const alternate = getFontAlternate(paragraphMarkFontFamily, options.fontAlternates);
71
+ if (alternate) attrs.defaultAlternateFontFamily = alternate;
72
+ }
73
+ }
74
+ const isFullyHiddenParagraph = defaultTextFormatting?.hidden === true && (runs.length === 0 || hasOnlyHiddenTextRuns(runs));
75
+ if (isFullyHiddenParagraph && attrs.listMarker !== void 0) attrs.listMarkerHidden = true;
76
+ if (isFullyHiddenParagraph) attrs.suppressEmptyParagraphHeight = true;
77
+ if (runs.length === 0 && expectDetachedWatermarkHostAttr(node.attrs)) attrs.suppressEmptyParagraphHeight = false;
78
+ if (runs.length === 0 && pmAttrs._pageBreakCarrier === true && !hasVisibleParagraphPayload(attrs)) attrs.suppressEmptyParagraphHeight = true;
79
+ const bookmarkNames = pmAttrs.bookmarks?.map((b) => b.name);
80
+ const block = {
81
+ kind: "paragraph",
82
+ id: nextBlockId(),
83
+ runs,
84
+ attrs,
85
+ ...pmAttrs.paraId ? { paraId: pmAttrs.paraId } : {},
86
+ ...bookmarkNames && bookmarkNames.length > 0 ? { bookmarks: bookmarkNames } : {},
87
+ pmStart: startPos,
88
+ pmEnd: startPos + node.nodeSize
89
+ };
90
+ const frame = pmAttrs._originalFormatting?.frame;
91
+ if (frame !== void 0 && frame.dropCap !== "drop" && frame.dropCap !== "margin") setParagraphFrame(block, {
92
+ ...frame.width !== void 0 ? { width: twipsToPixels(frame.width) } : {},
93
+ ...frame.height !== void 0 ? { height: twipsToPixels(frame.height) } : {},
94
+ ...frame.hSpace !== void 0 ? { hSpace: twipsToPixels(frame.hSpace) } : {},
95
+ ...frame.vSpace !== void 0 ? { vSpace: twipsToPixels(frame.vSpace) } : {},
96
+ ...frame.hAnchor !== void 0 ? { hAnchor: frame.hAnchor } : {},
97
+ ...frame.vAnchor !== void 0 ? { vAnchor: frame.vAnchor } : {},
98
+ ...frame.x !== void 0 ? { x: twipsToPixels(frame.x) } : {},
99
+ ...frame.y !== void 0 ? { y: twipsToPixels(frame.y) } : {},
100
+ ...frame.xAlign !== void 0 ? { xAlign: frame.xAlign } : {},
101
+ ...frame.yAlign !== void 0 ? { yAlign: frame.yAlign } : {},
102
+ ...frame.wrap !== void 0 ? { wrap: frame.wrap } : {}
103
+ });
104
+ node.descendants((child) => {
105
+ if (child.type.name !== "textBoxAnchor") return true;
106
+ options.textBoxAnchorBlockIds.set(expectTextBoxAnchorAttrs(child).anchorId, block.id);
107
+ return false;
108
+ });
109
+ return block;
110
+ }
111
+ //#endregion
112
+ export { convertParagraph, drawsParagraphBorder, hasVisibleParagraphPayload };
@@ -0,0 +1,15 @@
1
+ import { Run, RunFormatting } from "../../layout-engine/types.js";
2
+ import { FlowConversionOptions } from "./flowConversionShared.js";
3
+ import { Node } from "prosemirror-model";
4
+ //#region src/layout-bridge/convert/paragraphRuns.d.ts
5
+ /**
6
+ * Convert a paragraph node to runs.
7
+ */
8
+ type PageBreakRunProjection = {
9
+ pmStart: number;
10
+ pmEnd: number;
11
+ trackedChange?: Pick<RunFormatting, "isInsertion" | "changeAuthor" | "changeDate" | "changeRevisionId" | "isSuggestion" | "suggestionId">;
12
+ };
13
+ declare function paragraphToRuns(node: Node, startPos: number, _options: FlowConversionOptions, pageBreaks?: PageBreakRunProjection[]): Run[];
14
+ //#endregion
15
+ export { PageBreakRunProjection, paragraphToRuns };