@stll/folio-core 0.47.1 → 0.47.3
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/notePropertiesParser.js +10 -2
- package/dist/docx/numberingParser.d.ts +2 -10
- package/dist/docx/numberingParser.js +6 -14
- package/dist/docx/paragraphProperties.js +3 -1
- package/dist/docx/runParser.js +10 -4
- package/dist/docx/serializer/numberingSerializer.js +2 -3
- package/dist/docx/serializer/paragraphSerializer.js +4 -4
- package/dist/docx/serializer/runSerializer.js +5 -2
- package/dist/docx/serializer/sectionPropertiesSerializer.js +10 -2
- package/dist/docx/serializer/tableSerializer.js +2 -1
- package/dist/docx/tableParser.js +3 -1
- package/dist/internal/paragraphFormattingSerialization.d.ts +1 -1
- package/dist/internal/paragraphFormattingSerialization.js +19 -13
- package/dist/prosemirror/attrs/index.js +21 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +31 -15
- package/dist/prosemirror/conversion/toProseDoc.js +13 -3
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +2 -1
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +7 -3
- package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +5 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.js +40 -1
- package/dist/prosemirror/paragraphFormattingProvenance.d.ts +3 -0
- package/dist/prosemirror/paragraphFormattingProvenance.js +1 -0
- package/dist/prosemirror/runFormattingInlineCarriers.js +18 -1
- package/dist/prosemirror/schema/marks.d.ts +4 -0
- package/dist/prosemirror/schema/nodes.d.ts +11 -0
- package/dist/utils/tableOperations.d.ts +1 -1
- package/package.json +2 -2
|
@@ -52,7 +52,11 @@ function parseFootnoteProperties(element) {
|
|
|
52
52
|
const numFmtEl = findChild(element, "w", "numFmt");
|
|
53
53
|
if (numFmtEl) {
|
|
54
54
|
const numFmt = parseNumberFormat(getAttribute(numFmtEl, "w", "val"));
|
|
55
|
-
if (numFmt !== void 0)
|
|
55
|
+
if (numFmt !== void 0) {
|
|
56
|
+
props.numFmt = numFmt;
|
|
57
|
+
const format = getAttribute(numFmtEl, "w", "format");
|
|
58
|
+
if (format !== null) props.numFmtFormat = format;
|
|
59
|
+
}
|
|
56
60
|
}
|
|
57
61
|
const numStartEl = findChild(element, "w", "numStart");
|
|
58
62
|
if (numStartEl) {
|
|
@@ -81,7 +85,11 @@ function parseEndnoteProperties(element) {
|
|
|
81
85
|
const numFmtEl = findChild(element, "w", "numFmt");
|
|
82
86
|
if (numFmtEl) {
|
|
83
87
|
const numFmt = parseNumberFormat(getAttribute(numFmtEl, "w", "val"));
|
|
84
|
-
if (numFmt !== void 0)
|
|
88
|
+
if (numFmt !== void 0) {
|
|
89
|
+
props.numFmt = numFmt;
|
|
90
|
+
const format = getAttribute(numFmtEl, "w", "format");
|
|
91
|
+
if (format !== null) props.numFmtFormat = format;
|
|
92
|
+
}
|
|
85
93
|
}
|
|
86
94
|
const numStartEl = findChild(element, "w", "numStart");
|
|
87
95
|
if (numStartEl) {
|
|
@@ -24,7 +24,7 @@ type NumberingMap = {
|
|
|
24
24
|
* @returns NumberingMap with definitions and helper functions
|
|
25
25
|
*/
|
|
26
26
|
declare function parseNumbering(numberingXml: string | null): NumberingMap;
|
|
27
|
-
/** A `w:numFmt` as the model holds it:
|
|
27
|
+
/** A `w:numFmt` as the model holds it: its token and authored format metadata. */
|
|
28
28
|
type ResolvedNumFmt = Pick<document_d_exports.ListLevel, "numFmt" | "numFmtFormat">;
|
|
29
29
|
/**
|
|
30
30
|
* The width a `custom` format's zero-padded first token counts to, or
|
|
@@ -35,14 +35,6 @@ type ResolvedNumFmt = Pick<document_d_exports.ListLevel, "numFmt" | "numFmtForma
|
|
|
35
35
|
* way; the width is the digit count of the first token, clamped to five.
|
|
36
36
|
*/
|
|
37
37
|
declare const customNumberFormatPadWidth: (format: string | undefined) => number | undefined;
|
|
38
|
-
/**
|
|
39
|
-
* The `@w:format` a level's `w:numFmt` writes, which only `custom` carries.
|
|
40
|
-
*
|
|
41
|
-
* `custom` is the reserved member that defers to another attribute, so the
|
|
42
|
-
* comparison lives here, in the module the reserved-value registry names as
|
|
43
|
-
* its owner, rather than in the serializer that needs the answer.
|
|
44
|
-
*/
|
|
45
|
-
declare const customNumberFormatOf: (level: ResolvedNumFmt) => string | undefined;
|
|
46
38
|
/** What the marker renderer counts a level in; `custom` decides by pad width. */
|
|
47
39
|
declare const counterFormatOf: (level: ResolvedNumFmt) => document_d_exports.CounterFormat;
|
|
48
40
|
/** {@link MARKER_ALIGNMENT} for a level that may not state a justification. */
|
|
@@ -92,4 +84,4 @@ declare function isBulletLevel(level: document_d_exports.ListLevel): boolean;
|
|
|
92
84
|
/** Whether a numbering level reserves horizontal space for a visible marker. */
|
|
93
85
|
declare const numberingLevelHasMarkerSlot: (level: Pick<document_d_exports.ListLevel, "numFmt">) => boolean;
|
|
94
86
|
//#endregion
|
|
95
|
-
export { NumberingMap, computeListRendering, counterFormatOf, createNumberingMap,
|
|
87
|
+
export { NumberingMap, computeListRendering, counterFormatOf, createNumberingMap, customNumberFormatPadWidth, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerAlignmentForLevel, markerFormattingFromLevel, numberingLevelHasMarkerSlot, padDecimal, parseNumbering, renderListMarker };
|
|
@@ -248,16 +248,16 @@ function parseListLevel(element) {
|
|
|
248
248
|
* Resolve a `<w:numFmt>` element, or null when it is absent or carries a token
|
|
249
249
|
* outside `ST_NumberFormat` (an mc:Fallback can then supply the rendering).
|
|
250
250
|
*
|
|
251
|
-
*
|
|
252
|
-
* decoded here into a synthetic `decimalZero{3,4,5}` the
|
|
253
|
-
* declare, which the serializer wrote back as a `w:val`
|
|
254
|
-
* The pad
|
|
251
|
+
* `@w:format` stays beside every admitted token exactly as authored. `custom`
|
|
252
|
+
* used to be decoded here into a synthetic `decimalZero{3,4,5}` the
|
|
253
|
+
* enumeration does not declare, which the serializer wrote back as a `w:val`
|
|
254
|
+
* no consumer can read. The pad-width interpretation belongs to the renderer,
|
|
255
|
+
* not to the model; preservation belongs to every schema-valid `CT_NumFmt`.
|
|
255
256
|
*/
|
|
256
257
|
function resolveNumFmt(numFmtEl) {
|
|
257
258
|
if (!numFmtEl) return null;
|
|
258
259
|
const numFmt = narrowEnum(getAttribute(numFmtEl, "w", "val"), NumberFormatSchema);
|
|
259
260
|
if (numFmt === void 0) return null;
|
|
260
|
-
if (numFmt !== "custom") return { numFmt };
|
|
261
261
|
const format = getAttribute(numFmtEl, "w", "format");
|
|
262
262
|
return format === null ? { numFmt } : {
|
|
263
263
|
numFmt,
|
|
@@ -293,14 +293,6 @@ const customNumberFormatPadWidth = (format) => {
|
|
|
293
293
|
if (!/^0+1$/u.test(firstToken)) return;
|
|
294
294
|
return Math.min(firstToken.length, 5);
|
|
295
295
|
};
|
|
296
|
-
/**
|
|
297
|
-
* The `@w:format` a level's `w:numFmt` writes, which only `custom` carries.
|
|
298
|
-
*
|
|
299
|
-
* `custom` is the reserved member that defers to another attribute, so the
|
|
300
|
-
* comparison lives here, in the module the reserved-value registry names as
|
|
301
|
-
* its owner, rather than in the serializer that needs the answer.
|
|
302
|
-
*/
|
|
303
|
-
const customNumberFormatOf = (level) => level.numFmt === "custom" ? level.numFmtFormat : void 0;
|
|
304
296
|
/** What the marker renderer counts a level in; `custom` decides by pad width. */
|
|
305
297
|
const counterFormatOf = (level) => {
|
|
306
298
|
if (level.numFmt !== "custom") return level.numFmt;
|
|
@@ -511,4 +503,4 @@ function isBulletLevel(level) {
|
|
|
511
503
|
/** Whether a numbering level reserves horizontal space for a visible marker. */
|
|
512
504
|
const numberingLevelHasMarkerSlot = (level) => level.numFmt !== "none";
|
|
513
505
|
//#endregion
|
|
514
|
-
export { computeListRendering, counterFormatOf, createNumberingMap,
|
|
506
|
+
export { computeListRendering, counterFormatOf, createNumberingMap, customNumberFormatPadWidth, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerAlignmentForLevel, markerFormattingFromLevel, numberingLevelHasMarkerSlot, padDecimal, parseNumbering, renderListMarker };
|
|
@@ -128,8 +128,10 @@ function parseParagraphProperties(pPr, theme) {
|
|
|
128
128
|
if (stated !== void 0) formatting.numPr = stated;
|
|
129
129
|
const numberingChange = findChild(child, "w", "numberingChange");
|
|
130
130
|
if (numberingChange) formatting.numberingChangeXml = captureVerbatimXml(numberingChange);
|
|
131
|
+
const numberingInsertion = findChild(child, "w", "ins");
|
|
132
|
+
if (numberingInsertion) formatting.numberingInsertionXml = captureVerbatimXml(numberingInsertion);
|
|
131
133
|
const statedLevel = parseNumericAttribute(findChild(child, "w", "ilvl"), "w", "val");
|
|
132
|
-
return stated !== void 0 || numberingChange !== null || statedLevel === -1;
|
|
134
|
+
return stated !== void 0 || numberingChange !== null || numberingInsertion !== null || statedLevel === -1;
|
|
133
135
|
}),
|
|
134
136
|
suppressLineNumbers: toggle("suppressLineNumbers", "suppressLineNumbers"),
|
|
135
137
|
pBdr: once("pBdr", (child) => {
|
package/dist/docx/runParser.js
CHANGED
|
@@ -16,7 +16,7 @@ import { resolveThemeFontRef } from "./themeParser.js";
|
|
|
16
16
|
import { parsePropertyChangeInfo } from "./trackedChangeInfo.js";
|
|
17
17
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
18
18
|
import { parseVmlImageContent, shouldPreserveRawVmlPict } from "./vmlImageParser.js";
|
|
19
|
-
import { WORDPROCESSINGML_NAMESPACE_URIS, cloneWithXmlnsDeclarations, findAllDeep, findChild, findChildren, getAttribute, getChildElements, getLocalName, getNamespaceUri, getTextContent, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, selectAlternateContentBranch } from "./xmlParser.js";
|
|
19
|
+
import { WORDPROCESSINGML_NAMESPACE_URIS, cloneWithXmlnsDeclarations, findAllDeep, findChild, findChildren, getAttribute, getChildElements, getLocalName, getNamespaceUri, getTextContent, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, parseOnOffAttribute, selectAlternateContentBranch } from "./xmlParser.js";
|
|
20
20
|
import { DRAWING_RAW_XML_MODES } from "@stll/docx-core/model";
|
|
21
21
|
//#region src/docx/runParser.ts
|
|
22
22
|
/**
|
|
@@ -486,19 +486,25 @@ function parseSymbolContent(element) {
|
|
|
486
486
|
* Parse footnote reference (w:footnoteReference)
|
|
487
487
|
*/
|
|
488
488
|
function parseFootnoteReference(element) {
|
|
489
|
-
|
|
489
|
+
const reference = {
|
|
490
490
|
type: "footnoteRef",
|
|
491
491
|
id: parseNumericAttribute(element, "w", "id") ?? 0
|
|
492
492
|
};
|
|
493
|
+
const customMarkFollows = parseOnOffAttribute(element, "w", "customMarkFollows");
|
|
494
|
+
if (customMarkFollows !== void 0) reference.customMarkFollows = customMarkFollows;
|
|
495
|
+
return reference;
|
|
493
496
|
}
|
|
494
497
|
/**
|
|
495
498
|
* Parse endnote reference (w:endnoteReference)
|
|
496
499
|
*/
|
|
497
500
|
function parseEndnoteReference(element) {
|
|
498
|
-
|
|
501
|
+
const reference = {
|
|
499
502
|
type: "endnoteRef",
|
|
500
503
|
id: parseNumericAttribute(element, "w", "id") ?? 0
|
|
501
504
|
};
|
|
505
|
+
const customMarkFollows = parseOnOffAttribute(element, "w", "customMarkFollows");
|
|
506
|
+
if (customMarkFollows !== void 0) reference.customMarkFollows = customMarkFollows;
|
|
507
|
+
return reference;
|
|
502
508
|
}
|
|
503
509
|
/**
|
|
504
510
|
* Parse field character (w:fldChar)
|
|
@@ -670,7 +676,7 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
|
|
|
670
676
|
case "cr":
|
|
671
677
|
contents.push({
|
|
672
678
|
type: "break",
|
|
673
|
-
|
|
679
|
+
sourceElement: "cr"
|
|
674
680
|
});
|
|
675
681
|
break;
|
|
676
682
|
case "AlternateContent": {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { serializeParagraphPropertySet } from "../../internal/paragraphFormattingSerialization.js";
|
|
2
2
|
import { serializePreservedAttributes } from "../attributeRemainder.js";
|
|
3
|
-
import { customNumberFormatOf } from "../numberingParser.js";
|
|
4
3
|
import { serializePartElement } from "./partNamespaces.js";
|
|
5
4
|
import { serializeTextFormatting } from "./textFormattingSerializer.js";
|
|
6
5
|
import { intAttr } from "./xmlUtils.js";
|
|
@@ -9,8 +8,8 @@ import { serializeSequenceChildren } from "@stll/docx-core/schema";
|
|
|
9
8
|
//#region src/docx/serializer/numberingSerializer.ts
|
|
10
9
|
/** Serialize one `w:lvl`. */
|
|
11
10
|
function serializeLevel(level) {
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
let numFmtFormat = "";
|
|
12
|
+
if (level.numFmtFormat !== void 0) numFmtFormat = ` w:format="${escapeXmlAttribute(level.numFmtFormat)}"`;
|
|
14
13
|
const pPr = level.pPr === void 0 ? "" : serializeParagraphPropertySet({ formatting: level.pPr }) || "<w:pPr/>";
|
|
15
14
|
const rPr = level.rPr === void 0 ? "" : serializeTextFormatting(level.rPr) || "<w:rPr/>";
|
|
16
15
|
const lvlTextAttrs = [`w:val="${escapeXmlAttribute(level.lvlText)}"`];
|
|
@@ -29,10 +29,10 @@ const RESERVED_PARAGRAPH_PROPERTY_CHILDREN = /* @__PURE__ */ new Set(["pPrChange
|
|
|
29
29
|
/**
|
|
30
30
|
* Revision records a captured `w:pPr` may not carry into a replay.
|
|
31
31
|
*
|
|
32
|
-
* `w:
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* The revision records inside `w:numPr` are not among them: the model holds
|
|
33
|
+
* `w:numberingChange` and `w:ins`, and the serializer writes both back, so both
|
|
34
|
+
* paths keep them. Refusing the capture used to force a rebuild that could not
|
|
35
|
+
* write the records, which turned a replay gate into a lost revision.
|
|
36
36
|
*/
|
|
37
37
|
const RESERVED_PARAGRAPH_CAPTURE_CHILDREN = /* @__PURE__ */ new Set([
|
|
38
38
|
...RESERVED_PARAGRAPH_PROPERTY_CHILDREN,
|
|
@@ -76,6 +76,7 @@ function serializeTabContent(content) {
|
|
|
76
76
|
* Serialize break content (w:br)
|
|
77
77
|
*/
|
|
78
78
|
function serializeBreakContent(content) {
|
|
79
|
+
if (content.sourceElement === "cr") return "<w:cr/>";
|
|
79
80
|
const attrs = [];
|
|
80
81
|
if (content.breakType === "page") attrs.push("w:type=\"page\"");
|
|
81
82
|
else if (content.breakType === "column") attrs.push("w:type=\"column\"");
|
|
@@ -94,8 +95,10 @@ function serializeSymbolContent(content) {
|
|
|
94
95
|
* Serialize footnote/endnote reference
|
|
95
96
|
*/
|
|
96
97
|
function serializeNoteReference(content) {
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
let customMarkFollows = "";
|
|
99
|
+
if (content.customMarkFollows !== void 0) customMarkFollows = ` w:customMarkFollows="${content.customMarkFollows ? "1" : "0"}"`;
|
|
100
|
+
if (content.type === "footnoteRef") return `<w:footnoteReference w:id="${content.id}"${customMarkFollows}/>`;
|
|
101
|
+
return `<w:endnoteReference w:id="${content.id}"${customMarkFollows}/>`;
|
|
99
102
|
}
|
|
100
103
|
/**
|
|
101
104
|
* Serialize field character (w:fldChar)
|
|
@@ -13,7 +13,11 @@ function serializeFootnoteProperties(props) {
|
|
|
13
13
|
if (!props) return "";
|
|
14
14
|
const parts = [];
|
|
15
15
|
if (props.position) parts.push(`<w:pos w:val="${props.position}"/>`);
|
|
16
|
-
if (props.numFmt)
|
|
16
|
+
if (props.numFmt) {
|
|
17
|
+
let format = "";
|
|
18
|
+
if (props.numFmtFormat !== void 0) format = ` w:format="${escapeXmlAttribute(props.numFmtFormat)}"`;
|
|
19
|
+
parts.push(`<w:numFmt w:val="${props.numFmt}"${format}/>`);
|
|
20
|
+
}
|
|
17
21
|
if (props.numStart !== void 0) parts.push(`<w:numStart w:val="${intAttr(props.numStart)}"/>`);
|
|
18
22
|
if (props.numRestart) parts.push(`<w:numRestart w:val="${props.numRestart}"/>`);
|
|
19
23
|
return parts.length > 0 ? `<w:footnotePr>${parts.join("")}</w:footnotePr>` : "";
|
|
@@ -25,7 +29,11 @@ function serializeEndnoteProperties(props) {
|
|
|
25
29
|
if (!props) return "";
|
|
26
30
|
const parts = [];
|
|
27
31
|
if (props.position) parts.push(`<w:pos w:val="${props.position}"/>`);
|
|
28
|
-
if (props.numFmt)
|
|
32
|
+
if (props.numFmt) {
|
|
33
|
+
let format = "";
|
|
34
|
+
if (props.numFmtFormat !== void 0) format = ` w:format="${escapeXmlAttribute(props.numFmtFormat)}"`;
|
|
35
|
+
parts.push(`<w:numFmt w:val="${props.numFmt}"${format}/>`);
|
|
36
|
+
}
|
|
29
37
|
if (props.numStart !== void 0) parts.push(`<w:numStart w:val="${intAttr(props.numStart)}"/>`);
|
|
30
38
|
if (props.numRestart) parts.push(`<w:numRestart w:val="${props.numRestart}"/>`);
|
|
31
39
|
return parts.length > 0 ? `<w:endnotePr>${parts.join("")}</w:endnotePr>` : "";
|
|
@@ -529,10 +529,11 @@ function serializeCellContent(content, serializeParagraph) {
|
|
|
529
529
|
*/
|
|
530
530
|
function serializeTableCell(cell, serializeParagraph) {
|
|
531
531
|
const parts = [];
|
|
532
|
+
const id = cell.id === void 0 ? "" : ` w:id="${escapeXmlAttribute(cell.id)}"`;
|
|
532
533
|
const tcPrXml = serializeTableCellFormatting(cell.formatting, cell.propertyChanges, cell.structuralChange);
|
|
533
534
|
if (tcPrXml) parts.push(tcPrXml);
|
|
534
535
|
parts.push(serializeCellContent(cell.content, serializeParagraph));
|
|
535
|
-
return `<w:tc>${parts.join("")}</w:tc>`;
|
|
536
|
+
return `<w:tc${id}>${parts.join("")}</w:tc>`;
|
|
536
537
|
}
|
|
537
538
|
/**
|
|
538
539
|
* A container's verbatim sink and its bookmark markers, as one positioned list.
|
package/dist/docx/tableParser.js
CHANGED
|
@@ -12,7 +12,7 @@ import { TABLE_LOOK_FLAGS } from "./tableLook.js";
|
|
|
12
12
|
import { parsePropertyChangeInfo, parseTrackedChangeInfo } from "./trackedChangeInfo.js";
|
|
13
13
|
import { percentageSpelling, transitionalSlotEncoding } from "./transitionalSpelling.js";
|
|
14
14
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
15
|
-
import { cloneElement, findChild, findChildren, findWordprocessingChild, getAttribute, getLocalName, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, parseOnOffAttribute, parseTableMeasurementValue, selectAlternateContentBranch } from "./xmlParser.js";
|
|
15
|
+
import { WORDPROCESSINGML_NAMESPACE_URIS, cloneElement, findChild, findChildren, findWordprocessingChild, getAttribute, getAttributeByNamespaceUri, getLocalName, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, parseOnOffAttribute, parseTableMeasurementValue, selectAlternateContentBranch } from "./xmlParser.js";
|
|
16
16
|
//#region src/docx/tableParser.ts
|
|
17
17
|
/**
|
|
18
18
|
* Sanity cap on `w:gridSpan` (and the derived table column count). Word's
|
|
@@ -858,6 +858,8 @@ function parseTableCell(tcElement, styles, theme, numbering, rels, media, option
|
|
|
858
858
|
type: "tableCell",
|
|
859
859
|
content: []
|
|
860
860
|
};
|
|
861
|
+
const id = getAttributeByNamespaceUri(tcElement, WORDPROCESSINGML_NAMESPACE_URIS, "id");
|
|
862
|
+
if (id !== null) cell.id = id;
|
|
861
863
|
const tcPrElement = findChild(tcElement, "w", "tcPr");
|
|
862
864
|
const formatting = parseTableCellProperties(tcPrElement);
|
|
863
865
|
if (formatting) cell.formatting = formatting;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { ParagraphNumberingOverride } from "@stll/docx-core/model";
|
|
3
3
|
//#region src/internal/paragraphFormattingSerialization.d.ts
|
|
4
|
-
type ClassifiedParagraphFormattingField = "alignment" | "bidi" | "kinsoku" | "overflowPunctuation" | "spaceBefore" | "spaceAfter" | "lineSpacing" | "lineSpacingRule" | "snapToGrid" | "beforeAutospacing" | "afterAutospacing" | "spacingExplicit" | "spacingPreservedAttributes" | "indentLeft" | "indentRight" | "indentFirstLine" | "hangingIndent" | "indentPreservedAttributes" | "borders" | "shading" | "tabs" | "keepNext" | "keepLines" | "widowControl" | "pageBreakBefore" | "contextualSpacing" | "numPr" | "numPrFromStyle" | "numberingChangeXml" | "outlineLevel" | "styleId" | "frame" | "suppressLineNumbers" | "suppressAutoHyphens" | "runProperties" | "runInWithNext" | "preserved";
|
|
4
|
+
type ClassifiedParagraphFormattingField = "alignment" | "bidi" | "kinsoku" | "overflowPunctuation" | "spaceBefore" | "spaceAfter" | "lineSpacing" | "lineSpacingRule" | "snapToGrid" | "beforeAutospacing" | "afterAutospacing" | "spacingExplicit" | "spacingPreservedAttributes" | "indentLeft" | "indentRight" | "indentFirstLine" | "hangingIndent" | "indentPreservedAttributes" | "borders" | "shading" | "tabs" | "keepNext" | "keepLines" | "widowControl" | "pageBreakBefore" | "contextualSpacing" | "numPr" | "numPrFromStyle" | "numberingChangeXml" | "numberingInsertionXml" | "outlineLevel" | "styleId" | "frame" | "suppressLineNumbers" | "suppressAutoHyphens" | "runProperties" | "runInWithNext" | "preserved";
|
|
5
5
|
type ExhaustiveParagraphFormatting = document_d_exports.ExhaustiveFields<document_d_exports.ParagraphFormatting, ClassifiedParagraphFormattingField>;
|
|
6
6
|
/**
|
|
7
7
|
* Whether resolved numbering still belongs to the paragraph's style tier.
|
|
@@ -72,32 +72,30 @@ const serializeIndentation = (formatting) => {
|
|
|
72
72
|
const written = serializePreservedAttributes(attrs, formatting.indentPreservedAttributes);
|
|
73
73
|
return written.length === 0 ? "" : `<w:ind ${written.join(" ")}/>`;
|
|
74
74
|
};
|
|
75
|
-
|
|
76
|
-
* `w:numPr`, with the tracked record of the numbering it replaced.
|
|
77
|
-
*
|
|
78
|
-
* `w:numberingChange` is the revision a reviewer's numbering change left
|
|
79
|
-
* behind. It is history: nothing in the model derives it, and a rebuilt
|
|
80
|
-
* `w:numPr` that drops it discards the revision silently. It is written even
|
|
81
|
-
* when the paragraph's own numbering reference is gone, because a change
|
|
82
|
-
* record with nothing left to describe is still a record — the schema declares
|
|
83
|
-
* it last in `CT_NumPr`, after `w:ilvl` and `w:numId`.
|
|
84
|
-
*/
|
|
85
|
-
const serializeNumbering = (numPr, numberingChangeXml) => {
|
|
75
|
+
const serializeNumbering = ({ numPr, numberingChangeXml, numberingInsertionXml }) => {
|
|
86
76
|
const slots = numPr === void 0 ? {} : paragraphNumberingSlots(numPr);
|
|
87
77
|
const parts = [];
|
|
88
78
|
if (slots.ilvl !== void 0) parts.push(`<w:ilvl w:val="${intAttr(slots.ilvl)}"/>`);
|
|
89
79
|
if (slots.numId !== void 0) parts.push(`<w:numId w:val="${intAttr(slots.numId)}"/>`);
|
|
90
80
|
const change = replayableNumberingChangeXml(numberingChangeXml);
|
|
91
81
|
if (change !== null) parts.push(change);
|
|
82
|
+
const insertion = replayableNumberingInsertionXml(numberingInsertionXml);
|
|
83
|
+
if (insertion !== null) parts.push(insertion);
|
|
92
84
|
return parts.length === 0 ? "" : `<w:numPr>${parts.join("")}</w:numPr>`;
|
|
93
85
|
};
|
|
94
86
|
const NUMBERING_CHANGE_ROOT_NAME = /* @__PURE__ */ new Set(["numberingChange"]);
|
|
87
|
+
const NUMBERING_INSERTION_ROOT_NAME = /* @__PURE__ */ new Set(["ins"]);
|
|
95
88
|
const WORDPROCESSINGML_NAMESPACE = /* @__PURE__ */ new Set([NAMESPACES.w]);
|
|
96
89
|
const replayableNumberingChangeXml = (numberingChangeXml) => sanitizeCapturedXmlElement(numberingChangeXml, {
|
|
97
90
|
allowedLocalNames: NUMBERING_CHANGE_ROOT_NAME,
|
|
98
91
|
allowedNamespaceUris: WORDPROCESSINGML_NAMESPACE,
|
|
99
92
|
inheritedNamespaceScope: OOXML_NAMESPACE_SCOPE
|
|
100
93
|
});
|
|
94
|
+
const replayableNumberingInsertionXml = (numberingInsertionXml) => sanitizeCapturedXmlElement(numberingInsertionXml, {
|
|
95
|
+
allowedLocalNames: NUMBERING_INSERTION_ROOT_NAME,
|
|
96
|
+
allowedNamespaceUris: WORDPROCESSINGML_NAMESPACE,
|
|
97
|
+
inheritedNamespaceScope: OOXML_NAMESPACE_SCOPE
|
|
98
|
+
});
|
|
101
99
|
const serializeFrameProperties = (frame) => {
|
|
102
100
|
if (!frame) return "";
|
|
103
101
|
const { dropCap, lines, width, height, hSpace, vSpace, hAnchor, vAnchor, x, y, xAlign, yAlign, wrap, preservedAttributes } = frame;
|
|
@@ -152,7 +150,7 @@ const paragraphMarkPropertiesInner = (runProperties, runInWithNext) => {
|
|
|
152
150
|
*/
|
|
153
151
|
const modelParagraphFormattingEmission = (input) => {
|
|
154
152
|
if (!input) return {};
|
|
155
|
-
const { alignment, bidi, kinsoku, overflowPunctuation, spaceBefore, spaceAfter, lineSpacing, lineSpacingRule, snapToGrid, beforeAutospacing, afterAutospacing, spacingExplicit, spacingPreservedAttributes, indentLeft, indentRight, indentFirstLine, hangingIndent, indentPreservedAttributes, borders, shading, tabs, keepNext, keepLines, widowControl, pageBreakBefore, contextualSpacing, numPr, numPrFromStyle, numberingChangeXml, outlineLevel, styleId, frame, suppressLineNumbers, suppressAutoHyphens, runProperties, runInWithNext, preserved } = input;
|
|
153
|
+
const { alignment, bidi, kinsoku, overflowPunctuation, spaceBefore, spaceAfter, lineSpacing, lineSpacingRule, snapToGrid, beforeAutospacing, afterAutospacing, spacingExplicit, spacingPreservedAttributes, indentLeft, indentRight, indentFirstLine, hangingIndent, indentPreservedAttributes, borders, shading, tabs, keepNext, keepLines, widowControl, pageBreakBefore, contextualSpacing, numPr, numPrFromStyle, numberingChangeXml, numberingInsertionXml, outlineLevel, styleId, frame, suppressLineNumbers, suppressAutoHyphens, runProperties, runInWithNext, preserved } = input;
|
|
156
154
|
modelSpacingProvenance(spacingExplicit);
|
|
157
155
|
const propertiesXml = serializeSequenceChildren({
|
|
158
156
|
container: "paragraph-properties",
|
|
@@ -164,7 +162,15 @@ const modelParagraphFormattingEmission = (input) => {
|
|
|
164
162
|
["pageBreakBefore", serializeOnOffElement(pageBreakBefore, "pageBreakBefore")],
|
|
165
163
|
["framePr", serializeFrameProperties(frame)],
|
|
166
164
|
["widowControl", serializeOnOffElement(widowControl, "widowControl")],
|
|
167
|
-
["numPr", isStyleSourcedParagraphNumbering(numPr, numPrFromStyle) ? serializeNumbering(
|
|
165
|
+
["numPr", isStyleSourcedParagraphNumbering(numPr, numPrFromStyle) ? serializeNumbering({
|
|
166
|
+
numPr: void 0,
|
|
167
|
+
numberingChangeXml,
|
|
168
|
+
numberingInsertionXml
|
|
169
|
+
}) : serializeNumbering({
|
|
170
|
+
numPr,
|
|
171
|
+
numberingChangeXml,
|
|
172
|
+
numberingInsertionXml
|
|
173
|
+
})],
|
|
168
174
|
["suppressLineNumbers", serializeOnOffElement(suppressLineNumbers, "suppressLineNumbers")],
|
|
169
175
|
["pBdr", serializeParagraphBorders(borders)],
|
|
170
176
|
["shd", serializeShading(shading)],
|
|
@@ -89,6 +89,7 @@ const TEXT_BOX_TRACKED_CHANGE_TYPES = [
|
|
|
89
89
|
"moveTo"
|
|
90
90
|
];
|
|
91
91
|
const HARD_BREAK_TYPES = ["column", "textWrapping"];
|
|
92
|
+
const HARD_BREAK_SOURCE_ELEMENTS = ["br", "cr"];
|
|
92
93
|
const PAGE_BREAK_CLEAR_VALUES = [
|
|
93
94
|
"none",
|
|
94
95
|
"left",
|
|
@@ -269,8 +270,19 @@ const readHardBreakAttrs = (node) => {
|
|
|
269
270
|
const attrs = attrsRecord(node.attrs);
|
|
270
271
|
const issues = [];
|
|
271
272
|
expectNodeType(node, "hardBreak", issues);
|
|
273
|
+
optionalOneOf(attrs, "sourceElement", "hardBreak.attrs.sourceElement", issues, HARD_BREAK_SOURCE_ELEMENTS);
|
|
272
274
|
optionalOneOf(attrs, "breakType", "hardBreak.attrs.breakType", issues, HARD_BREAK_TYPES);
|
|
273
275
|
optionalOneOf(attrs, "clear", "hardBreak.attrs.clear", issues, PAGE_BREAK_CLEAR_VALUES);
|
|
276
|
+
if (attrs["sourceElement"] === "cr") {
|
|
277
|
+
if (attrs["breakType"] !== void 0 && attrs["breakType"] !== null) issues.push({
|
|
278
|
+
path: "hardBreak.attrs.breakType",
|
|
279
|
+
message: "Not allowed on a carriage-return element."
|
|
280
|
+
});
|
|
281
|
+
if (attrs["clear"] !== void 0 && attrs["clear"] !== null) issues.push({
|
|
282
|
+
path: "hardBreak.attrs.clear",
|
|
283
|
+
message: "Not allowed on a carriage-return element."
|
|
284
|
+
});
|
|
285
|
+
}
|
|
274
286
|
return attrsResult(attrs, issues);
|
|
275
287
|
};
|
|
276
288
|
const expectHardBreakAttrs = (node) => expectCachedNodeAttrs(node, hardBreakAttrsCache, readHardBreakAttrs, "hard break attrs");
|
|
@@ -386,6 +398,7 @@ const readTableCellAttrs = (node) => {
|
|
|
386
398
|
const attrs = attrsRecord(node.attrs);
|
|
387
399
|
const issues = [];
|
|
388
400
|
expectNodeTypeOneOf(node, ["tableCell", "tableHeader"], issues);
|
|
401
|
+
optionalString(attrs, "_docxCellId", "tableCell.attrs._docxCellId", issues);
|
|
389
402
|
requiredNumber(attrs, "colspan", "tableCell.attrs.colspan", issues);
|
|
390
403
|
requiredNumber(attrs, "rowspan", "tableCell.attrs.rowspan", issues);
|
|
391
404
|
optionalOneOf(attrs, "_omittedGridSlot", "tableCell.attrs._omittedGridSlot", issues, OMITTED_GRID_SLOT_VALUES);
|
|
@@ -767,6 +780,7 @@ const readFootnoteRefMarkAttrs = (mark) => {
|
|
|
767
780
|
requiredStringOrNumber(attrs, "id", "footnoteRef.attrs.id", issues);
|
|
768
781
|
optionalOneOf(attrs, "noteType", "footnoteRef.attrs.noteType", issues, NOTE_TYPES);
|
|
769
782
|
optionalOneOf(attrs, "vertAlign", "footnoteRef.attrs.vertAlign", issues, NOTE_REF_VERT_ALIGNS);
|
|
783
|
+
optionalBoolean(attrs, "customMarkFollows", "footnoteRef.attrs.customMarkFollows", issues);
|
|
770
784
|
return attrsResult(attrs, issues);
|
|
771
785
|
};
|
|
772
786
|
const expectFootnoteRefMarkAttrs = (mark) => expectCachedMarkAttrs(mark, footnoteRefAttrsCache, readFootnoteRefMarkAttrs, "footnote reference attrs");
|
|
@@ -1018,6 +1032,9 @@ const readHyperlinkMarkAttrs = (mark) => {
|
|
|
1018
1032
|
requiredString(attrs, "href", "hyperlink.attrs.href", issues);
|
|
1019
1033
|
optionalString(attrs, "tooltip", "hyperlink.attrs.tooltip", issues);
|
|
1020
1034
|
optionalString(attrs, "rId", "hyperlink.attrs.rId", issues);
|
|
1035
|
+
optionalString(attrs, "target", "hyperlink.attrs.target", issues);
|
|
1036
|
+
optionalBoolean(attrs, "history", "hyperlink.attrs.history", issues);
|
|
1037
|
+
optionalString(attrs, "docLocation", "hyperlink.attrs.docLocation", issues);
|
|
1021
1038
|
validateNonNegativeInteger(attrs["_docxHyperlinkIndex"], "hyperlink.attrs._docxHyperlinkIndex", issues);
|
|
1022
1039
|
return attrsResult(attrs, issues);
|
|
1023
1040
|
};
|
|
@@ -2107,6 +2124,7 @@ const validateParagraphFormatting = (value, path, issues) => {
|
|
|
2107
2124
|
optionalOneOf(value, "lineSpacingRule", `${path}.lineSpacingRule`, issues, LINE_SPACING_RULE_VALUES);
|
|
2108
2125
|
optionalString(value, "styleId", `${path}.styleId`, issues);
|
|
2109
2126
|
optionalString(value, "numberingChangeXml", `${path}.numberingChangeXml`, issues);
|
|
2127
|
+
optionalString(value, "numberingInsertionXml", `${path}.numberingInsertionXml`, issues);
|
|
2110
2128
|
optionalOneOf(value, "listNumFmt", `${path}.listNumFmt`, issues, COUNTER_FORMAT_VALUES);
|
|
2111
2129
|
optionalString(value, "listMarker", `${path}.listMarker`, issues);
|
|
2112
2130
|
optionalString(value, "listMarkerTemplate", `${path}.listMarkerTemplate`, issues);
|
|
@@ -2612,6 +2630,9 @@ const optionalEmptyHyperlinkArray = (value, issues) => {
|
|
|
2612
2630
|
optionalString(item, "anchor", `${itemPath}.anchor`, issues);
|
|
2613
2631
|
optionalString(item, "tooltip", `${itemPath}.tooltip`, issues);
|
|
2614
2632
|
optionalString(item, "rId", `${itemPath}.rId`, issues);
|
|
2633
|
+
optionalString(item, "target", `${itemPath}.target`, issues);
|
|
2634
|
+
optionalBoolean(item, "history", `${itemPath}.history`, issues);
|
|
2635
|
+
optionalString(item, "docLocation", `${itemPath}.docLocation`, issues);
|
|
2615
2636
|
}
|
|
2616
2637
|
};
|
|
2617
2638
|
//#endregion
|
|
@@ -1643,6 +1643,9 @@ function createEmptyHyperlink(attrs) {
|
|
|
1643
1643
|
if (attrs.anchor !== void 0) hyperlink.anchor = attrs.anchor;
|
|
1644
1644
|
if (attrs.tooltip !== void 0) hyperlink.tooltip = attrs.tooltip;
|
|
1645
1645
|
if (attrs.rId !== void 0) hyperlink.rId = attrs.rId;
|
|
1646
|
+
if (attrs.target !== void 0) hyperlink.target = attrs.target;
|
|
1647
|
+
if (attrs.history !== void 0) hyperlink.history = attrs.history;
|
|
1648
|
+
if (attrs.docLocation !== void 0) hyperlink.docLocation = attrs.docLocation;
|
|
1646
1649
|
return hyperlink;
|
|
1647
1650
|
}
|
|
1648
1651
|
function getCommentMarkIds(marks) {
|
|
@@ -1684,6 +1687,9 @@ function getLinkKey(mark) {
|
|
|
1684
1687
|
attrs.href,
|
|
1685
1688
|
attrs.rId ?? "",
|
|
1686
1689
|
attrs.tooltip ?? "",
|
|
1690
|
+
attrs.target ?? "",
|
|
1691
|
+
attrs.history === void 0 ? "" : String(attrs.history),
|
|
1692
|
+
attrs.docLocation ?? "",
|
|
1687
1693
|
attrs._docxHyperlinkIndex ?? ""
|
|
1688
1694
|
].join("\0");
|
|
1689
1695
|
}
|
|
@@ -1701,21 +1707,24 @@ function getMarksKey(marks) {
|
|
|
1701
1707
|
function createHyperlink(linkMark) {
|
|
1702
1708
|
const attrs = expectHyperlinkMarkAttrs(linkMark);
|
|
1703
1709
|
const href = attrs.href;
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1710
|
+
const metadata = {
|
|
1711
|
+
...attrs.tooltip === void 0 ? {} : { tooltip: attrs.tooltip },
|
|
1712
|
+
...attrs.target === void 0 ? {} : { target: attrs.target },
|
|
1713
|
+
...attrs.history === void 0 ? {} : { history: attrs.history },
|
|
1714
|
+
...attrs.docLocation === void 0 ? {} : { docLocation: attrs.docLocation }
|
|
1715
|
+
};
|
|
1716
|
+
if (href.startsWith("#")) return {
|
|
1717
|
+
type: "hyperlink",
|
|
1718
|
+
anchor: href.slice(1),
|
|
1719
|
+
...metadata,
|
|
1720
|
+
children: []
|
|
1721
|
+
};
|
|
1713
1722
|
const hyperlink = {
|
|
1714
1723
|
type: "hyperlink",
|
|
1715
1724
|
href,
|
|
1725
|
+
...metadata,
|
|
1716
1726
|
children: []
|
|
1717
1727
|
};
|
|
1718
|
-
if (attrs.tooltip) hyperlink.tooltip = attrs.tooltip;
|
|
1719
1728
|
if (attrs.rId) hyperlink.rId = attrs.rId;
|
|
1720
1729
|
return hyperlink;
|
|
1721
1730
|
}
|
|
@@ -1831,12 +1840,14 @@ function getNoteReferenceVertAlign(noteAttrs, marks) {
|
|
|
1831
1840
|
}
|
|
1832
1841
|
function createNoteReferenceRun(noteRefMark, marks, formattingContext) {
|
|
1833
1842
|
const noteAttrs = expectFootnoteRefMarkAttrs(noteRefMark);
|
|
1843
|
+
const noteRef = {
|
|
1844
|
+
type: noteAttrs.noteType === "endnote" ? "endnoteRef" : "footnoteRef",
|
|
1845
|
+
id: typeof noteAttrs.id === "string" ? Number.parseInt(noteAttrs.id, 10) || 0 : noteAttrs.id
|
|
1846
|
+
};
|
|
1847
|
+
if (noteAttrs.customMarkFollows !== void 0) noteRef.customMarkFollows = noteAttrs.customMarkFollows;
|
|
1834
1848
|
const run = {
|
|
1835
1849
|
type: "run",
|
|
1836
|
-
content: [
|
|
1837
|
-
type: noteAttrs.noteType === "endnote" ? "endnoteRef" : "footnoteRef",
|
|
1838
|
-
id: typeof noteAttrs.id === "string" ? Number.parseInt(noteAttrs.id, 10) || 0 : noteAttrs.id
|
|
1839
|
-
}]
|
|
1850
|
+
content: [noteRef]
|
|
1840
1851
|
};
|
|
1841
1852
|
const formatting = getAtomRunFormattingFromMarks(marks, formattingContext);
|
|
1842
1853
|
const vertAlign = getNoteReferenceVertAlign(noteAttrs, marks);
|
|
@@ -2045,8 +2056,12 @@ function createPageBreakCarrierRun({ baseParagraphFormatting, clear, inheritedFo
|
|
|
2045
2056
|
function createBreakRun(attrs, marks, formattingContext) {
|
|
2046
2057
|
const run = {
|
|
2047
2058
|
type: "run",
|
|
2048
|
-
content: [{
|
|
2059
|
+
content: [attrs.sourceElement === "cr" ? {
|
|
2060
|
+
type: "break",
|
|
2061
|
+
sourceElement: "cr"
|
|
2062
|
+
} : {
|
|
2049
2063
|
type: "break",
|
|
2064
|
+
...attrs.sourceElement !== void 0 ? { sourceElement: attrs.sourceElement } : {},
|
|
2050
2065
|
...attrs.breakType !== void 0 ? { breakType: attrs.breakType } : {},
|
|
2051
2066
|
...attrs.clear !== void 0 ? { clear: attrs.clear } : {}
|
|
2052
2067
|
}]
|
|
@@ -3305,6 +3320,7 @@ function convertPMTableCell(node, documentCounts, styleResolver = null) {
|
|
|
3305
3320
|
type: "tableCell",
|
|
3306
3321
|
content
|
|
3307
3322
|
};
|
|
3323
|
+
if (attrs._docxCellId !== void 0) cell.id = attrs._docxCellId;
|
|
3308
3324
|
const cellFormatting = tableCellAttrsToFormatting(attrs);
|
|
3309
3325
|
if (cellFormatting) cell.formatting = cellFormatting;
|
|
3310
3326
|
if (Array.isArray(attrs.tcPrChange) && attrs.tcPrChange.length > 0) cell.propertyChanges = [...attrs.tcPrChange];
|
|
@@ -492,7 +492,10 @@ function convertParagraph(paragraph, styleResolver, context, extraRunFormatting,
|
|
|
492
492
|
...content.href !== void 0 ? { href: content.href } : {},
|
|
493
493
|
...content.anchor !== void 0 ? { anchor: content.anchor } : {},
|
|
494
494
|
...content.tooltip !== void 0 ? { tooltip: content.tooltip } : {},
|
|
495
|
-
...content.rId !== void 0 ? { rId: content.rId } : {}
|
|
495
|
+
...content.rId !== void 0 ? { rId: content.rId } : {},
|
|
496
|
+
...content.target !== void 0 ? { target: content.target } : {},
|
|
497
|
+
...content.history !== void 0 ? { history: content.history } : {},
|
|
498
|
+
...content.docLocation !== void 0 ? { docLocation: content.docLocation } : {}
|
|
496
499
|
});
|
|
497
500
|
break;
|
|
498
501
|
}
|
|
@@ -1325,6 +1328,7 @@ function convertTableCell({ cell, styleResolver, context, isHeader, gridWidthPer
|
|
|
1325
1328
|
colspan: formatting?.gridSpan ?? 1,
|
|
1326
1329
|
rowspan
|
|
1327
1330
|
};
|
|
1331
|
+
if (cell.id !== void 0) attrs._docxCellId = cell.id;
|
|
1328
1332
|
if (effectiveFormatting.width.type === "value") {
|
|
1329
1333
|
attrs.width = effectiveFormatting.width.value;
|
|
1330
1334
|
if (effectiveFormatting.width.widthType) attrs.widthType = effectiveFormatting.width.widthType;
|
|
@@ -2064,6 +2068,7 @@ function convertRunContent(content, marks, formatting, textBoxAnchors, authoredF
|
|
|
2064
2068
|
case "break":
|
|
2065
2069
|
if (content.breakType === "textWrapping" || !content.breakType) {
|
|
2066
2070
|
const attrs = {
|
|
2071
|
+
...content.sourceElement !== void 0 ? { sourceElement: content.sourceElement } : {},
|
|
2067
2072
|
...content.breakType !== void 0 ? { breakType: content.breakType } : {},
|
|
2068
2073
|
...content.clear !== void 0 ? { clear: content.clear } : {}
|
|
2069
2074
|
};
|
|
@@ -2095,7 +2100,8 @@ function convertRunContent(content, marks, formatting, textBoxAnchors, authoredF
|
|
|
2095
2100
|
const footnoteMark = schema.mark("footnoteRef", {
|
|
2096
2101
|
id: content.id.toString(),
|
|
2097
2102
|
noteType: "footnote",
|
|
2098
|
-
vertAlign: noteReferenceVertAlign(formatting?.vertAlign)
|
|
2103
|
+
vertAlign: noteReferenceVertAlign(formatting?.vertAlign),
|
|
2104
|
+
customMarkFollows: content.customMarkFollows
|
|
2099
2105
|
});
|
|
2100
2106
|
return [schema.text(content.id.toString(), [...marks, footnoteMark])];
|
|
2101
2107
|
}
|
|
@@ -2103,7 +2109,8 @@ function convertRunContent(content, marks, formatting, textBoxAnchors, authoredF
|
|
|
2103
2109
|
const endnoteMark = schema.mark("footnoteRef", {
|
|
2104
2110
|
id: content.id.toString(),
|
|
2105
2111
|
noteType: "endnote",
|
|
2106
|
-
vertAlign: noteReferenceVertAlign(formatting?.vertAlign)
|
|
2112
|
+
vertAlign: noteReferenceVertAlign(formatting?.vertAlign),
|
|
2113
|
+
customMarkFollows: content.customMarkFollows
|
|
2107
2114
|
});
|
|
2108
2115
|
return [schema.text(content.id.toString(), [...marks, endnoteMark])];
|
|
2109
2116
|
}
|
|
@@ -2284,6 +2291,9 @@ function convertHyperlink(hyperlink, { getInheritedRunFormatting, styleResolver,
|
|
|
2284
2291
|
href,
|
|
2285
2292
|
tooltip: hyperlink.tooltip,
|
|
2286
2293
|
rId: hyperlink.rId,
|
|
2294
|
+
target: hyperlink.target,
|
|
2295
|
+
history: hyperlink.history,
|
|
2296
|
+
docLocation: hyperlink.docLocation,
|
|
2287
2297
|
_docxHyperlinkIndex: hyperlinkIndex
|
|
2288
2298
|
});
|
|
2289
2299
|
for (const { content: child, stack } of withInlineWrapperStacks(hyperlink.children)) {
|
|
@@ -82,6 +82,9 @@ const HyperlinkExtension = createMarkExtension({
|
|
|
82
82
|
href: {},
|
|
83
83
|
tooltip: { default: null },
|
|
84
84
|
rId: { default: null },
|
|
85
|
+
target: { default: null },
|
|
86
|
+
history: { default: null },
|
|
87
|
+
docLocation: { default: null },
|
|
85
88
|
_docxHyperlinkIndex: { default: null }
|
|
86
89
|
},
|
|
87
90
|
inclusive: false,
|
|
@@ -89,14 +92,15 @@ const HyperlinkExtension = createMarkExtension({
|
|
|
89
92
|
tag: "a[href]",
|
|
90
93
|
getAttrs: (dom) => ({
|
|
91
94
|
href: sanitizeStoredHref(dom.getAttribute("href") ?? void 0),
|
|
92
|
-
tooltip: dom.getAttribute("title") ?? void 0
|
|
95
|
+
tooltip: dom.getAttribute("title") ?? void 0,
|
|
96
|
+
target: dom.getAttribute("target") ?? void 0
|
|
93
97
|
})
|
|
94
98
|
}],
|
|
95
99
|
toDOM(mark) {
|
|
96
|
-
const { href, tooltip } = expectHyperlinkMarkAttrs(mark);
|
|
100
|
+
const { href, target, tooltip } = expectHyperlinkMarkAttrs(mark);
|
|
97
101
|
const domAttrs = {
|
|
98
102
|
href: sanitizeStoredHref(href),
|
|
99
|
-
...anchorTargetAttrs(
|
|
103
|
+
...anchorTargetAttrs(target)
|
|
100
104
|
};
|
|
101
105
|
if (tooltip) domAttrs["title"] = tooltip;
|
|
102
106
|
return [
|
|
@@ -12,6 +12,7 @@ const HardBreakExtension = createNodeExtension({
|
|
|
12
12
|
inline: true,
|
|
13
13
|
group: "inline",
|
|
14
14
|
attrs: {
|
|
15
|
+
sourceElement: { default: null },
|
|
15
16
|
breakType: { default: null },
|
|
16
17
|
clear: { default: null }
|
|
17
18
|
},
|
|
@@ -22,15 +23,18 @@ const HardBreakExtension = createNodeExtension({
|
|
|
22
23
|
if (!(node instanceof HTMLElement)) return null;
|
|
23
24
|
const breakType = node.dataset["docxBreakType"];
|
|
24
25
|
const clear = node.dataset["docxBreakClear"];
|
|
26
|
+
const sourceElement = node.dataset["docxSourceElement"];
|
|
25
27
|
const attrs = {};
|
|
28
|
+
if (sourceElement === "br" || sourceElement === "cr") attrs["sourceElement"] = sourceElement;
|
|
26
29
|
if (breakType === "column" || breakType === "textWrapping") attrs["breakType"] = breakType;
|
|
27
30
|
if (clear === "none" || clear === "left" || clear === "right" || clear === "all") attrs["clear"] = clear;
|
|
28
31
|
return Object.keys(attrs).length > 0 ? attrs : null;
|
|
29
32
|
}
|
|
30
33
|
}],
|
|
31
34
|
toDOM(node) {
|
|
32
|
-
const { breakType, clear } = expectHardBreakAttrs(node);
|
|
35
|
+
const { sourceElement, breakType, clear } = expectHardBreakAttrs(node);
|
|
33
36
|
const attrs = {};
|
|
37
|
+
if (sourceElement !== void 0) attrs["data-docx-source-element"] = sourceElement;
|
|
34
38
|
if (breakType !== void 0) attrs["data-docx-break-type"] = breakType;
|
|
35
39
|
if (clear !== void 0) attrs["data-docx-break-clear"] = clear;
|
|
36
40
|
return Object.keys(attrs).length > 0 ? ["br", attrs] : ["br"];
|
|
@@ -39,6 +39,43 @@ const preserveOmittedGridSlots = (command) => (state, dispatch, view) => {
|
|
|
39
39
|
if (table && hasOmittedGridSlots(table)) return false;
|
|
40
40
|
return command(state, dispatch, view);
|
|
41
41
|
};
|
|
42
|
+
const preserveSourceCellIdentity = (command) => (state, dispatch, view) => {
|
|
43
|
+
if (!command(state, void 0, view)) return false;
|
|
44
|
+
if (!dispatch) return true;
|
|
45
|
+
const rect = selectedRect(state);
|
|
46
|
+
const sourceRelativePosition = rect.map.map.at(rect.top * rect.map.width + rect.left);
|
|
47
|
+
if (sourceRelativePosition === void 0) return panic("The selected table cell is absent from its table map");
|
|
48
|
+
const sourceCell = rect.table.nodeAt(sourceRelativePosition);
|
|
49
|
+
if (!sourceCell) return panic("The selected table cell is absent from its table map");
|
|
50
|
+
const sourceId = expectTableCellAttrs(sourceCell)._docxCellId;
|
|
51
|
+
return command(state, (tr) => {
|
|
52
|
+
if (sourceId === void 0 || sourceId === null) {
|
|
53
|
+
dispatch(tr);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const mappedTablePosition = tr.mapping.map(rect.tableStart - 1, -1);
|
|
57
|
+
const table = tr.doc.nodeAt(mappedTablePosition);
|
|
58
|
+
if (!table) return panic("The split-cell transaction lost its table");
|
|
59
|
+
const tableStart = mappedTablePosition + 1;
|
|
60
|
+
const map = TableMap.get(table);
|
|
61
|
+
const survivingRelativePosition = map.map.at(rect.top * map.width + rect.left);
|
|
62
|
+
if (survivingRelativePosition === void 0) return panic("The surviving table cell is absent from its table map");
|
|
63
|
+
const cleared = /* @__PURE__ */ new Set();
|
|
64
|
+
for (let row = rect.top; row < rect.bottom; row++) for (let column = rect.left; column < rect.right; column++) {
|
|
65
|
+
const relativePosition = map.map.at(row * map.width + column);
|
|
66
|
+
if (relativePosition === void 0) return panic("A split table cell is absent from its table map");
|
|
67
|
+
if (relativePosition === survivingRelativePosition || cleared.has(relativePosition)) continue;
|
|
68
|
+
cleared.add(relativePosition);
|
|
69
|
+
const cell = table.nodeAt(relativePosition);
|
|
70
|
+
if (!cell) return panic("A split table cell is absent from its table map");
|
|
71
|
+
tr.setNodeMarkup(tableStart + relativePosition, null, {
|
|
72
|
+
...cell.attrs,
|
|
73
|
+
_docxCellId: null
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
dispatch(tr);
|
|
77
|
+
}, view);
|
|
78
|
+
};
|
|
42
79
|
const isOneOf = (value, values) => value !== null && value !== void 0 && values.some((allowed) => allowed === value);
|
|
43
80
|
/** Map CSS border-style to OOXML border style. */
|
|
44
81
|
function cssBorderStyleToOoxml(cssStyle) {
|
|
@@ -334,6 +371,7 @@ const tableCellSpec = {
|
|
|
334
371
|
tableRole: "cell",
|
|
335
372
|
isolating: true,
|
|
336
373
|
attrs: {
|
|
374
|
+
_docxCellId: { default: null },
|
|
337
375
|
colspan: { default: 1 },
|
|
338
376
|
rowspan: { default: 1 },
|
|
339
377
|
_omittedGridSlot: { default: null },
|
|
@@ -402,6 +440,7 @@ const tableHeaderSpec = {
|
|
|
402
440
|
tableRole: "header_cell",
|
|
403
441
|
isolating: true,
|
|
404
442
|
attrs: {
|
|
443
|
+
_docxCellId: { default: null },
|
|
405
444
|
colspan: { default: 1 },
|
|
406
445
|
rowspan: { default: 1 },
|
|
407
446
|
_omittedGridSlot: { default: null },
|
|
@@ -2004,7 +2043,7 @@ const TablePluginExtension = createExtension({
|
|
|
2004
2043
|
selectRow: () => selectRow,
|
|
2005
2044
|
selectColumn: () => selectColumn,
|
|
2006
2045
|
mergeCells: () => preserveOmittedGridSlots(mergeCells),
|
|
2007
|
-
splitCell: () => preserveOmittedGridSlots(splitCell),
|
|
2046
|
+
splitCell: () => preserveOmittedGridSlots(preserveSourceCellIdentity(splitCell)),
|
|
2008
2047
|
setCellBorder: (side, spec, clearOthers) => setCellBorder(side, spec, clearOthers),
|
|
2009
2048
|
setTableBorders: (preset, borderSpec) => setTableBorders(preset, borderSpec),
|
|
2010
2049
|
setTableBorderPreset: (preset) => setTableBorderPreset(preset),
|
|
@@ -129,6 +129,9 @@ declare const PARAGRAPH_FORMATTING_WRITE_BACK: {
|
|
|
129
129
|
readonly numberingChangeXml: {
|
|
130
130
|
readonly kind: "original-only";
|
|
131
131
|
};
|
|
132
|
+
readonly numberingInsertionXml: {
|
|
133
|
+
readonly kind: "original-only";
|
|
134
|
+
};
|
|
132
135
|
readonly outlineLevel: {
|
|
133
136
|
readonly kind: "style-resolved-attr";
|
|
134
137
|
readonly attr: "outlineLevel";
|
|
@@ -78,6 +78,7 @@ const PARAGRAPH_FORMATTING_WRITE_BACK = {
|
|
|
78
78
|
numPr: { kind: "direct-provenance" },
|
|
79
79
|
numPrFromStyle: { kind: "derived" },
|
|
80
80
|
numberingChangeXml: { kind: "original-only" },
|
|
81
|
+
numberingInsertionXml: { kind: "original-only" },
|
|
81
82
|
outlineLevel: {
|
|
82
83
|
kind: "style-resolved-attr",
|
|
83
84
|
attr: "outlineLevel"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { decodeOoxmlSymbolCharacter } from "../utils/ooxmlSymbol.js";
|
|
2
2
|
import { expectFieldAttrs, expectPreservedXmlAttrs, expectSymbolAttrs } from "./attrs/index.js";
|
|
3
|
+
import { PRESERVED_XML_LEVELS } from "./schema/nodes.js";
|
|
3
4
|
import { panic } from "better-result";
|
|
4
5
|
import { Mark } from "prosemirror-model";
|
|
5
6
|
//#region src/prosemirror/runFormattingInlineCarriers.ts
|
|
@@ -31,6 +32,22 @@ const RUN_FORMATTING_INLINE_ATOM_DISPOSITIONS = Object.freeze({
|
|
|
31
32
|
textBoxAnchor: "not-a-run"
|
|
32
33
|
});
|
|
33
34
|
const dispositionsByName = new Map(Object.entries(RUN_FORMATTING_INLINE_ATOM_DISPOSITIONS));
|
|
35
|
+
/**
|
|
36
|
+
* One preserved capture is a `w:r` child and the other is a `w:p` child, and
|
|
37
|
+
* the map above can only name the node type.
|
|
38
|
+
*
|
|
39
|
+
* Only the run-level capture serializes inside a `w:r`, so only it has a
|
|
40
|
+
* `w:rPr` to carry; the save path already branches on the same level. A
|
|
41
|
+
* paragraph-level capture is the markup Word writes BETWEEN runs — `w:proofErr`
|
|
42
|
+
* above all — and treating it as a carrier makes every consumer of this map
|
|
43
|
+
* wrong in the same way: formatting marks put on it are dropped on save, and a
|
|
44
|
+
* comparison is asked to line up the base's proofing annotations with the
|
|
45
|
+
* revised document's own, which no redline can do.
|
|
46
|
+
*/
|
|
47
|
+
const PRESERVED_XML_DISPOSITIONS = {
|
|
48
|
+
[PRESERVED_XML_LEVELS.run]: "preserved-xml-run",
|
|
49
|
+
[PRESERVED_XML_LEVELS.inline]: "not-a-run"
|
|
50
|
+
};
|
|
34
51
|
const CONTROL_CHARACTER_BY_DISPOSITION = Object.freeze({
|
|
35
52
|
"break-run": "\n",
|
|
36
53
|
"field-run": null,
|
|
@@ -73,7 +90,7 @@ const runFormattingInlineAtomDisposition = (node) => {
|
|
|
73
90
|
if (!node.isInline || !node.isAtom) return null;
|
|
74
91
|
const disposition = dispositionsByName.get(node.type.name);
|
|
75
92
|
if (!disposition) return panic(`Inline atom ${JSON.stringify(node.type.name)} has no run-formatting disposition`);
|
|
76
|
-
return disposition;
|
|
93
|
+
return disposition === "preserved-xml-run" ? PRESERVED_XML_DISPOSITIONS[expectPreservedXmlAttrs(node).level] : disposition;
|
|
77
94
|
};
|
|
78
95
|
/** Apply a mark set without changing a carrier's text or serialized ownership. */
|
|
79
96
|
const applyMarksToRunFormattingRepresentation = ({ tr, representation, marks }) => {
|
|
@@ -81,6 +81,7 @@ type FootnoteRefAttrs = {
|
|
|
81
81
|
id: string | number;
|
|
82
82
|
noteType?: "footnote" | "endnote";
|
|
83
83
|
vertAlign?: "baseline" | "superscript";
|
|
84
|
+
customMarkFollows?: document_d_exports.NoteReferenceContent["customMarkFollows"];
|
|
84
85
|
};
|
|
85
86
|
type CommentAttrs = {
|
|
86
87
|
commentId: number;
|
|
@@ -271,6 +272,9 @@ type HyperlinkAttrs = {
|
|
|
271
272
|
href: string;
|
|
272
273
|
tooltip?: string;
|
|
273
274
|
rId?: string;
|
|
275
|
+
target?: document_d_exports.Hyperlink["target"];
|
|
276
|
+
history?: document_d_exports.Hyperlink["history"];
|
|
277
|
+
docLocation?: document_d_exports.Hyperlink["docLocation"];
|
|
274
278
|
_docxHyperlinkIndex?: number;
|
|
275
279
|
};
|
|
276
280
|
//#endregion
|
|
@@ -7,6 +7,12 @@ import { ParagraphNumberingAttr } from "../numberingAttr.js";
|
|
|
7
7
|
import { ParagraphDirection } from "../paragraphDirection.js";
|
|
8
8
|
//#region src/prosemirror/schema/nodes.d.ts
|
|
9
9
|
type HardBreakAttrs = {
|
|
10
|
+
sourceElement: "cr";
|
|
11
|
+
breakType?: never;
|
|
12
|
+
clear?: never;
|
|
13
|
+
} | {
|
|
14
|
+
/** Absent and `br` both serialize as the standard break element. */
|
|
15
|
+
sourceElement?: "br";
|
|
10
16
|
/** Absent means the source `w:br` omitted `w:type`. */
|
|
11
17
|
breakType?: "column" | "textWrapping";
|
|
12
18
|
clear?: "none" | "left" | "right" | "all";
|
|
@@ -216,6 +222,9 @@ type ParagraphAttrs = {
|
|
|
216
222
|
anchor?: document_d_exports.Hyperlink["anchor"];
|
|
217
223
|
tooltip?: document_d_exports.Hyperlink["tooltip"];
|
|
218
224
|
rId?: document_d_exports.Hyperlink["rId"];
|
|
225
|
+
target?: document_d_exports.Hyperlink["target"];
|
|
226
|
+
history?: document_d_exports.Hyperlink["history"];
|
|
227
|
+
docLocation?: document_d_exports.Hyperlink["docLocation"];
|
|
219
228
|
}[];
|
|
220
229
|
/**
|
|
221
230
|
* Run-in heading flag: this paragraph's mark carries
|
|
@@ -1056,6 +1065,8 @@ type SuggestedStructuralMarker = {
|
|
|
1056
1065
|
* Table cell attributes
|
|
1057
1066
|
*/
|
|
1058
1067
|
type TableCellAttrs = {
|
|
1068
|
+
/** Authored OOXML cell identifier. */
|
|
1069
|
+
_docxCellId?: document_d_exports.TableCell["id"];
|
|
1059
1070
|
/** Column span */
|
|
1060
1071
|
colspan: number;
|
|
1061
1072
|
/** Row span */
|
|
@@ -5,7 +5,7 @@ type TablePropertiesCommand = {
|
|
|
5
5
|
widthType?: document_d_exports.TableWidthType | null;
|
|
6
6
|
justification?: document_d_exports.TableAlignment | null;
|
|
7
7
|
};
|
|
8
|
-
declare const TABLE_PROPERTY_JUSTIFICATIONS: readonly
|
|
8
|
+
declare const TABLE_PROPERTY_JUSTIFICATIONS: readonly document_d_exports.TableAlignment[];
|
|
9
9
|
declare const toTablePropertyJustification: (value: string | null | undefined) => NonNullable<TablePropertiesCommand["justification"]>;
|
|
10
10
|
type TableAction = "addRowAbove" | "addRowBelow" | "addColumnLeft" | "addColumnRight" | "deleteRow" | "deleteColumn" | "mergeCells" | "splitCell" | "deleteTable" | "selectTable" | "selectRow" | "selectColumn" | "borderAll" | "borderOutside" | "borderInside" | "borderNone" | "borderTop" | "borderBottom" | "borderLeft" | "borderRight" | {
|
|
11
11
|
type: "cellFillColor";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stll/folio-core",
|
|
3
|
-
"version": "0.47.
|
|
3
|
+
"version": "0.47.3",
|
|
4
4
|
"description": "Headless, framework-neutral core of folio: the OOXML (.docx) parser, document model, ProseMirror integration, and page-layout engine. No React.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"document-model",
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"perf": "bun scripts/profile-editor.ts"
|
|
129
129
|
},
|
|
130
130
|
"dependencies": {
|
|
131
|
-
"@stll/docx-core": "^0.25.
|
|
131
|
+
"@stll/docx-core": "^0.25.2",
|
|
132
132
|
"@stll/docx-utils": "^0.1.0",
|
|
133
133
|
"@stll/template-conditions": ">=0.4.0 <1.0.0",
|
|
134
134
|
"better-result": "3.0.1",
|