@stll/folio-core 0.37.2 → 0.37.4
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/ai-edits/table-cell-mutations.js +10 -5
- package/dist/ai-edits/table-template.js +19 -5
- package/dist/controller/hiddenEditorManager.js +11 -0
- package/dist/controller/layoutPipeline.js +1 -1
- package/dist/display-list/build/textBoxPrimitives.js +34 -1
- package/dist/display-list/dom/renderDisplayListToDom.js +5 -2
- package/dist/display-list/types.d.ts +8 -4
- package/dist/docx/paragraphPropertySource.d.ts +82 -2
- package/dist/docx/paragraphPropertySource.js +569 -3
- package/dist/docx/parser.js +9 -0
- package/dist/docx/server/materializeYjsDocx.d.ts +1 -1
- package/dist/docx/server/materializeYjsDocx.js +21 -2
- package/dist/headless-layout.js +1 -1
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +7 -1
- package/dist/layout-bridge/convert/headerFooterLayout.js +20 -3
- package/dist/layout-bridge/convert/toFlowBlocks.js +43 -12
- package/dist/layout-engine/index.js +20 -10
- package/dist/layout-engine/measure/measureBlocks.d.ts +7 -3
- package/dist/layout-engine/measure/measureBlocks.js +10 -7
- package/dist/layout-engine/measure/measureParagraph.d.ts +2 -0
- package/dist/layout-engine/measure/measureParagraph.js +1 -1
- package/dist/layout-engine/paginator.d.ts +1 -1
- package/dist/layout-engine/paginator.js +41 -4
- package/dist/layout-engine/textBoxFlow.d.ts +2 -0
- package/dist/layout-engine/textBoxFlow.js +9 -5
- package/dist/layout-engine/types.d.ts +6 -0
- package/dist/layout-painter/documentColors.d.ts +10 -1
- package/dist/layout-painter/documentColors.js +16 -1
- package/dist/layout-painter/renderParagraph.js +9 -24
- package/dist/layout-painter/renderTable.js +3 -3
- package/dist/layout-painter/renderTextBox.js +2 -1
- package/dist/pdf/pageSpace.d.ts +12 -1
- package/dist/pdf/pageSpace.js +24 -1
- package/dist/pdf/paint.js +2 -2
- package/dist/prosemirror/attrs/index.js +32 -8
- package/dist/prosemirror/commands/comments.js +12 -3
- package/dist/prosemirror/commands/tableCellMergeResolution.js +18 -11
- package/dist/prosemirror/conversion/fromProseDoc.js +114 -25
- package/dist/prosemirror/conversion/toProseDoc.js +101 -8
- package/dist/prosemirror/extensions/core/DocExtension.js +2 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -0
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +5 -1
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +132 -41
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -61
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +16 -0
- package/dist/prosemirror/schema/nodes.d.ts +14 -2
- package/dist/prosemirror/schema/nodes.js +7 -0
- package/dist/prosemirror/yjsParagraphSourceContract.d.ts +9 -0
- package/dist/prosemirror/yjsParagraphSourceContract.js +26 -0
- package/dist/utils/rotationBoundingBox.d.ts +5 -1
- package/dist/utils/rotationBoundingBox.js +9 -1
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { numPrEqual } from "../../docx/numberingParser.js";
|
|
2
|
-
import { copyParagraphPropertySource, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceTransferId, linkParagraphPropertySourceCandidate, recreateProseNodeWithParagraphPropertySource } from "../../docx/paragraphPropertySource.js";
|
|
2
|
+
import { PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, ParagraphPropertySourceValidationError, copyDocumentParagraphPropertySourceContract, copyDocumentParagraphPropertySources, copyParagraphPropertyCapture, copyParagraphPropertySource, decodeTableCellParagraphSourcePayload, getDocumentParagraphPropertySourceContract, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceToken, getParagraphPropertySourceTransferId, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, isParagraphPropertySourceToken, linkParagraphPropertySourceCandidate, paragraphPropertySourceBelongsToDocument, paragraphPropertySourceTokenMatchesContract, recreateProseNodeWithParagraphPropertySource, restoreTableCellsWithParagraphPropertySources, visitDocumentStoryParagraphs, visitTableCellParagraphPropertySourceBindings } from "../../docx/paragraphPropertySource.js";
|
|
3
3
|
import { visitDocxParagraphs } from "../../docx/paragraphTraversal.js";
|
|
4
4
|
import { ShapeOutlineStyleSchema, narrowEnum } from "../../docx/parserEnums.js";
|
|
5
5
|
import { DATE_UTC_ATTRIBUTE } from "../../docx/trackedChangeInfo.js";
|
|
@@ -103,23 +103,84 @@ const uniqueParagraphsById = (content, includeTransferIds = false) => {
|
|
|
103
103
|
});
|
|
104
104
|
return paragraphs;
|
|
105
105
|
};
|
|
106
|
+
const restoreParagraphPropertySource = (paragraph, baseParagraph) => {
|
|
107
|
+
copyParagraphPropertySource(paragraph, baseParagraph);
|
|
108
|
+
const baseFormatting = baseParagraph.formatting;
|
|
109
|
+
if (!baseFormatting?.numPr || !baseFormatting.numPrFromStyle || !numPrEqual(baseFormatting.numPr, baseFormatting.numPrFromStyle)) return;
|
|
110
|
+
const { numPr, numPrFromStyle, ...authoredFormatting } = baseFormatting;
|
|
111
|
+
if (canonicalJson(paragraph.formatting ?? {}) !== canonicalJson(authoredFormatting)) return;
|
|
112
|
+
paragraph.formatting = {
|
|
113
|
+
...paragraph.formatting,
|
|
114
|
+
numPr,
|
|
115
|
+
numPrFromStyle
|
|
116
|
+
};
|
|
117
|
+
};
|
|
106
118
|
const restoreParagraphPropertySources = (content, baseContent, linkedTargets, linkedSources) => {
|
|
107
119
|
const baseParagraphs = uniqueParagraphsById(baseContent, true);
|
|
108
120
|
for (const [paraId, paragraph] of uniqueParagraphsById(content)) {
|
|
109
121
|
const baseParagraph = baseParagraphs.get(paraId);
|
|
110
122
|
if (!paragraph || !baseParagraph || linkedTargets.has(paragraph) || linkedSources.has(baseParagraph) || !getParagraphPropertySource(baseParagraph)) continue;
|
|
111
|
-
|
|
112
|
-
const baseFormatting = baseParagraph.formatting;
|
|
113
|
-
if (!baseFormatting?.numPr || !baseFormatting.numPrFromStyle || !numPrEqual(baseFormatting.numPr, baseFormatting.numPrFromStyle)) continue;
|
|
114
|
-
const { numPr, numPrFromStyle, ...authoredFormatting } = baseFormatting;
|
|
115
|
-
if (canonicalJson(paragraph.formatting ?? {}) !== canonicalJson(authoredFormatting)) continue;
|
|
116
|
-
paragraph.formatting = {
|
|
117
|
-
...paragraph.formatting,
|
|
118
|
-
numPr,
|
|
119
|
-
numPrFromStyle
|
|
120
|
-
};
|
|
123
|
+
restoreParagraphPropertySource(paragraph, baseParagraph);
|
|
121
124
|
}
|
|
122
125
|
};
|
|
126
|
+
const sourceValidationError = (code, message) => new ParagraphPropertySourceValidationError({
|
|
127
|
+
code,
|
|
128
|
+
message
|
|
129
|
+
});
|
|
130
|
+
const validateParagraphPropertySourceTokens = (pmDoc, baseDocument, contract) => {
|
|
131
|
+
const sourceParagraphs = copyDocumentParagraphPropertySources(baseDocument);
|
|
132
|
+
if (!sourceParagraphs) panic("A paragraph-property source contract lost its source registry");
|
|
133
|
+
const currentTokens = /* @__PURE__ */ new Set();
|
|
134
|
+
visitDocumentStoryParagraphs(baseDocument.package.document.content, (paragraph) => {
|
|
135
|
+
const token = getParagraphPropertySourceToken(paragraph);
|
|
136
|
+
if (!token) {
|
|
137
|
+
if (paragraphPropertySourceBelongsToDocument(paragraph, baseDocument)) throw sourceValidationError("invalid_token", "A source-bound paragraph is missing its paragraph-property token.");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (!paragraphPropertySourceTokenMatchesContract(token, contract)) throw sourceValidationError("invalid_token", "The source document contains an invalid paragraph-property token.");
|
|
141
|
+
if (!sourceParagraphs.has(token)) throw sourceValidationError("unknown_token", "The source document contains an unknown paragraph-property token.");
|
|
142
|
+
if (currentTokens.has(token)) throw sourceValidationError("duplicate_token", "The source document contains a duplicate paragraph-property token.");
|
|
143
|
+
currentTokens.add(token);
|
|
144
|
+
});
|
|
145
|
+
const seen = /* @__PURE__ */ new Set();
|
|
146
|
+
const validateToken = (token) => {
|
|
147
|
+
if (token === null || token === void 0) return;
|
|
148
|
+
if (!isParagraphPropertySourceToken(token)) throw sourceValidationError("invalid_token", "A paragraph contains a malformed paragraph-property token.");
|
|
149
|
+
if (!paragraphPropertySourceTokenMatchesContract(token, contract)) throw sourceValidationError("unknown_token", "A paragraph-property token belongs to a different source document.");
|
|
150
|
+
if (seen.has(token)) throw sourceValidationError("duplicate_token", "A paragraph-property token is attached to more than one paragraph.");
|
|
151
|
+
if (!sourceParagraphs.has(token)) throw sourceValidationError("unknown_token", "A paragraph-property token is not present in the source document.");
|
|
152
|
+
seen.add(token);
|
|
153
|
+
};
|
|
154
|
+
const validateTableCellBinding = (binding) => {
|
|
155
|
+
switch (binding.type) {
|
|
156
|
+
case "authored": return;
|
|
157
|
+
case "source":
|
|
158
|
+
validateToken(binding.token);
|
|
159
|
+
return;
|
|
160
|
+
default: return binding;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
pmDoc.descendants((node) => {
|
|
164
|
+
if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
|
|
165
|
+
const continuationCells = expectTableCellAttrs(node)._docxVMergeContinuationCells;
|
|
166
|
+
if (continuationCells !== void 0 && continuationCells !== null) visitTableCellParagraphPropertySourceBindings(decodeTableCellParagraphSourcePayload(continuationCells), (binding) => validateTableCellBinding(binding));
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
if (node.type.name !== "paragraph") return true;
|
|
170
|
+
validateToken(getProseParagraphPropertySourceToken(node));
|
|
171
|
+
return false;
|
|
172
|
+
});
|
|
173
|
+
return sourceParagraphs;
|
|
174
|
+
};
|
|
175
|
+
const restoreParagraphPropertySourcesByToken = (content, baseParagraphs) => {
|
|
176
|
+
visitDocumentStoryParagraphs(content, (paragraph) => {
|
|
177
|
+
const token = getParagraphPropertySourceToken(paragraph);
|
|
178
|
+
if (!token) return;
|
|
179
|
+
const baseParagraph = baseParagraphs.get(token);
|
|
180
|
+
if (!baseParagraph) panic("Validated paragraph-property token lost its source owner");
|
|
181
|
+
restoreParagraphPropertySource(paragraph, baseParagraph);
|
|
182
|
+
});
|
|
183
|
+
};
|
|
123
184
|
const restoreLinkedParagraphPropertySources = (content) => {
|
|
124
185
|
const targetsBySource = /* @__PURE__ */ new Map();
|
|
125
186
|
const linkedTargets = /* @__PURE__ */ new Set();
|
|
@@ -133,7 +194,7 @@ const restoreLinkedParagraphPropertySources = (content) => {
|
|
|
133
194
|
});
|
|
134
195
|
for (const [source, targets] of targetsBySource) if (targets.length === 1) {
|
|
135
196
|
const target = targets.at(0);
|
|
136
|
-
if (target)
|
|
197
|
+
if (target) copyParagraphPropertyCapture(target, source);
|
|
137
198
|
}
|
|
138
199
|
return {
|
|
139
200
|
targets: linkedTargets,
|
|
@@ -143,20 +204,30 @@ const restoreLinkedParagraphPropertySources = (content) => {
|
|
|
143
204
|
/** Convert a ProseMirror document to the document model. */
|
|
144
205
|
function fromProseDoc(pmDoc, baseDocument) {
|
|
145
206
|
assertValidProseMirrorDocument(pmDoc, "Cannot convert invalid ProseMirror document to DOCX model");
|
|
207
|
+
const baseContract = baseDocument ? getDocumentParagraphPropertySourceContract(baseDocument) : void 0;
|
|
208
|
+
const proseContractAttribute = pmDoc.attrs[PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR];
|
|
209
|
+
const proseContract = getProseDocumentParagraphPropertySourceContract(pmDoc);
|
|
210
|
+
if (proseContractAttribute !== null && proseContractAttribute !== void 0 && proseContract === null || (baseContract ?? null) !== proseContract) throw sourceValidationError("contract_mismatch", "The ProseMirror document does not match its paragraph-property source document.");
|
|
211
|
+
const tokenSources = baseContract && proseContract && baseDocument ? validateParagraphPropertySourceTokens(pmDoc, baseDocument, baseContract) : null;
|
|
146
212
|
const blocks = extractBlocks(pmDoc, "resolve", baseDocument?.package.styles ? createStyleEngine(baseDocument.package.styles) : null);
|
|
147
213
|
const linkedSources = restoreLinkedParagraphPropertySources(blocks);
|
|
148
|
-
if (
|
|
214
|
+
if (tokenSources) restoreParagraphPropertySourcesByToken(blocks, tokenSources);
|
|
215
|
+
else if (baseDocument) restoreParagraphPropertySources(blocks, baseDocument.package.document.content, linkedSources.targets, linkedSources.sources);
|
|
149
216
|
const documentBody = { content: blocks };
|
|
150
217
|
if (baseDocument?.package.document.finalSectionProperties) documentBody.finalSectionProperties = baseDocument.package.document.finalSectionProperties;
|
|
151
218
|
if (baseDocument?.package.document.sections) documentBody.sections = baseDocument.package.document.sections;
|
|
152
219
|
if (baseDocument?.package.document.comments) documentBody.comments = baseDocument.package.document.comments;
|
|
153
|
-
if (baseDocument)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
220
|
+
if (baseDocument) {
|
|
221
|
+
const updatedDocument = {
|
|
222
|
+
...baseDocument,
|
|
223
|
+
package: {
|
|
224
|
+
...baseDocument.package,
|
|
225
|
+
document: documentBody
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
copyDocumentParagraphPropertySourceContract(updatedDocument, baseDocument);
|
|
229
|
+
return updatedDocument;
|
|
230
|
+
}
|
|
160
231
|
return { package: { document: documentBody } };
|
|
161
232
|
}
|
|
162
233
|
const isSuggestedMark = (mark) => mark.attrs["provenance"] === "suggested";
|
|
@@ -2606,7 +2677,7 @@ function convertPMTableRow(node, documentCounts, activeVerticalMerges, styleReso
|
|
|
2606
2677
|
const colspan = Math.max(cellAttrs.colspan, 1);
|
|
2607
2678
|
cells.push(convertPMTableCell(cellNode, documentCounts, styleResolver));
|
|
2608
2679
|
if (cellAttrs.rowspan > 1) {
|
|
2609
|
-
const continuationCells = cellAttrs._docxVMergeContinuationCells;
|
|
2680
|
+
const continuationCells = cellAttrs._docxVMergeContinuationCells !== void 0 && cellAttrs._docxVMergeContinuationCells !== null ? restoreTableCellsWithParagraphPropertySources(decodeTableCellParagraphSourcePayload(cellAttrs._docxVMergeContinuationCells)) : void 0;
|
|
2610
2681
|
activeVerticalMerges?.set(gridColumn, {
|
|
2611
2682
|
remainingRows: cellAttrs.rowspan - 1,
|
|
2612
2683
|
colspan,
|
|
@@ -2791,6 +2862,24 @@ function tableCellAttrsToFormatting(attrs) {
|
|
|
2791
2862
|
if (attrs.margins) f.margins = buildCellMarginsFromAttrs(attrs.margins);
|
|
2792
2863
|
return f;
|
|
2793
2864
|
}
|
|
2865
|
+
const isUnchangedSourceEmptyPlaceholder = (blocks) => {
|
|
2866
|
+
const paragraph = blocks.at(0);
|
|
2867
|
+
if (blocks.length !== 1 || paragraph?.type !== "paragraph" || paragraph.content.length !== 0) return false;
|
|
2868
|
+
return Object.keys(paragraph).every((key) => key === "type" || key === "content" || key === "paraId" || key === "textId");
|
|
2869
|
+
};
|
|
2870
|
+
const projectTextBoxBodyContent = (state, blocks) => {
|
|
2871
|
+
switch (state.type) {
|
|
2872
|
+
case "source-empty": return isUnchangedSourceEmptyPlaceholder(blocks) ? { type: "source-empty" } : {
|
|
2873
|
+
type: "authored",
|
|
2874
|
+
content: blocks
|
|
2875
|
+
};
|
|
2876
|
+
case "authored": return {
|
|
2877
|
+
type: "authored",
|
|
2878
|
+
content: blocks
|
|
2879
|
+
};
|
|
2880
|
+
default: return state;
|
|
2881
|
+
}
|
|
2882
|
+
};
|
|
2794
2883
|
/**
|
|
2795
2884
|
* Convert a ProseMirror textBox node back to a Paragraph wrapping a ShapeContent run.
|
|
2796
2885
|
* The text box content becomes a Shape with textBody.
|
|
@@ -2803,6 +2892,7 @@ function convertPMTextBox(node, styleResolver = null) {
|
|
|
2803
2892
|
if (child.type.name === "paragraph") childBlocks.push(convertPMParagraph(child, void 0, void 0, styleResolver));
|
|
2804
2893
|
else if (child.type.name === "table") childBlocks.push(convertPMTable(child, void 0, styleResolver));
|
|
2805
2894
|
});
|
|
2895
|
+
const textBodyContent = projectTextBoxBodyContent(attrs._docxTextBodyContentState, childBlocks);
|
|
2806
2896
|
const shape = {
|
|
2807
2897
|
type: "shape",
|
|
2808
2898
|
shapeType: "textBox",
|
|
@@ -2811,10 +2901,7 @@ function convertPMTextBox(node, styleResolver = null) {
|
|
|
2811
2901
|
height: attrs.height ? pixelsToEmu(attrs.height) : 0
|
|
2812
2902
|
},
|
|
2813
2903
|
textBody: {
|
|
2814
|
-
content:
|
|
2815
|
-
type: "paragraph",
|
|
2816
|
-
content: []
|
|
2817
|
-
}],
|
|
2904
|
+
content: textBodyContent.type === "source-empty" ? [] : textBodyContent.content,
|
|
2818
2905
|
...attrs.autoFit !== void 0 ? { autoFit: attrs.autoFit } : {},
|
|
2819
2906
|
...attrs.textWrap !== void 0 ? { textWrap: attrs.textWrap } : {},
|
|
2820
2907
|
...verticalAlign !== void 0 ? { anchor: verticalAlign } : {},
|
|
@@ -2829,6 +2916,8 @@ function convertPMTextBox(node, styleResolver = null) {
|
|
|
2829
2916
|
}
|
|
2830
2917
|
};
|
|
2831
2918
|
if (attrs.textBoxId) shape.id = attrs.textBoxId;
|
|
2919
|
+
const transform = parseTransformAttr(attrs.transform);
|
|
2920
|
+
if (transform) shape.transform = transform;
|
|
2832
2921
|
if (attrs.fillColor) shape.fill = {
|
|
2833
2922
|
type: "solid",
|
|
2834
2923
|
color: { rgb: attrs.fillColor.replace("#", "") }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveColorValueToHex } from "../../docx/drawingUtils.js";
|
|
2
|
-
import {
|
|
2
|
+
import { PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, createProseParagraphWithPropertySource, getDocumentParagraphPropertySourceContract, recreateProseNodeWithParagraphPropertySource, transportTableCellsWithParagraphPropertySources } from "../../docx/paragraphPropertySource.js";
|
|
3
3
|
import { buildPageBreakRunSourceDescendantIndex } from "../../internal/pageBreakRunSourceDescendantIndex.js";
|
|
4
4
|
import { createStyleEngine } from "../../style-engine/styleEngine.js";
|
|
5
5
|
import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
|
|
@@ -131,6 +131,7 @@ function toProseDoc(document, options) {
|
|
|
131
131
|
const finalSectionStart = document.package.document.sections?.at(-1)?.properties.sectionStart ?? null;
|
|
132
132
|
const adjustLineHeightInTable = document.package.settings?.adjustLineHeightInTable === true;
|
|
133
133
|
const pmDoc = stampNumberedRefFieldBaselines(schema.node("doc", {
|
|
134
|
+
[PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR]: getDocumentParagraphPropertySourceContract(document) ?? null,
|
|
134
135
|
_finalSectionStart: finalSectionStart,
|
|
135
136
|
_adjustLineHeightInTable: adjustLineHeightInTable
|
|
136
137
|
}, nodes));
|
|
@@ -205,7 +206,8 @@ function convertParagraph(paragraph, styleResolver, context, activeCommentIds, e
|
|
|
205
206
|
let paragraphStyleRunFormatting;
|
|
206
207
|
let paragraphStyleFontFamily;
|
|
207
208
|
if (styleResolver) {
|
|
208
|
-
|
|
209
|
+
const resolved = styleResolver.resolveParagraphStyle(paragraph.formatting?.styleId);
|
|
210
|
+
styleRunFormatting = extraRunFormatting === void 0 ? resolved.runFormatting : withoutFontFamily(resolved.runFormatting);
|
|
209
211
|
const paragraphStyle = paragraph.formatting?.styleId ? styleResolver.getStyle(paragraph.formatting.styleId) ?? styleResolver.getDefaultParagraphStyle() : styleResolver.getDefaultParagraphStyle();
|
|
210
212
|
paragraphStyleRunFormatting = paragraphStyle?.type === "paragraph" ? paragraphStyle.rPr : void 0;
|
|
211
213
|
paragraphStyleFontFamily = resolveParagraphStyleFontFamily(paragraph.formatting?.styleId, styleResolver);
|
|
@@ -343,18 +345,28 @@ function convertParagraph(paragraph, styleResolver, context, activeCommentIds, e
|
|
|
343
345
|
}
|
|
344
346
|
if (bookmarksArr) attrs.bookmarks = bookmarksArr;
|
|
345
347
|
if (emptyHyperlinks) attrs._emptyHyperlinks = emptyHyperlinks;
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
348
|
+
return createProseParagraphWithPropertySource(schema.nodes["paragraph"], paragraph, {
|
|
349
|
+
attrs,
|
|
350
|
+
content: inlineNodes
|
|
351
|
+
});
|
|
349
352
|
}
|
|
353
|
+
const withoutFontFamily = (formatting) => {
|
|
354
|
+
if (!formatting?.fontFamily) return formatting;
|
|
355
|
+
const { fontFamily: _fontFamily, ...withoutFont } = formatting;
|
|
356
|
+
return Object.keys(withoutFont).length > 0 ? withoutFont : void 0;
|
|
357
|
+
};
|
|
350
358
|
const resolveParagraphStyleFontFamily = (styleId, styleResolver) => {
|
|
351
359
|
let style = styleId ? styleResolver.getStyle(styleId) : styleResolver.getDefaultParagraphStyle();
|
|
352
360
|
const visited = /* @__PURE__ */ new Set();
|
|
361
|
+
const styleChain = [];
|
|
353
362
|
while (style?.type === "paragraph" && !visited.has(style.styleId)) {
|
|
354
363
|
visited.add(style.styleId);
|
|
355
|
-
if (style.rPr?.fontFamily)
|
|
364
|
+
if (style.rPr?.fontFamily) styleChain.push({ fontFamily: style.rPr.fontFamily });
|
|
356
365
|
style = style.basedOn ? styleResolver.getStyle(style.basedOn) : void 0;
|
|
357
366
|
}
|
|
367
|
+
let formatting;
|
|
368
|
+
for (const styleFormatting of styleChain.toReversed()) formatting = mergeTextFormatting(formatting, styleFormatting);
|
|
369
|
+
return formatting?.fontFamily;
|
|
358
370
|
};
|
|
359
371
|
/**
|
|
360
372
|
* Apply comment marks to PM nodes within a comment range.
|
|
@@ -789,7 +801,14 @@ function paragraphContentHasMeaningfulContent(content) {
|
|
|
789
801
|
return true;
|
|
790
802
|
}
|
|
791
803
|
function convertTable(table, styleResolver, context) {
|
|
792
|
-
for (const row of table.rows) for (const cell of row.cells)
|
|
804
|
+
for (const row of table.rows) for (const cell of row.cells) {
|
|
805
|
+
if (cell.formatting?.vMerge === "continue" && context.pageBreakRunSourceDescendants.containsPageBreakRun(cell.content)) throw new UnsupportedDocxToProseMirrorConversionError({
|
|
806
|
+
message: `${PAGE_BREAK_CONTAINER_DESCRIPTIONS["table-cell"]} cannot be represented in the editor model`,
|
|
807
|
+
owner: "table-cell",
|
|
808
|
+
contentType: "break"
|
|
809
|
+
});
|
|
810
|
+
assertSourceContainerHasNoPageBreakRun(cell.content, "table-cell", context.pageBreakRunSourceDescendants);
|
|
811
|
+
}
|
|
793
812
|
const rowSpanMap = calculateRowSpans(table);
|
|
794
813
|
const columnWidths = table.columnWidths;
|
|
795
814
|
const totalWidth = columnWidths?.reduce((sum, w) => sum + w, 0) ?? 0;
|
|
@@ -1077,7 +1096,7 @@ function convertTableCell({ cell, styleResolver, context, isHeader, gridWidthPer
|
|
|
1077
1096
|
...cell.structuralChange.verticalMergeOriginal !== void 0 ? { verticalMergeOriginal: cell.structuralChange.verticalMergeOriginal } : {}
|
|
1078
1097
|
};
|
|
1079
1098
|
if (preserveVMergeRestart) attrs._preserveVMergeRestart = true;
|
|
1080
|
-
if (vMergeContinuationCells && vMergeContinuationCells.length > 0) attrs._docxVMergeContinuationCells = vMergeContinuationCells;
|
|
1099
|
+
if (vMergeContinuationCells && vMergeContinuationCells.length > 0) attrs._docxVMergeContinuationCells = transportTableCellsWithParagraphPropertySources(vMergeContinuationCells);
|
|
1081
1100
|
const contentNodes = [];
|
|
1082
1101
|
for (const content of cell.content) if (content.type === "paragraph") contentNodes.push(...convertParagraphWithTextBoxes(content, styleResolver, {
|
|
1083
1102
|
textBoxGroupId: context.nextTextBoxGroupId(),
|
|
@@ -1255,12 +1274,74 @@ const PAGE_BREAK_PARAGRAPH_OWNERS = {
|
|
|
1255
1274
|
};
|
|
1256
1275
|
function assertSourceContainerHasNoPageBreakRun(content, owner, sourceDescendants) {
|
|
1257
1276
|
if (!sourceDescendants.containsPageBreakRun(content)) return;
|
|
1277
|
+
if (owner === "table-cell" && hasSingleLeadingTableCellPageBreak(content)) return;
|
|
1258
1278
|
throw new UnsupportedDocxToProseMirrorConversionError({
|
|
1259
1279
|
message: `${PAGE_BREAK_CONTAINER_DESCRIPTIONS[owner]} cannot be represented in the editor model`,
|
|
1260
1280
|
owner,
|
|
1261
1281
|
contentType: "break"
|
|
1262
1282
|
});
|
|
1263
1283
|
}
|
|
1284
|
+
/** A leading page-break run in a cell advances the whole row during pagination. This
|
|
1285
|
+
* narrow shape has a lossless PM representation and a row-wide layout
|
|
1286
|
+
* projection; interior breaks still require table-fragment ownership. */
|
|
1287
|
+
function hasSingleLeadingTableCellPageBreak(content) {
|
|
1288
|
+
const paragraph = content.length === 1 && content[0]?.type === "paragraph" ? content[0] : void 0;
|
|
1289
|
+
return paragraph !== void 0 && hasSingleLeadingParagraphPageBreak(paragraph);
|
|
1290
|
+
}
|
|
1291
|
+
const scanLeadingPageBreakRun = (run, scan) => {
|
|
1292
|
+
for (const content of run.content) {
|
|
1293
|
+
if (content.type === "renderedPageBreak") continue;
|
|
1294
|
+
if (content.type === "break" && content.breakType === "page") {
|
|
1295
|
+
scan.pageBreaks += 1;
|
|
1296
|
+
continue;
|
|
1297
|
+
}
|
|
1298
|
+
if (scan.pageBreaks === 0) scan.contentBeforeBreak = true;
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
const scanLeadingPageBreakContent = (content, scan) => {
|
|
1302
|
+
switch (content.type) {
|
|
1303
|
+
case "run":
|
|
1304
|
+
scanLeadingPageBreakRun(content, scan);
|
|
1305
|
+
return;
|
|
1306
|
+
case "hyperlink":
|
|
1307
|
+
for (const child of content.children) scanLeadingPageBreakContent(child, scan);
|
|
1308
|
+
return;
|
|
1309
|
+
case "insertion":
|
|
1310
|
+
case "deletion":
|
|
1311
|
+
case "moveFrom":
|
|
1312
|
+
case "moveTo":
|
|
1313
|
+
case "inlineSdt":
|
|
1314
|
+
for (const child of content.content) scanLeadingPageBreakContent(child, scan);
|
|
1315
|
+
return;
|
|
1316
|
+
case "simpleField":
|
|
1317
|
+
for (const child of content.content) scanLeadingPageBreakContent(child, scan);
|
|
1318
|
+
return;
|
|
1319
|
+
case "complexField":
|
|
1320
|
+
for (const child of content.fieldResult) scanLeadingPageBreakRun(child, scan);
|
|
1321
|
+
return;
|
|
1322
|
+
case "bookmarkStart":
|
|
1323
|
+
case "bookmarkEnd":
|
|
1324
|
+
case "commentRangeStart":
|
|
1325
|
+
case "commentRangeEnd":
|
|
1326
|
+
case "moveFromRangeStart":
|
|
1327
|
+
case "moveFromRangeEnd":
|
|
1328
|
+
case "moveToRangeStart":
|
|
1329
|
+
case "moveToRangeEnd": return;
|
|
1330
|
+
case "commentReference":
|
|
1331
|
+
case "mathEquation":
|
|
1332
|
+
if (scan.pageBreaks === 0) scan.contentBeforeBreak = true;
|
|
1333
|
+
return;
|
|
1334
|
+
default: return content;
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
const hasSingleLeadingParagraphPageBreak = (paragraph) => {
|
|
1338
|
+
const scan = {
|
|
1339
|
+
contentBeforeBreak: false,
|
|
1340
|
+
pageBreaks: 0
|
|
1341
|
+
};
|
|
1342
|
+
for (const content of paragraph.content) scanLeadingPageBreakContent(content, scan);
|
|
1343
|
+
return !scan.contentBeforeBreak && scan.pageBreaks === 1;
|
|
1344
|
+
};
|
|
1264
1345
|
function assertParagraphPageBreakCanBeProjected({ paragraph, attrs, effectiveFrame, sourceDescendants }) {
|
|
1265
1346
|
const sourceFeatures = sourceDescendants.paragraphFeatures(paragraph);
|
|
1266
1347
|
if (!sourceFeatures.hasPageBreakRun) return;
|
|
@@ -1270,6 +1351,7 @@ function assertParagraphPageBreakCanBeProjected({ paragraph, attrs, effectiveFra
|
|
|
1270
1351
|
hasTextBoxAnchor: sourceFeatures.hasTextBoxShape && !sourceFeatures.pageBreakSharesTextBoxShape
|
|
1271
1352
|
});
|
|
1272
1353
|
if (disposition.status === "supported") return;
|
|
1354
|
+
if (disposition.reason !== "textBoxAnchor" && hasSingleLeadingParagraphPageBreak(paragraph)) return;
|
|
1273
1355
|
throw new UnsupportedDocxToProseMirrorConversionError({
|
|
1274
1356
|
message: disposition.message,
|
|
1275
1357
|
owner: PAGE_BREAK_PARAGRAPH_OWNERS[disposition.reason],
|
|
@@ -2065,6 +2147,7 @@ function textBoxFromShape(shape, textBody) {
|
|
|
2065
2147
|
if (shape.wrap) textBox.wrap = shape.wrap;
|
|
2066
2148
|
if (shape.fill) textBox.fill = shape.fill;
|
|
2067
2149
|
if (shape.outline) textBox.outline = shape.outline;
|
|
2150
|
+
if (shape.transform) textBox.transform = shape.transform;
|
|
2068
2151
|
if (textBody.margins) textBox.margins = textBody.margins;
|
|
2069
2152
|
if (textBody.autoFit) textBox.autoFit = textBody.autoFit;
|
|
2070
2153
|
if (textBody.textWrap) textBox.textWrap = textBody.textWrap;
|
|
@@ -2089,6 +2172,14 @@ function convertTextBox(textBox, styleResolver, options) {
|
|
|
2089
2172
|
if (textBox.outline.color?.rgb) outlineColor = `#${textBox.outline.color.rgb}`;
|
|
2090
2173
|
outlineStyle = textBox.outline.style || "solid";
|
|
2091
2174
|
}
|
|
2175
|
+
let transform;
|
|
2176
|
+
if (textBox.transform) {
|
|
2177
|
+
const transforms = [];
|
|
2178
|
+
if (textBox.transform.rotation) transforms.push(`rotate(${textBox.transform.rotation}deg)`);
|
|
2179
|
+
if (textBox.transform.flipH) transforms.push("scaleX(-1)");
|
|
2180
|
+
if (textBox.transform.flipV) transforms.push("scaleY(-1)");
|
|
2181
|
+
if (transforms.length > 0) transform = transforms.join(" ");
|
|
2182
|
+
}
|
|
2092
2183
|
const marginTop = textBox.margins?.top !== void 0 ? emuToPixels(textBox.margins.top) : 4;
|
|
2093
2184
|
const marginBottom = textBox.margins?.bottom !== void 0 ? emuToPixels(textBox.margins.bottom) : 4;
|
|
2094
2185
|
const marginLeft = textBox.margins?.left !== void 0 ? emuToPixels(textBox.margins.left) : 7;
|
|
@@ -2151,6 +2242,7 @@ function convertTextBox(textBox, styleResolver, options) {
|
|
|
2151
2242
|
outlineWidth,
|
|
2152
2243
|
outlineColor,
|
|
2153
2244
|
outlineStyle,
|
|
2245
|
+
transform,
|
|
2154
2246
|
marginTop,
|
|
2155
2247
|
marginBottom,
|
|
2156
2248
|
marginLeft,
|
|
@@ -2167,6 +2259,7 @@ function convertTextBox(textBox, styleResolver, options) {
|
|
|
2167
2259
|
_docxPlacement: options.placement,
|
|
2168
2260
|
_docxGroupId: options.groupId,
|
|
2169
2261
|
_docxAnchorId: options.anchorId,
|
|
2262
|
+
_docxTextBodyContentState: textBox.content.length === 0 ? { type: "source-empty" } : { type: "authored" },
|
|
2170
2263
|
_docxTrackedChange: options.trackedChange,
|
|
2171
2264
|
_docxInlineSdts: options.inlineSdts.length > 0 ? options.inlineSdts : void 0
|
|
2172
2265
|
}, contentNodes);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR } from "../../../docx/paragraphPropertySource.js";
|
|
1
2
|
import { createNodeExtension } from "../create.js";
|
|
2
3
|
//#region src/prosemirror/extensions/core/DocExtension.ts
|
|
3
4
|
/**
|
|
@@ -8,6 +9,7 @@ const DocExtension = createNodeExtension({
|
|
|
8
9
|
schemaNodeName: "doc",
|
|
9
10
|
nodeSpec: {
|
|
10
11
|
attrs: {
|
|
12
|
+
[PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR]: { default: null },
|
|
11
13
|
_finalSectionStart: { default: null },
|
|
12
14
|
_adjustLineHeightInTable: { default: false }
|
|
13
15
|
},
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR } from "../../../docx/paragraphPropertySource.js";
|
|
1
2
|
import { PARAGRAPH_ALIGNMENT_VALUES } from "../../../types/documentEnumValues.js";
|
|
2
3
|
import { paragraphToStyle } from "../../../utils/formatToStyle.js";
|
|
3
4
|
import { collectHeadings } from "../../../utils/headingCollector.js";
|
|
@@ -184,6 +185,7 @@ const paragraphNodeSpec = {
|
|
|
184
185
|
group: "block",
|
|
185
186
|
attrs: {
|
|
186
187
|
paraId: { default: null },
|
|
188
|
+
[PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR]: { default: null },
|
|
187
189
|
idStability: { default: void 0 },
|
|
188
190
|
textId: { default: null },
|
|
189
191
|
alignment: { default: null },
|
|
@@ -2,7 +2,11 @@ import { Extension } from "../types.js";
|
|
|
2
2
|
import { EditorState, PluginKey, Transaction } from "prosemirror-state";
|
|
3
3
|
import { Node } from "prosemirror-model";
|
|
4
4
|
//#region src/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts
|
|
5
|
-
|
|
5
|
+
type ParagraphPropertySourceSeed = {
|
|
6
|
+
contract: string | null;
|
|
7
|
+
tokens: ReadonlySet<string>;
|
|
8
|
+
};
|
|
9
|
+
declare const paraIdAllocatorKey: PluginKey<ParagraphPropertySourceSeed>;
|
|
6
10
|
/**
|
|
7
11
|
* Transaction meta asking this plugin to derive the ids it mints from a
|
|
8
12
|
* caller-supplied seed instead of `Math.random()`.
|