@stll/folio-core 0.28.0 → 0.28.1
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.
- package/dist/docx/styleParser.js +15 -14
- package/dist/layout-bridge/convert/toFlowBlocks.js +74 -23
- package/dist/layout-engine/footnoteColumnReflow.d.ts +15 -0
- package/dist/layout-engine/footnoteColumnReflow.js +74 -0
- package/dist/layout-engine/index.d.ts +3 -5
- package/dist/layout-engine/index.js +36 -15
- package/dist/layout-engine/measure/cache.d.ts +1 -1
- package/dist/layout-engine/measure/cache.js +11 -2
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +3 -1
- package/dist/layout-engine/measure/listMarkerWidth.js +6 -4
- package/dist/layout-engine/paginator.d.ts +6 -0
- package/dist/layout-engine/paginator.js +30 -11
- package/dist/layout-engine/types.d.ts +11 -6
- package/dist/prosemirror/attrs/index.js +3 -2
- package/dist/prosemirror/commands/formatPainter.js +45 -1
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +4 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +59 -44
- package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -1
- package/dist/prosemirror/conversion/toProseDoc.js +156 -33
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +22 -22
- package/dist/prosemirror/schema/marks.d.ts +3 -1
- package/dist/prosemirror/styles/styleResolver.d.ts +0 -1
- package/dist/prosemirror/styles/styleResolver.js +22 -29
- package/dist/prosemirror/styles/styleToggleCascade.d.ts +37 -0
- package/dist/prosemirror/styles/styleToggleCascade.js +52 -0
- package/dist/utils/textFormattingMerge.d.ts +9 -1
- package/dist/utils/textFormattingMerge.js +32 -1
- package/package.json +1 -1
|
@@ -99,8 +99,8 @@ function createPaginator(options) {
|
|
|
99
99
|
const pageMargins = getPageMargins(pageNumber, logicalNumber);
|
|
100
100
|
const topMargin = pageMargins.top;
|
|
101
101
|
const contentBottom = pageSize.h - pageMargins.bottom;
|
|
102
|
-
const
|
|
103
|
-
const pageContentBottom = contentBottom -
|
|
102
|
+
const footnoteHeightFloor = options.footnoteReservedHeights?.get(pageNumber) ?? 0;
|
|
103
|
+
const pageContentBottom = contentBottom - footnoteHeightFloor;
|
|
104
104
|
const page = {
|
|
105
105
|
number: pageNumber,
|
|
106
106
|
logicalNumber,
|
|
@@ -108,7 +108,7 @@ function createPaginator(options) {
|
|
|
108
108
|
fragments: [],
|
|
109
109
|
margins: pageMargins,
|
|
110
110
|
size: { ...pageSize },
|
|
111
|
-
...
|
|
111
|
+
...footnoteHeightFloor > 0 ? { footnoteReservedHeight: footnoteHeightFloor } : {},
|
|
112
112
|
...columns.count > 1 ? { columns: { ...columns } } : {}
|
|
113
113
|
};
|
|
114
114
|
applySectionMetadata(page);
|
|
@@ -119,7 +119,9 @@ function createPaginator(options) {
|
|
|
119
119
|
topMargin,
|
|
120
120
|
contentBottom: pageContentBottom,
|
|
121
121
|
rawContentBottom: contentBottom,
|
|
122
|
-
footnoteHeight,
|
|
122
|
+
footnoteHeight: footnoteHeightFloor,
|
|
123
|
+
footnoteHeightFloor,
|
|
124
|
+
footnoteDemandHeight: 0,
|
|
123
125
|
trailingSpacing: 0
|
|
124
126
|
};
|
|
125
127
|
pages.push(page);
|
|
@@ -197,7 +199,7 @@ function createPaginator(options) {
|
|
|
197
199
|
const y = state.cursorY + actualSpaceBefore;
|
|
198
200
|
fragment.x = x;
|
|
199
201
|
fragment.y = y;
|
|
200
|
-
state
|
|
202
|
+
commitFragment(state, fragment);
|
|
201
203
|
state.cursorY = y + height;
|
|
202
204
|
state.trailingSpacing = spaceAfter;
|
|
203
205
|
return {
|
|
@@ -206,6 +208,18 @@ function createPaginator(options) {
|
|
|
206
208
|
y
|
|
207
209
|
};
|
|
208
210
|
}
|
|
211
|
+
function commitFragment(state, fragment) {
|
|
212
|
+
if (sectionStartPending && state.page.sectionIndex !== currentSectionIndex) {
|
|
213
|
+
if (currentPageNumbering.type === "restart") nextLogicalPageNumber = currentPageNumbering.start + 1;
|
|
214
|
+
sectionStartPending = false;
|
|
215
|
+
}
|
|
216
|
+
state.page.fragments.push(fragment);
|
|
217
|
+
}
|
|
218
|
+
function addUnflowedFragment(fragment) {
|
|
219
|
+
const state = getCurrentState();
|
|
220
|
+
commitFragment(state, fragment);
|
|
221
|
+
return state;
|
|
222
|
+
}
|
|
209
223
|
/**
|
|
210
224
|
* Reserve additional footnote area on the current page.
|
|
211
225
|
*
|
|
@@ -229,8 +243,9 @@ function createPaginator(options) {
|
|
|
229
243
|
function addFootnoteHeight(additionalHeight, footnoteIds) {
|
|
230
244
|
if (!Number.isFinite(additionalHeight) || additionalHeight <= 0) return;
|
|
231
245
|
const state = getCurrentState();
|
|
232
|
-
const separatorOverhead = state.
|
|
233
|
-
state.
|
|
246
|
+
const separatorOverhead = state.footnoteDemandHeight === 0 ? 12 : 0;
|
|
247
|
+
state.footnoteDemandHeight += additionalHeight + separatorOverhead;
|
|
248
|
+
state.footnoteHeight = Math.max(state.footnoteHeightFloor, state.footnoteDemandHeight);
|
|
234
249
|
state.contentBottom = state.rawContentBottom - state.footnoteHeight;
|
|
235
250
|
state.page.footnoteReservedHeight = state.footnoteHeight;
|
|
236
251
|
if (footnoteIds && footnoteIds.length > 0) {
|
|
@@ -261,10 +276,10 @@ function createPaginator(options) {
|
|
|
261
276
|
const pageMargins = getPageMargins(current.page.number, current.page.logicalNumber);
|
|
262
277
|
const topMargin = pageMargins.top;
|
|
263
278
|
const rawContentBottom = pageSize.h - pageMargins.bottom;
|
|
264
|
-
const
|
|
279
|
+
const footnoteHeightFloor = options.footnoteReservedHeights?.get(current.page.number) ?? 0;
|
|
265
280
|
current.page.size = { ...pageSize };
|
|
266
281
|
current.page.margins = pageMargins;
|
|
267
|
-
if (
|
|
282
|
+
if (footnoteHeightFloor > 0) current.page.footnoteReservedHeight = footnoteHeightFloor;
|
|
268
283
|
else delete current.page.footnoteReservedHeight;
|
|
269
284
|
if (columns.count > 1) current.page.columns = { ...columns };
|
|
270
285
|
else delete current.page.columns;
|
|
@@ -272,8 +287,10 @@ function createPaginator(options) {
|
|
|
272
287
|
current.cursorY = topMargin;
|
|
273
288
|
current.columnIndex = 0;
|
|
274
289
|
current.rawContentBottom = rawContentBottom;
|
|
275
|
-
current.footnoteHeight =
|
|
276
|
-
current.
|
|
290
|
+
current.footnoteHeight = footnoteHeightFloor;
|
|
291
|
+
current.footnoteHeightFloor = footnoteHeightFloor;
|
|
292
|
+
current.footnoteDemandHeight = 0;
|
|
293
|
+
current.contentBottom = rawContentBottom - footnoteHeightFloor;
|
|
277
294
|
current.trailingSpacing = 0;
|
|
278
295
|
columnRegionTop = topMargin;
|
|
279
296
|
return true;
|
|
@@ -370,6 +387,8 @@ function createPaginator(options) {
|
|
|
370
387
|
ensureFits,
|
|
371
388
|
/** Add a fragment to current page. */
|
|
372
389
|
addFragment,
|
|
390
|
+
/** Add an already-positioned fragment without advancing normal flow. */
|
|
391
|
+
addUnflowedFragment,
|
|
373
392
|
/** Reserve additional footnote area on the current page. */
|
|
374
393
|
addFootnoteHeight,
|
|
375
394
|
/** Force a page break. */
|
|
@@ -1059,6 +1059,8 @@ type TextBoxFragment = FragmentBase & {
|
|
|
1059
1059
|
kind: "textBox";
|
|
1060
1060
|
/** Height of the text box. */
|
|
1061
1061
|
height: number;
|
|
1062
|
+
/** True if this text box is positioned outside normal body flow. */
|
|
1063
|
+
isPositioned?: true;
|
|
1062
1064
|
};
|
|
1063
1065
|
/**
|
|
1064
1066
|
* Union of all fragment types.
|
|
@@ -1226,18 +1228,21 @@ type LayoutOptions = {
|
|
|
1226
1228
|
evenAndOddHeaders?: boolean;
|
|
1227
1229
|
/** Swap left/right margins on even physical pages. */
|
|
1228
1230
|
mirrorMargins?: boolean;
|
|
1229
|
-
/**
|
|
1231
|
+
/**
|
|
1232
|
+
* Per-page footnote reserved heights (pageNumber → height in pixels).
|
|
1233
|
+
* When `footnoteHeightById` is also supplied, a value is a retry floor only
|
|
1234
|
+
* while that page has assigned footnote IDs; without the dynamic height map,
|
|
1235
|
+
* the value remains a static reservation.
|
|
1236
|
+
*/
|
|
1230
1237
|
footnoteReservedHeights?: Map<number, number>;
|
|
1231
1238
|
/**
|
|
1232
1239
|
* Footnote content heights keyed by internal footnote id (the OOXML
|
|
1233
1240
|
* `<w:footnoteReference w:id>`). When provided, the layout engine
|
|
1234
1241
|
* tracks footnote demand per body line: each line carrying a fn ref
|
|
1235
1242
|
* grows its page's reservation by that fn's height before the next
|
|
1236
|
-
* line is fitted.
|
|
1237
|
-
* reservation
|
|
1238
|
-
*
|
|
1239
|
-
* above the fn area) on documents with multiple long footnotes per
|
|
1240
|
-
* page.
|
|
1243
|
+
* line is fitted. Multi-column pages may retry with the observed
|
|
1244
|
+
* reservation as a shared floor when a later column invalidates
|
|
1245
|
+
* earlier-column placement.
|
|
1241
1246
|
*/
|
|
1242
1247
|
footnoteHeightById?: Map<number, number>;
|
|
1243
1248
|
/** Header/footer references for each document section, by section index. */
|
|
@@ -77,11 +77,10 @@ const TEXT_BOX_TRACKED_CHANGE_TYPES = [
|
|
|
77
77
|
"moveTo"
|
|
78
78
|
];
|
|
79
79
|
const HARD_BREAK_TYPES = ["column"];
|
|
80
|
-
const
|
|
80
|
+
const RUN_FORMATTING_OVERRIDE_BOOLEAN_KEYS = [
|
|
81
81
|
"bold",
|
|
82
82
|
"italic",
|
|
83
83
|
"strike",
|
|
84
|
-
"doubleStrike",
|
|
85
84
|
"allCaps",
|
|
86
85
|
"smallCaps",
|
|
87
86
|
"hidden",
|
|
@@ -90,6 +89,7 @@ const RUN_FORMATTING_OVERRIDE_FALSE_KEYS = [
|
|
|
90
89
|
"shadow",
|
|
91
90
|
"outline"
|
|
92
91
|
];
|
|
92
|
+
const RUN_FORMATTING_OVERRIDE_FALSE_KEYS = ["doubleStrike", "rtl"];
|
|
93
93
|
const SECTION_ORIENTATIONS = ["portrait", "landscape"];
|
|
94
94
|
const SECTION_START_TYPES = [
|
|
95
95
|
"continuous",
|
|
@@ -659,6 +659,7 @@ const readRunFormattingOverrideMarkAttrs = (mark) => {
|
|
|
659
659
|
const attrs = attrsRecord(mark.attrs);
|
|
660
660
|
const issues = [];
|
|
661
661
|
expectMarkType(mark, "runFormattingOverride", issues);
|
|
662
|
+
for (const key of RUN_FORMATTING_OVERRIDE_BOOLEAN_KEYS) optionalBoolean(attrs, key, `runFormattingOverride.attrs.${key}`, issues);
|
|
662
663
|
for (const key of RUN_FORMATTING_OVERRIDE_FALSE_KEYS) optionalFalse(attrs, key, `runFormattingOverride.attrs.${key}`, issues);
|
|
663
664
|
optionalBoolean(attrs, "boldCs", "runFormattingOverride.attrs.boldCs", issues);
|
|
664
665
|
optionalBoolean(attrs, "italicCs", "runFormattingOverride.attrs.italicCs", issues);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { expectCharacterStyleMarkAttrs, expectParagraphAttrs } from "../attrs/index.js";
|
|
1
2
|
//#region src/prosemirror/commands/formatPainter.ts
|
|
2
3
|
/**
|
|
3
4
|
* Character-formatting marks the painter copies. Structural marks — comments,
|
|
@@ -64,6 +65,40 @@ function sanitizeOverrideMark(mark) {
|
|
|
64
65
|
if (Object.keys(kept).length === 0) return null;
|
|
65
66
|
return mark.type.create(kept);
|
|
66
67
|
}
|
|
68
|
+
const PAINTABLE_STYLE_TOGGLE_MARKS = [
|
|
69
|
+
["allCaps", "allCaps"],
|
|
70
|
+
["bold", "bold"],
|
|
71
|
+
["emboss", "emboss"],
|
|
72
|
+
["imprint", "imprint"],
|
|
73
|
+
["italic", "italic"],
|
|
74
|
+
["outline", "textOutline"],
|
|
75
|
+
["shadow", "textShadow"],
|
|
76
|
+
["smallCaps", "smallCaps"],
|
|
77
|
+
["strike", "strike"]
|
|
78
|
+
];
|
|
79
|
+
const PAINTABLE_COMPLEX_SCRIPT_TOGGLES = ["boldCs", "italicCs"];
|
|
80
|
+
function materializeStyleToggleNegatives({ inheritedFormatting, marks }) {
|
|
81
|
+
const characterStyle = marks.find((mark) => mark.type.name === "characterStyle");
|
|
82
|
+
if (!characterStyle) return [...marks];
|
|
83
|
+
const styleRPr = expectCharacterStyleMarkAttrs(characterStyle)._styleRPr;
|
|
84
|
+
if (!styleRPr) return [...marks];
|
|
85
|
+
const negativeAttrs = {};
|
|
86
|
+
for (const [key, markName] of PAINTABLE_STYLE_TOGGLE_MARKS) if (styleRPr[key] === true && !marks.some((mark) => mark.type.name === markName)) negativeAttrs[key] = false;
|
|
87
|
+
for (const key of PAINTABLE_COMPLEX_SCRIPT_TOGGLES) {
|
|
88
|
+
if (styleRPr[key] === void 0) continue;
|
|
89
|
+
const inheritedValue = inheritedFormatting?.[key] ?? false;
|
|
90
|
+
if (!(styleRPr[key] === true ? !inheritedValue : false)) negativeAttrs[key] = false;
|
|
91
|
+
}
|
|
92
|
+
if (Object.keys(negativeAttrs).length === 0) return [...marks];
|
|
93
|
+
const existingOverride = marks.find((mark) => mark.type.name === "runFormattingOverride");
|
|
94
|
+
const overrideType = existingOverride?.type ?? characterStyle.type.schema.marks["runFormattingOverride"];
|
|
95
|
+
if (!overrideType) return [...marks];
|
|
96
|
+
const override = overrideType.create({
|
|
97
|
+
...negativeAttrs,
|
|
98
|
+
...existingOverride?.attrs
|
|
99
|
+
});
|
|
100
|
+
return existingOverride ? marks.map((mark) => mark === existingOverride ? override : mark) : [...marks, override];
|
|
101
|
+
}
|
|
67
102
|
/**
|
|
68
103
|
* Marks of the first text node inside [from, to). A word processor's format
|
|
69
104
|
* brush copies from the start of the source selection, so a mixed selection
|
|
@@ -81,6 +116,12 @@ function firstTextMarks(state, from, to) {
|
|
|
81
116
|
});
|
|
82
117
|
return marks ?? [];
|
|
83
118
|
}
|
|
119
|
+
function paragraphDefaultTextFormatting(position) {
|
|
120
|
+
for (let depth = position.depth; depth >= 0; depth -= 1) {
|
|
121
|
+
const ancestor = position.node(depth);
|
|
122
|
+
if (ancestor.type.name === "paragraph") return expectParagraphAttrs(ancestor).defaultTextFormatting;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
84
125
|
/**
|
|
85
126
|
* Capture the paintable character marks active over the current selection.
|
|
86
127
|
* Returns the marks (with their attrs) to hand to `applyFormatMarks`. An empty
|
|
@@ -100,7 +141,10 @@ function captureFormatMarks(state) {
|
|
|
100
141
|
}
|
|
101
142
|
captured.push(mark);
|
|
102
143
|
}
|
|
103
|
-
return
|
|
144
|
+
return materializeStyleToggleNegatives({
|
|
145
|
+
inheritedFormatting: paragraphDefaultTextFormatting($from),
|
|
146
|
+
marks: captured
|
|
147
|
+
});
|
|
104
148
|
}
|
|
105
149
|
/**
|
|
106
150
|
* Apply captured marks onto the current selection's range: clear every paintable
|
|
@@ -8,7 +8,10 @@ declare function fromProseDoc(pmDoc: Node, baseDocument?: document_d_exports.Doc
|
|
|
8
8
|
/**
|
|
9
9
|
* Convert ProseMirror marks to TextFormatting
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
type MarksToTextFormattingOptions = {
|
|
12
|
+
inheritedFormatting: document_d_exports.TextFormatting | undefined;
|
|
13
|
+
};
|
|
14
|
+
declare function marksToTextFormatting(marks: readonly Mark[], options?: MarksToTextFormattingOptions): document_d_exports.TextFormatting;
|
|
12
15
|
declare const standaloneTableCellFromProseMirror: (node: Node) => document_d_exports.TableCell;
|
|
13
16
|
/**
|
|
14
17
|
* Update a Document with content from a ProseMirror document
|
|
@@ -8,6 +8,7 @@ import { applyRunFormattingOverrideAttrs } from "../extensions/marks/RunFormatti
|
|
|
8
8
|
import { directionToBidi } from "../paragraphDirection.js";
|
|
9
9
|
import { RUN_FORMATTING_MARK_NAMES } from "../runFormattingMarkNames.js";
|
|
10
10
|
import { parseShapeGeometryAdjustments } from "../shapeGeometryAdjustments.js";
|
|
11
|
+
import { cascadeStyleTextFormatting } from "../styles/styleToggleCascade.js";
|
|
11
12
|
import { expectTextBoxAnchorAttrs } from "../textBoxAnchorAttrs.js";
|
|
12
13
|
import { assertValidProseMirrorDocument } from "../validation.js";
|
|
13
14
|
import { runShadingAttrsToShading } from "./runShadingMark.js";
|
|
@@ -647,6 +648,7 @@ function paragraphAttrsToFormatting(attrs) {
|
|
|
647
648
|
*/
|
|
648
649
|
function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, textBoxAnchorMarkers, skipLeadingRenderedPageBreak = false) {
|
|
649
650
|
const content = [];
|
|
651
|
+
const inheritedFormatting = paragraph.type.name === "paragraph" ? expectParagraphAttrs(paragraph).defaultTextFormatting ?? void 0 : void 0;
|
|
650
652
|
const sortedEmptyHyperlinks = (emptyHyperlinks ?? []).map((attrs, order) => ({
|
|
651
653
|
attrs,
|
|
652
654
|
order
|
|
@@ -763,7 +765,12 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
|
|
|
763
765
|
const changeMark = insertionMark ?? deletionMark;
|
|
764
766
|
if (!changeMark) return;
|
|
765
767
|
const changeAttrs = expectTrackedChangeMarkAttrs(changeMark);
|
|
766
|
-
const
|
|
768
|
+
const otherMarks = node.marks.filter((m) => m.type.name !== "insertion" && m.type.name !== "deletion");
|
|
769
|
+
const run = createTrackedChangeRun({
|
|
770
|
+
inheritedFormatting,
|
|
771
|
+
marks: otherMarks,
|
|
772
|
+
node
|
|
773
|
+
});
|
|
767
774
|
if (!run) return;
|
|
768
775
|
const trackedContent = linkMark ? {
|
|
769
776
|
...createHyperlink(linkMark),
|
|
@@ -809,7 +816,11 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
|
|
|
809
816
|
currentHyperlink = createHyperlink(linkMark);
|
|
810
817
|
currentHyperlinkKey = linkKey;
|
|
811
818
|
}
|
|
812
|
-
addNodeToHyperlink(
|
|
819
|
+
addNodeToHyperlink({
|
|
820
|
+
hyperlink: currentHyperlink,
|
|
821
|
+
inheritedFormatting,
|
|
822
|
+
node
|
|
823
|
+
});
|
|
813
824
|
return;
|
|
814
825
|
}
|
|
815
826
|
if (currentHyperlink) flushCurrentInline();
|
|
@@ -818,7 +829,11 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
|
|
|
818
829
|
if (currentRun && currentMarksKey === marksKey) appendTextToRun(currentRun, node.text || "");
|
|
819
830
|
else {
|
|
820
831
|
if (currentRun) content.push(currentRun);
|
|
821
|
-
currentRun = createRunFromText(
|
|
832
|
+
currentRun = createRunFromText({
|
|
833
|
+
inheritedFormatting,
|
|
834
|
+
marks: node.marks,
|
|
835
|
+
text: node.text || ""
|
|
836
|
+
});
|
|
822
837
|
currentMarksKey = marksKey;
|
|
823
838
|
}
|
|
824
839
|
} else if (node.type.name === "symbol") {
|
|
@@ -880,11 +895,11 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
|
|
|
880
895
|
});
|
|
881
896
|
return content;
|
|
882
897
|
}
|
|
883
|
-
function createTrackedChangeRun(
|
|
898
|
+
function createTrackedChangeRun({ inheritedFormatting, marks, node }) {
|
|
884
899
|
const noteRefMark = marks.find((mark) => mark.type.name === "footnoteRef");
|
|
885
900
|
if (noteRefMark) return createNoteReferenceRun(noteRefMark, marks);
|
|
886
901
|
if (node.isText) {
|
|
887
|
-
const formatting = marksToTextFormatting(marks);
|
|
902
|
+
const formatting = marksToTextFormatting(marks, { inheritedFormatting });
|
|
888
903
|
const run = {
|
|
889
904
|
type: "run",
|
|
890
905
|
content: node.text ? [{
|
|
@@ -989,10 +1004,7 @@ function createHyperlink(linkMark) {
|
|
|
989
1004
|
if (attrs.rId) hyperlink.rId = attrs.rId;
|
|
990
1005
|
return hyperlink;
|
|
991
1006
|
}
|
|
992
|
-
|
|
993
|
-
* Add a node to a hyperlink
|
|
994
|
-
*/
|
|
995
|
-
function addNodeToHyperlink(hyperlink, node) {
|
|
1007
|
+
function addNodeToHyperlink({ hyperlink, inheritedFormatting, node }) {
|
|
996
1008
|
const noteRefMark = node.marks.find((m) => m.type.name === "footnoteRef");
|
|
997
1009
|
if (noteRefMark) {
|
|
998
1010
|
hyperlink.children.push(createNoteReferenceRun(noteRefMark, node.marks));
|
|
@@ -1000,7 +1012,11 @@ function addNodeToHyperlink(hyperlink, node) {
|
|
|
1000
1012
|
}
|
|
1001
1013
|
const nonLinkMarks = node.marks.filter((m) => m.type.name !== "hyperlink");
|
|
1002
1014
|
if (node.isText && node.text) {
|
|
1003
|
-
const run = createRunFromText(
|
|
1015
|
+
const run = createRunFromText({
|
|
1016
|
+
inheritedFormatting,
|
|
1017
|
+
marks: nonLinkMarks,
|
|
1018
|
+
text: node.text
|
|
1019
|
+
});
|
|
1004
1020
|
hyperlink.children.push(run);
|
|
1005
1021
|
return;
|
|
1006
1022
|
}
|
|
@@ -1044,11 +1060,8 @@ function createNoteReferenceRun(noteRefMark, marks) {
|
|
|
1044
1060
|
if (vertAlign) run.formatting = { vertAlign };
|
|
1045
1061
|
return run;
|
|
1046
1062
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
*/
|
|
1050
|
-
function createRunFromText(text, marks) {
|
|
1051
|
-
const formatting = getRunFormattingFromMarks(marks);
|
|
1063
|
+
function createRunFromText({ inheritedFormatting, marks, text }) {
|
|
1064
|
+
const formatting = getRunFormattingFromMarks(marks, { inheritedFormatting });
|
|
1052
1065
|
const run = {
|
|
1053
1066
|
type: "run",
|
|
1054
1067
|
content: [{
|
|
@@ -1082,9 +1095,9 @@ function restoreRunPropertyChanges(run, marks) {
|
|
|
1082
1095
|
if (changes.length === 0) return;
|
|
1083
1096
|
run.propertyChanges = [...changes];
|
|
1084
1097
|
}
|
|
1085
|
-
function getRunFormattingFromMarks(marks) {
|
|
1098
|
+
function getRunFormattingFromMarks(marks, options) {
|
|
1086
1099
|
if (!marks || marks.length === 0) return;
|
|
1087
|
-
const formatting = marksToTextFormatting(marks);
|
|
1100
|
+
const formatting = marksToTextFormatting(marks, options);
|
|
1088
1101
|
return Object.keys(formatting).length > 0 ? formatting : void 0;
|
|
1089
1102
|
}
|
|
1090
1103
|
/**
|
|
@@ -1354,11 +1367,9 @@ function createShapeRun(node) {
|
|
|
1354
1367
|
}]
|
|
1355
1368
|
};
|
|
1356
1369
|
}
|
|
1357
|
-
|
|
1358
|
-
* Convert ProseMirror marks to TextFormatting
|
|
1359
|
-
*/
|
|
1360
|
-
function marksToTextFormatting(marks) {
|
|
1370
|
+
function marksToTextFormatting(marks, options) {
|
|
1361
1371
|
const formatting = {};
|
|
1372
|
+
let directOverrideFormatting;
|
|
1362
1373
|
let characterStyleRPr;
|
|
1363
1374
|
let runFormattingOverrideMark;
|
|
1364
1375
|
for (const mark of marks) switch (mark.type.name) {
|
|
@@ -1483,8 +1494,18 @@ function marksToTextFormatting(marks) {
|
|
|
1483
1494
|
}
|
|
1484
1495
|
default: break;
|
|
1485
1496
|
}
|
|
1486
|
-
if (runFormattingOverrideMark)
|
|
1487
|
-
|
|
1497
|
+
if (runFormattingOverrideMark) {
|
|
1498
|
+
const overrideAttrs = expectRunFormattingOverrideMarkAttrs(runFormattingOverrideMark);
|
|
1499
|
+
applyRunFormattingOverrideAttrs(formatting, overrideAttrs);
|
|
1500
|
+
directOverrideFormatting = {};
|
|
1501
|
+
applyRunFormattingOverrideAttrs(directOverrideFormatting, overrideAttrs);
|
|
1502
|
+
}
|
|
1503
|
+
if (characterStyleRPr) return subtractCharacterStyleFormatting({
|
|
1504
|
+
directOverrideFormatting,
|
|
1505
|
+
formatting,
|
|
1506
|
+
inheritedFormatting: options?.inheritedFormatting,
|
|
1507
|
+
styleRPr: characterStyleRPr
|
|
1508
|
+
});
|
|
1488
1509
|
return formatting;
|
|
1489
1510
|
}
|
|
1490
1511
|
/**
|
|
@@ -1509,35 +1530,29 @@ const NEGATABLE_STYLE_KEYS = [
|
|
|
1509
1530
|
["outline", null],
|
|
1510
1531
|
["rtl", null]
|
|
1511
1532
|
];
|
|
1512
|
-
|
|
1513
|
-
* Drop run formatting values that the run's character style already provides
|
|
1514
|
-
* (the `w:rStyle` reference re-imposes them on load), so a styled run
|
|
1515
|
-
* serializes back to a style reference instead of baked direct formatting.
|
|
1516
|
-
*
|
|
1517
|
-
* `styleRPr` is the load-time snapshot from the characterStyle mark, captured
|
|
1518
|
-
* in the same normal form this module produces from marks, so value equality
|
|
1519
|
-
* is a faithful "came from the style and is unchanged" check. Values that
|
|
1520
|
-
* differ (user edits, direct overrides from the source document) stay as
|
|
1521
|
-
* direct formatting, which wins over the style per the OOXML cascade.
|
|
1522
|
-
*
|
|
1523
|
-
* When the user *removes* a boolean property the style supplied (e.g. toggling
|
|
1524
|
-
* off bold on text whose `w:rStyle` is bold), that key is no longer present in
|
|
1525
|
-
* `formatting`, so the subtraction loop above never sees it. Without a counter,
|
|
1526
|
-
* the run keeps the style reference and Word re-applies the removed formatting
|
|
1527
|
-
* on load. The second pass emits an explicit negative override (`false`) for
|
|
1528
|
-
* each negatable boolean the style set to `true` but the run no longer carries.
|
|
1529
|
-
*/
|
|
1530
|
-
function subtractCharacterStyleFormatting(formatting, styleRPr) {
|
|
1533
|
+
function subtractCharacterStyleFormatting({ directOverrideFormatting, formatting, inheritedFormatting, styleRPr }) {
|
|
1531
1534
|
const result = {};
|
|
1535
|
+
const effectiveStyleFormatting = cascadeStyleTextFormatting([{
|
|
1536
|
+
formatting: inheritedFormatting,
|
|
1537
|
+
type: "direct"
|
|
1538
|
+
}, {
|
|
1539
|
+
formatting: styleRPr,
|
|
1540
|
+
type: "style"
|
|
1541
|
+
}]).formatting;
|
|
1532
1542
|
for (const key of Object.keys(formatting)) {
|
|
1533
1543
|
const value = formatting[key];
|
|
1534
1544
|
if (value === void 0) continue;
|
|
1535
|
-
|
|
1545
|
+
if (directOverrideFormatting?.[key] !== void 0) {
|
|
1546
|
+
result[key] = value;
|
|
1547
|
+
continue;
|
|
1548
|
+
}
|
|
1549
|
+
const styleValue = key === "styleId" ? void 0 : effectiveStyleFormatting?.[key];
|
|
1550
|
+
if ((key === "boldCs" || key === "italicCs") && styleValue === false && value === true) continue;
|
|
1536
1551
|
if (styleValue !== void 0 && JSON.stringify(value) === JSON.stringify(styleValue)) continue;
|
|
1537
1552
|
result[key] = value;
|
|
1538
1553
|
}
|
|
1539
1554
|
for (const [key, csKey] of NEGATABLE_STYLE_KEYS) {
|
|
1540
|
-
if (
|
|
1555
|
+
if (effectiveStyleFormatting?.[key] !== true || formatting[key] !== void 0) continue;
|
|
1541
1556
|
result[key] = false;
|
|
1542
1557
|
if (csKey !== null) result[csKey] = false;
|
|
1543
1558
|
}
|
|
@@ -22,7 +22,10 @@ declare function standaloneTableCellToProseMirror(cell: document_d_exports.Table
|
|
|
22
22
|
/**
|
|
23
23
|
* Convert TextFormatting to ProseMirror marks
|
|
24
24
|
*/
|
|
25
|
-
|
|
25
|
+
type TextFormattingToMarksOptions = {
|
|
26
|
+
overrideFormatting: document_d_exports.TextFormatting | undefined;
|
|
27
|
+
};
|
|
28
|
+
declare function textFormattingToMarks(formatting: document_d_exports.TextFormatting | undefined, options?: TextFormattingToMarksOptions): ReturnType<typeof schema.mark>[];
|
|
26
29
|
/**
|
|
27
30
|
* Convert HeaderFooter content (array of Paragraph/Table blocks) to a ProseMirror document.
|
|
28
31
|
* Used for editing headers/footers in their own ProseMirror editor and for
|