@stll/folio-core 0.43.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/headless.js +6 -5
- package/dist/ai-edits/index.d.ts +2 -2
- package/dist/ai-edits/index.js +2 -2
- package/dist/ai-edits/snapshot.js +13 -9
- package/dist/compare/content-alignment.js +16 -1
- package/dist/compare/inline-atoms.js +1 -1
- package/dist/compare/style-resources.js +6 -0
- package/dist/docx/appVersionNormalization.d.ts +0 -18
- package/dist/docx/blockContentParser.js +8 -0
- package/dist/docx/blockRangeMarkers.d.ts +36 -0
- package/dist/docx/blockRangeMarkers.js +59 -0
- package/dist/docx/bookmarkParser.d.ts +2 -20
- package/dist/docx/bookmarkParser.js +6 -30
- package/dist/docx/borderParser.d.ts +13 -0
- package/dist/docx/borderParser.js +71 -0
- package/dist/docx/builtInStyles.d.ts +165 -0
- package/dist/docx/builtInStyles.js +239 -0
- package/dist/docx/commentIdNormalization.d.ts +3 -1
- package/dist/docx/commentIdNormalization.js +18 -1
- package/dist/docx/commentParser.d.ts +2 -1
- package/dist/docx/commentParser.js +34 -7
- package/dist/docx/commentReferenceNormalization.d.ts +4 -1
- package/dist/docx/commentReferenceNormalization.js +23 -14
- package/dist/docx/danglingRelationshipReferences.d.ts +15 -0
- package/dist/docx/danglingRelationshipReferences.js +30 -0
- package/dist/docx/defaultParagraphStyle.d.ts +18 -1
- package/dist/docx/defaultParagraphStyle.js +23 -1
- package/dist/docx/documentParser.d.ts +2 -1
- package/dist/docx/documentParser.js +2 -2
- package/dist/docx/drawingUtils.d.ts +8 -1
- package/dist/docx/drawingUtils.js +12 -3
- package/dist/docx/fieldParser.js +3 -5
- package/dist/docx/footnoteParser.d.ts +3 -2
- package/dist/docx/footnoteParser.js +19 -4
- package/dist/docx/groupDrawingParser.js +1 -1
- package/dist/docx/headerFooterRefParser.d.ts +4 -3
- package/dist/docx/headerFooterRefParser.js +42 -12
- package/dist/docx/headerFooterReferenceNormalization.d.ts +4 -1
- package/dist/docx/headerFooterReferenceNormalization.js +5 -1
- package/dist/docx/hyperlinkParser.js +11 -15
- package/dist/docx/imageParser.d.ts +1 -1
- package/dist/docx/imageParser.js +22 -18
- package/dist/docx/imageRawXml.js +5 -5
- package/dist/docx/markupRangeMarker.d.ts +15 -0
- package/dist/docx/markupRangeMarker.js +44 -0
- package/dist/docx/noteReferenceStyles.d.ts +29 -0
- package/dist/docx/noteReferenceStyles.js +70 -0
- package/dist/docx/numberingReferenceNormalization.d.ts +4 -1
- package/dist/docx/numberingReferenceNormalization.js +20 -1
- package/dist/docx/paraIdRangeNormalization.d.ts +0 -19
- package/dist/docx/paragraphParser.js +66 -99
- package/dist/docx/paragraphPropertySource.js +1 -0
- package/dist/docx/paragraphTraversal.d.ts +37 -1
- package/dist/docx/paragraphTraversal.js +84 -1
- package/dist/docx/parseContext.d.ts +37 -0
- package/dist/docx/parseContext.js +67 -0
- package/dist/docx/parseWarningMessage.d.ts +6 -0
- package/dist/docx/parseWarningMessage.js +44 -0
- package/dist/docx/parser.js +81 -27
- package/dist/docx/relsParser.d.ts +28 -11
- package/dist/docx/relsParser.js +26 -13
- package/dist/docx/revisionIdNormalization.js +81 -7
- package/dist/docx/rezip.js +63 -22
- package/dist/docx/runConsolidator.js +1 -2
- package/dist/docx/runParser.d.ts +8 -1
- package/dist/docx/runParser.js +30 -48
- package/dist/docx/sectionParser.d.ts +2 -1
- package/dist/docx/sectionParser.js +21 -65
- package/dist/docx/serializer/borderSerializer.d.ts +1 -2
- package/dist/docx/serializer/commentSerializer.js +22 -9
- package/dist/docx/serializer/documentSerializer.d.ts +1 -5
- package/dist/docx/serializer/documentSerializer.js +6 -16
- package/dist/docx/serializer/headerFooterSerializer.js +5 -0
- package/dist/docx/serializer/markupRangeAttributes.d.ts +8 -0
- package/dist/docx/serializer/markupRangeAttributes.js +24 -0
- package/dist/docx/serializer/noteSerializer.js +5 -0
- package/dist/docx/serializer/paragraphSerializer.d.ts +1 -5
- package/dist/docx/serializer/paragraphSerializer.js +29 -35
- package/dist/docx/serializer/runSerializer.js +13 -7
- package/dist/docx/serializer/tableSerializer.js +28 -13
- package/dist/docx/serializer/textFormattingSerializer.d.ts +2 -3
- package/dist/docx/server/build.js +8 -1
- package/dist/docx/server/createBilingualDocument.js +10 -18
- package/dist/docx/server/extractDocxText.js +3 -4
- package/dist/docx/shadingParser.d.ts +6 -0
- package/dist/docx/shadingParser.js +32 -0
- package/dist/docx/shapeParser.js +3 -3
- package/dist/docx/styleParser.js +13 -87
- package/dist/docx/styleReferenceResolution.d.ts +36 -0
- package/dist/docx/styleReferenceResolution.js +51 -0
- package/dist/docx/tableLook.d.ts +57 -0
- package/dist/docx/tableLook.js +63 -0
- package/dist/docx/tableParser.d.ts +7 -9
- package/dist/docx/tableParser.js +64 -110
- package/dist/docx/textBoxParser.js +4 -4
- package/dist/docx/trackedMoveRangeNormalization.d.ts +3 -1
- package/dist/docx/trackedMoveRangeNormalization.js +11 -21
- package/dist/docx/transitionalSpelling.d.ts +13 -2
- package/dist/docx/transitionalSpelling.js +23 -1
- package/dist/docx/verbatimCapture.js +4 -11
- package/dist/docx/vmlImageParser.js +2 -2
- package/dist/docx/watermarkParser.js +2 -2
- package/dist/docx/xmlParser.d.ts +22 -32
- package/dist/docx/xmlParser.js +36 -21
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
- package/dist/internal/paragraphFormattingSerialization.d.ts +2 -3
- package/dist/internal/paragraphFormattingSerialization.js +26 -6
- package/dist/layout-bridge/convert/footnoteLayout.js +2 -7
- package/dist/layout-engine/index.d.ts +2 -2
- package/dist/layout-engine/index.js +2 -2
- package/dist/layout-engine/measure/measureBlocks.js +1 -6
- package/dist/layout-engine/types.d.ts +8 -2
- package/dist/layout-engine/types.js +35 -2
- package/dist/markdown/index.js +1 -1
- package/dist/markdown/internals.d.ts +6 -1
- package/dist/markdown/internals.js +14 -1
- package/dist/markdown/renderBlock.js +35 -21
- package/dist/markdown/renderParagraph.js +14 -5
- package/dist/markdown/renderRuns.js +4 -3
- package/dist/markdown/renderTable.js +4 -3
- package/dist/markdown/trailers.js +41 -7
- package/dist/markdown/types.d.ts +3 -7
- package/dist/prosemirror/attrs/index.js +2 -5
- package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +11 -1
- package/dist/prosemirror/bookmarkBoundaryAttrs.js +18 -3
- package/dist/prosemirror/commands/index.d.ts +3 -3
- package/dist/prosemirror/commands/index.js +2 -2
- package/dist/prosemirror/commands/paragraph.d.ts +3 -3
- package/dist/prosemirror/commands/paragraph.js +2 -2
- package/dist/prosemirror/commentIdAllocator.js +2 -7
- package/dist/prosemirror/conversion/fromProseDoc.js +129 -40
- package/dist/prosemirror/conversion/toProseDoc.d.ts +1 -14
- package/dist/prosemirror/conversion/toProseDoc.js +375 -316
- package/dist/prosemirror/extensions/core/ParagraphExtension.d.ts +14 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +11 -6
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +3 -3
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.d.ts +4 -1
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +6 -2
- package/dist/prosemirror/extensions/features/pastedHeadingStyles.d.ts +7 -0
- package/dist/prosemirror/extensions/features/pastedHeadingStyles.js +74 -0
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +11 -3
- package/dist/prosemirror/extensions/marks/markUtils.js +98 -19
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +7 -3
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +2 -1
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +1 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.js +15 -1
- package/dist/prosemirror/extensions/types.d.ts +2 -2
- package/dist/prosemirror/index.d.ts +3 -3
- package/dist/prosemirror/index.js +3 -3
- package/dist/prosemirror/insertOperations.d.ts +9 -2
- package/dist/prosemirror/insertOperations.js +9 -4
- package/dist/prosemirror/paragraphFormattingProvenance.d.ts +159 -0
- package/dist/prosemirror/paragraphFormattingProvenance.js +106 -0
- package/dist/prosemirror/plugins/documentStyles.d.ts +9 -1
- package/dist/prosemirror/plugins/documentStyles.js +11 -1
- package/dist/prosemirror/plugins/index.d.ts +2 -2
- package/dist/prosemirror/plugins/index.js +2 -2
- package/dist/prosemirror/plugins/revisionIds.d.ts +11 -2
- package/dist/prosemirror/plugins/revisionIds.js +21 -6
- package/dist/prosemirror/runFormattingReconciliation.js +3 -2
- package/dist/prosemirror/runStyleFormatting.d.ts +1 -1
- package/dist/prosemirror/schema/nodes.d.ts +31 -0
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +2 -0
- package/dist/prosemirror/styles/styleResolver.d.ts +9 -0
- package/dist/prosemirror/styles/styleResolver.js +12 -0
- package/dist/style-engine/styleEngine.d.ts +3 -0
- package/dist/style-engine/styleEngine.js +3 -0
- package/dist/style-sets/extract.js +1 -23
- package/dist/style-sets/stellaStyle.js +46 -39
- package/dist/style-sets/styleSetNormalization.d.ts +19 -0
- package/dist/style-sets/styleSetNormalization.js +99 -0
- package/dist/types/content.d.ts +2 -2
- package/dist/utils/createDocument.js +145 -20
- package/dist/utils/headingCollector.d.ts +8 -5
- package/dist/utils/headingCollector.js +23 -25
- package/dist/utils/tableOfContentsStyle.js +9 -2
- package/package.json +2 -2
- package/dist/docx/textWhitespace.d.ts +0 -4
- package/dist/docx/textWhitespace.js +0 -4
- package/dist/layout-bridge/engine/tableWidthUtils.d.ts +0 -6
- package/dist/layout-bridge/engine/tableWidthUtils.js +0 -25
- package/dist/markdown/headings.d.ts +0 -13
- package/dist/markdown/headings.js +0 -20
package/dist/docx/rezip.js
CHANGED
|
@@ -10,6 +10,7 @@ import { parseEndnotes, parseFootnotes } from "./footnoteParser.js";
|
|
|
10
10
|
import { parseHeaderFooterType } from "./headerFooterRefParser.js";
|
|
11
11
|
import { assertValidFolioDocumentModel } from "./modelValidation.js";
|
|
12
12
|
import { isNewDataUrlDrawing } from "./newImage.js";
|
|
13
|
+
import { missingNoteReferenceStyles, noteReferenceNeeds } from "./noteReferenceStyles.js";
|
|
13
14
|
import { parseNumbering } from "./numberingParser.js";
|
|
14
15
|
import { isNumberingReference } from "./numberingReference.js";
|
|
15
16
|
import { isUnsafePackagePath, reconcilePackageReferences, removeUnsafeEntries } from "./packageParts.js";
|
|
@@ -1389,6 +1390,42 @@ async function serializeNumberingIntoZip(doc, originalZip, newZip, compressionLe
|
|
|
1389
1390
|
}
|
|
1390
1391
|
const STYLES_PART_PATH = "word/styles.xml";
|
|
1391
1392
|
const STYLES_CLOSE_ROOT = "</w:styles>";
|
|
1393
|
+
/**
|
|
1394
|
+
* The style table a package folio creates starts from: `docDefaults` and
|
|
1395
|
+
* `Normal`, and nothing else. A document that carries its own style table
|
|
1396
|
+
* replaces this part wholesale.
|
|
1397
|
+
*/
|
|
1398
|
+
const SEED_STYLES_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1399
|
+
<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
1400
|
+
<w:docDefaults>
|
|
1401
|
+
<w:rPrDefault>
|
|
1402
|
+
<w:rPr>
|
|
1403
|
+
<w:rFonts w:ascii="Calibri" w:hAnsi="Calibri"/>
|
|
1404
|
+
<w:sz w:val="22"/>
|
|
1405
|
+
</w:rPr>
|
|
1406
|
+
</w:rPrDefault>
|
|
1407
|
+
<w:pPrDefault>
|
|
1408
|
+
<w:pPr>
|
|
1409
|
+
<w:spacing w:after="200" w:line="276" w:lineRule="auto"/>
|
|
1410
|
+
</w:pPr>
|
|
1411
|
+
</w:pPrDefault>
|
|
1412
|
+
</w:docDefaults>
|
|
1413
|
+
<w:style w:type="paragraph" w:default="1" w:styleId="Normal">
|
|
1414
|
+
<w:name w:val="Normal"/>
|
|
1415
|
+
</w:style>
|
|
1416
|
+
</w:styles>`;
|
|
1417
|
+
/**
|
|
1418
|
+
* The seed part plus the reference styles this package's comments and notes
|
|
1419
|
+
* need. A document with no style table of its own never reaches
|
|
1420
|
+
* {@link styleDefinitionsToSerialize}, so without this its reference marks
|
|
1421
|
+
* would carry a `w:rStyle` naming nothing — the defect
|
|
1422
|
+
* `noteReferenceStyles.ts` exists to remove, on the one path that skips it.
|
|
1423
|
+
*/
|
|
1424
|
+
const seedStylesXmlWith = (missing) => {
|
|
1425
|
+
if (missing.length === 0) return SEED_STYLES_XML;
|
|
1426
|
+
const rootClose = SEED_STYLES_XML.lastIndexOf(STYLES_CLOSE_ROOT);
|
|
1427
|
+
return SEED_STYLES_XML.slice(0, rootClose) + missing.map(serializeStyle).join("") + SEED_STYLES_XML.slice(rootClose);
|
|
1428
|
+
};
|
|
1392
1429
|
const STYLE_ID_PATTERN = /<w:style\b[^>]*?\bw:styleId="(?<id>[^"]+)"/gu;
|
|
1393
1430
|
/**
|
|
1394
1431
|
* Append styles the model defines but the original `word/styles.xml` lacks.
|
|
@@ -1397,6 +1434,27 @@ const STYLE_ID_PATTERN = /<w:style\b[^>]*?\bw:styleId="(?<id>[^"]+)"/gu;
|
|
|
1397
1434
|
* per-language clones a bilingual transform adds, are emitted before the root
|
|
1398
1435
|
* close so paragraphs referencing them resolve on reopen.
|
|
1399
1436
|
*/
|
|
1437
|
+
/**
|
|
1438
|
+
* The styles to write into a package folio is authoring: the model's, plus any
|
|
1439
|
+
* reference character style the serializers are about to emit for this
|
|
1440
|
+
* package's comments and notes but the style table does not define. See
|
|
1441
|
+
* `noteReferenceStyles.ts` — the reference mark would otherwise carry a
|
|
1442
|
+
* `w:rStyle` pointing at nothing.
|
|
1443
|
+
*
|
|
1444
|
+
* Only for a package folio writes from scratch. Repacking a document someone
|
|
1445
|
+
* else authored preserves `word/styles.xml` byte for byte, and adding a
|
|
1446
|
+
* definition there would rewrite a part the user never edited: their document,
|
|
1447
|
+
* their style table, missing reference style included.
|
|
1448
|
+
*/
|
|
1449
|
+
const styleDefinitionsToSerialize = (doc) => {
|
|
1450
|
+
const styles = doc.package.styles;
|
|
1451
|
+
if (!styles) return;
|
|
1452
|
+
const missing = missingNoteReferenceStyles(styles, noteReferenceNeeds(doc.package));
|
|
1453
|
+
return missing.length === 0 ? styles : {
|
|
1454
|
+
...styles,
|
|
1455
|
+
styles: [...styles.styles, ...missing]
|
|
1456
|
+
};
|
|
1457
|
+
};
|
|
1400
1458
|
async function serializeAddedStylesIntoZip(doc, originalZip, newZip, compressionLevel) {
|
|
1401
1459
|
const styles = doc.package.styles;
|
|
1402
1460
|
if (!styles || styles.styles.length === 0) return;
|
|
@@ -1578,25 +1636,7 @@ const createEmptyDocxZip = ({ creator, application }) => {
|
|
|
1578
1636
|
</w:sectPr>
|
|
1579
1637
|
</w:body>
|
|
1580
1638
|
</w:document>`);
|
|
1581
|
-
zip.file(
|
|
1582
|
-
<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
1583
|
-
<w:docDefaults>
|
|
1584
|
-
<w:rPrDefault>
|
|
1585
|
-
<w:rPr>
|
|
1586
|
-
<w:rFonts w:ascii="Calibri" w:hAnsi="Calibri"/>
|
|
1587
|
-
<w:sz w:val="22"/>
|
|
1588
|
-
</w:rPr>
|
|
1589
|
-
</w:rPrDefault>
|
|
1590
|
-
<w:pPrDefault>
|
|
1591
|
-
<w:pPr>
|
|
1592
|
-
<w:spacing w:after="200" w:line="276" w:lineRule="auto"/>
|
|
1593
|
-
</w:pPr>
|
|
1594
|
-
</w:pPrDefault>
|
|
1595
|
-
</w:docDefaults>
|
|
1596
|
-
<w:style w:type="paragraph" w:default="1" w:styleId="Normal">
|
|
1597
|
-
<w:name w:val="Normal"/>
|
|
1598
|
-
</w:style>
|
|
1599
|
-
</w:styles>`);
|
|
1639
|
+
zip.file(STYLES_PART_PATH, SEED_STYLES_XML);
|
|
1600
1640
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1601
1641
|
const creatorElement = creator === void 0 ? "" : `\n <dc:creator>${escapeXml(creator)}</dc:creator>`;
|
|
1602
1642
|
zip.file("docProps/core.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
@@ -1647,10 +1687,11 @@ const createDocumentSeedZip = async (doc, properties) => {
|
|
|
1647
1687
|
const relationships = ["<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"styles.xml\"/>"];
|
|
1648
1688
|
const overrides = [];
|
|
1649
1689
|
let nextRelationshipId = 2;
|
|
1650
|
-
|
|
1690
|
+
const styleDefinitions = styleDefinitionsToSerialize(doc);
|
|
1691
|
+
if (styleDefinitions) {
|
|
1651
1692
|
assertStyleNumberingReferences(doc);
|
|
1652
|
-
zip.file(
|
|
1653
|
-
}
|
|
1693
|
+
zip.file(STYLES_PART_PATH, serializeStylesXml(styleDefinitions));
|
|
1694
|
+
} else zip.file(STYLES_PART_PATH, seedStylesXmlWith(missingNoteReferenceStyles(void 0, noteReferenceNeeds(doc.package))));
|
|
1654
1695
|
const numbering = doc.package.numbering;
|
|
1655
1696
|
if (numbering && (numbering.abstractNums.length > 0 || numbering.nums.length > 0)) {
|
|
1656
1697
|
zip.file("word/numbering.xml", serializeNumberingXml(numbering));
|
|
@@ -114,8 +114,7 @@ function mergeRunContent(content1, content2) {
|
|
|
114
114
|
if (lastText?.type === "text" && firstText?.type === "text") {
|
|
115
115
|
result[result.length - 1] = {
|
|
116
116
|
type: "text",
|
|
117
|
-
text: lastText.text + firstText.text
|
|
118
|
-
...lastText.preserveSpace || firstText.preserveSpace ? { preserveSpace: true } : {}
|
|
117
|
+
text: lastText.text + firstText.text
|
|
119
118
|
};
|
|
120
119
|
for (let i = 1; i < content2.length; i++) result.push(content2[i]);
|
|
121
120
|
} else for (const c of content2) result.push(c);
|
package/dist/docx/runParser.d.ts
CHANGED
|
@@ -2,6 +2,13 @@ import { document_d_exports } from "../types/document.js";
|
|
|
2
2
|
import { StyleMap } from "./styleParser.js";
|
|
3
3
|
import { XmlElement } from "./xmlParser.js";
|
|
4
4
|
//#region src/docx/runParser.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* `w:vertAlign` `baseline` is the reserved value that means "no vertical
|
|
7
|
+
* offset", not a third offset beside `superscript` and `subscript`. It is
|
|
8
|
+
* answered here because this module reads the slot, so a consumer deciding
|
|
9
|
+
* whether a run sits on the baseline cannot drift from how it was parsed.
|
|
10
|
+
*/
|
|
11
|
+
declare const isBaselineVertAlign: (vertAlign: document_d_exports.TextFormatting["vertAlign"]) => boolean;
|
|
5
12
|
/**
|
|
6
13
|
* Parse run formatting properties (w:rPr)
|
|
7
14
|
*
|
|
@@ -74,4 +81,4 @@ declare function hasFieldChar(run: document_d_exports.Run): boolean;
|
|
|
74
81
|
*/
|
|
75
82
|
declare function getFieldCharType(run: document_d_exports.Run): "begin" | "separate" | "end" | null;
|
|
76
83
|
//#endregion
|
|
77
|
-
export { getFieldCharType, getImages, getRunText, hasContent, hasFieldChar, hasImage, parseRun, parseRunProperties };
|
|
84
|
+
export { getFieldCharType, getImages, getRunText, hasContent, hasFieldChar, hasImage, isBaselineVertAlign, parseRun, parseRunProperties };
|
package/dist/docx/runParser.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { isValidHexColor } from "../utils/colorResolver.js";
|
|
2
1
|
import { parseHorizontalScalePercent } from "../utils/horizontalScale.js";
|
|
3
2
|
import { parseDiagramPreview } from "./diagramPreview.js";
|
|
4
3
|
import { isGroupDrawing, parseGroupDrawing } from "./groupDrawingParser.js";
|
|
5
4
|
import { parseImage } from "./imageParser.js";
|
|
6
5
|
import { imageRawXmlFingerprint } from "./imageRawXml.js";
|
|
7
|
-
import { EmphasisMarkSchema, FontHintSchema, FontThemeSchema, HighlightColorSchema, PositionalTabAlignmentSchema, PositionalTabLeaderSchema, PositionalTabRelativeToSchema,
|
|
6
|
+
import { EmphasisMarkSchema, FontHintSchema, FontThemeSchema, HighlightColorSchema, PositionalTabAlignmentSchema, PositionalTabLeaderSchema, PositionalTabRelativeToSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum } from "./parserEnums.js";
|
|
7
|
+
import { parseShading } from "./shadingParser.js";
|
|
8
8
|
import { parseShapeFromDrawing, shouldPreserveRawShapeDrawing } from "./shapeParser.js";
|
|
9
9
|
import { isTextBoxDrawing } from "./textBoxParser.js";
|
|
10
|
-
import { requiresXmlSpacePreserve } from "./textWhitespace.js";
|
|
11
10
|
import { resolveThemeFontRef } from "./themeParser.js";
|
|
12
11
|
import { parsePropertyChangeInfo } from "./trackedChangeInfo.js";
|
|
13
12
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
14
13
|
import { parseVmlImageContent, shouldPreserveRawVmlPict } from "./vmlImageParser.js";
|
|
15
|
-
import { cloneWithXmlnsDeclarations, findAllDeep, findChild, findChildren, getAttribute, getChildElements, getLocalName, getTextContent, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, selectAlternateContentBranch } from "./xmlParser.js";
|
|
14
|
+
import { cloneWithXmlnsDeclarations, findAllDeep, findChild, findChildren, getAttribute, getChildElements, getLocalName, getTextContent, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, parseOnOffAttribute, selectAlternateContentBranch } from "./xmlParser.js";
|
|
16
15
|
import { DRAWING_RAW_XML_MODES } from "@stll/docx-core/model";
|
|
17
16
|
//#region src/docx/runParser.ts
|
|
18
17
|
/**
|
|
@@ -36,31 +35,6 @@ function parseColorValue(rgb, themeColor, themeTint, themeShade) {
|
|
|
36
35
|
if (themeShade) color.themeShade = themeShade;
|
|
37
36
|
return color;
|
|
38
37
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Parse shading properties (w:shd)
|
|
41
|
-
*/
|
|
42
|
-
function parseShadingProperties(shd) {
|
|
43
|
-
if (!shd) return;
|
|
44
|
-
const props = {};
|
|
45
|
-
const color = getAttribute(shd, "w", "color");
|
|
46
|
-
if (color === "auto") props.color = { auto: true };
|
|
47
|
-
else if (color && isValidHexColor(color)) props.color = { rgb: color };
|
|
48
|
-
const fill = getAttribute(shd, "w", "fill");
|
|
49
|
-
if (fill === "auto") props.fill = { auto: true };
|
|
50
|
-
else if (fill && isValidHexColor(fill)) props.fill = { rgb: fill };
|
|
51
|
-
const validatedThemeFill = narrowEnum(getAttribute(shd, "w", "themeFill"), ThemeColorSlotSchema);
|
|
52
|
-
if (validatedThemeFill) {
|
|
53
|
-
if (!props.fill) props.fill = {};
|
|
54
|
-
props.fill.themeColor = validatedThemeFill;
|
|
55
|
-
}
|
|
56
|
-
const themeFillTint = getAttribute(shd, "w", "themeFillTint");
|
|
57
|
-
if (themeFillTint && props.fill) props.fill.themeTint = themeFillTint;
|
|
58
|
-
const themeFillShade = getAttribute(shd, "w", "themeFillShade");
|
|
59
|
-
if (themeFillShade && props.fill) props.fill.themeShade = themeFillShade;
|
|
60
|
-
const pattern = narrowEnum(getAttribute(shd, "w", "val"), ShadingPatternSchema);
|
|
61
|
-
if (pattern) props.pattern = pattern;
|
|
62
|
-
return Object.keys(props).length > 0 ? props : void 0;
|
|
63
|
-
}
|
|
64
38
|
function collectFirstRunPropertyChildren(rPr) {
|
|
65
39
|
const children = {};
|
|
66
40
|
for (const child of rPr.elements ?? []) {
|
|
@@ -167,6 +141,13 @@ function collectFirstRunPropertyChildren(rPr) {
|
|
|
167
141
|
return children;
|
|
168
142
|
}
|
|
169
143
|
/**
|
|
144
|
+
* `w:vertAlign` `baseline` is the reserved value that means "no vertical
|
|
145
|
+
* offset", not a third offset beside `superscript` and `subscript`. It is
|
|
146
|
+
* answered here because this module reads the slot, so a consumer deciding
|
|
147
|
+
* whether a run sits on the baseline cannot drift from how it was parsed.
|
|
148
|
+
*/
|
|
149
|
+
const isBaselineVertAlign = (vertAlign) => vertAlign === "baseline";
|
|
150
|
+
/**
|
|
170
151
|
* Parse run formatting properties (w:rPr)
|
|
171
152
|
*
|
|
172
153
|
* Handles ALL rPr properties:
|
|
@@ -231,7 +212,7 @@ function parseRunProperties(rPr, theme, _styles) {
|
|
|
231
212
|
}
|
|
232
213
|
const shd = propertyChildren.shd;
|
|
233
214
|
if (shd) {
|
|
234
|
-
const shadingResult =
|
|
215
|
+
const shadingResult = parseShading(shd);
|
|
235
216
|
if (shadingResult) formatting.shading = shadingResult;
|
|
236
217
|
}
|
|
237
218
|
const sz = propertyChildren.sz;
|
|
@@ -370,14 +351,10 @@ function parseRunPropertyChanges(rPr, theme, styles, currentFormatting) {
|
|
|
370
351
|
* Parse text content (w:t)
|
|
371
352
|
*/
|
|
372
353
|
function parseTextContent(element) {
|
|
373
|
-
|
|
374
|
-
const preserveSpace = getAttribute(element, "xml", "space") === "preserve" || requiresXmlSpacePreserve(text);
|
|
375
|
-
const content = {
|
|
354
|
+
return {
|
|
376
355
|
type: "text",
|
|
377
|
-
text
|
|
356
|
+
text: getTextContent(element)
|
|
378
357
|
};
|
|
379
|
-
if (preserveSpace) content.preserveSpace = true;
|
|
380
|
-
return content;
|
|
381
358
|
}
|
|
382
359
|
/**
|
|
383
360
|
* Parse tab element (w:tab)
|
|
@@ -442,8 +419,8 @@ function parseEndnoteReference(element) {
|
|
|
442
419
|
*/
|
|
443
420
|
function parseFieldChar(element) {
|
|
444
421
|
const fldCharType = getAttribute(element, "w", "fldCharType");
|
|
445
|
-
const fldLock =
|
|
446
|
-
const dirty =
|
|
422
|
+
const fldLock = parseOnOffAttribute(element, "w", "fldLock") === true;
|
|
423
|
+
const dirty = parseOnOffAttribute(element, "w", "dirty") === true;
|
|
447
424
|
let charType = "begin";
|
|
448
425
|
if (fldCharType === "separate") charType = "separate";
|
|
449
426
|
else if (fldCharType === "end") charType = "end";
|
|
@@ -473,15 +450,14 @@ function parseInstrText(element) {
|
|
|
473
450
|
* Wrap raw XML the model cannot project at all.
|
|
474
451
|
*
|
|
475
452
|
* `DrawingContent` always carries an `Image`, so preservation-only content
|
|
476
|
-
* gets a placeholder one
|
|
477
|
-
*
|
|
478
|
-
*
|
|
453
|
+
* gets a placeholder one. It names no relationship, which keeps
|
|
454
|
+
* `classifyDrawingSafety` and the serializer on the replay path instead of
|
|
455
|
+
* regenerating DrawingML from the placeholder.
|
|
479
456
|
*/
|
|
480
457
|
const preserveOnlyDrawing = (rawXml) => ({
|
|
481
458
|
type: "drawing",
|
|
482
459
|
image: {
|
|
483
460
|
type: "image",
|
|
484
|
-
rId: "",
|
|
485
461
|
size: {
|
|
486
462
|
width: 0,
|
|
487
463
|
height: 0
|
|
@@ -530,13 +506,19 @@ function parseDrawingContent(element, rels, media) {
|
|
|
530
506
|
};
|
|
531
507
|
const image = parseImage(element, rels ?? void 0, media ?? void 0);
|
|
532
508
|
if (!image) return null;
|
|
533
|
-
const
|
|
509
|
+
const rawXml = captureVerbatimXml(element);
|
|
510
|
+
if (image.rId === void 0) return {
|
|
511
|
+
type: "drawing",
|
|
512
|
+
image,
|
|
513
|
+
rawXml,
|
|
514
|
+
rawXmlMode: DRAWING_RAW_XML_MODES.PRESERVE_ONLY
|
|
515
|
+
};
|
|
516
|
+
return {
|
|
534
517
|
type: "drawing",
|
|
535
|
-
image
|
|
518
|
+
image,
|
|
519
|
+
rawXml,
|
|
520
|
+
rawImageFingerprint: imageRawXmlFingerprint(image)
|
|
536
521
|
};
|
|
537
|
-
drawing.rawXml = captureVerbatimXml(element);
|
|
538
|
-
drawing.rawImageFingerprint = imageRawXmlFingerprint(image);
|
|
539
|
-
return drawing;
|
|
540
522
|
}
|
|
541
523
|
/**
|
|
542
524
|
* Parse all content within a run element
|
|
@@ -750,4 +732,4 @@ function getFieldCharType(run) {
|
|
|
750
732
|
return run.content.find((c) => c.type === "fieldChar")?.charType ?? null;
|
|
751
733
|
}
|
|
752
734
|
//#endregion
|
|
753
|
-
export { getFieldCharType, getImages, getRunText, hasContent, hasFieldChar, hasImage, parseRun, parseRunProperties };
|
|
735
|
+
export { getFieldCharType, getImages, getRunText, hasContent, hasFieldChar, hasImage, isBaselineVertAlign, parseRun, parseRunProperties };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { ParseContext } from "./parseContext.js";
|
|
2
3
|
import { XmlElement } from "./xmlParser.js";
|
|
3
4
|
//#region src/docx/sectionParser.d.ts
|
|
4
5
|
declare function getUnserializedSectionPropertyChildNames(props: document_d_exports.SectionProperties): readonly string[] | undefined;
|
|
@@ -8,7 +9,7 @@ declare function getUnserializedSectionPropertyChildNames(props: document_d_expo
|
|
|
8
9
|
* @param sectPr - The w:sectPr element
|
|
9
10
|
* @returns SectionProperties object
|
|
10
11
|
*/
|
|
11
|
-
declare function parseSectionProperties(sectPr: XmlElement | null): document_d_exports.SectionProperties;
|
|
12
|
+
declare function parseSectionProperties(sectPr: XmlElement | null, context?: ParseContext): document_d_exports.SectionProperties;
|
|
12
13
|
/**
|
|
13
14
|
* Get page width in pixels (96 DPI)
|
|
14
15
|
*
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { parseBorderSpec } from "./borderParser.js";
|
|
1
2
|
import { parseFooterReference, parseHeaderReference } from "./headerFooterRefParser.js";
|
|
2
3
|
import { parseEndnoteProperties, parseFootnoteProperties } from "./notePropertiesParser.js";
|
|
3
|
-
import {
|
|
4
|
+
import { NumberFormatSchema, ThemeColorSlotSchema, narrowEnum } from "./parserEnums.js";
|
|
4
5
|
import { parseSectionReferenceHistory } from "./sectionReferenceHistory.js";
|
|
5
6
|
import { parsePropertyChangeInfo } from "./trackedChangeInfo.js";
|
|
6
|
-
import { findChild, findChildren, getAttribute, getChildElements, getLocalName, parseBooleanElement, parseNumericAttribute } from "./xmlParser.js";
|
|
7
|
+
import { findChild, findChildren, getAttribute, getChildElements, getLocalName, parseBooleanElement, parseNumericAttribute, parseOnOffAttribute } from "./xmlParser.js";
|
|
7
8
|
//#region src/docx/sectionParser.ts
|
|
8
9
|
/**
|
|
9
10
|
* Sanity cap on `w:cols/@w:num`. Word's column picker tops out well below
|
|
@@ -49,49 +50,6 @@ function getUnserializedSectionPropertyChildNames(props) {
|
|
|
49
50
|
return Array.isArray(descriptor?.value) ? descriptor.value : void 0;
|
|
50
51
|
}
|
|
51
52
|
/**
|
|
52
|
-
* Parse a color element/attribute for page borders/background
|
|
53
|
-
*/
|
|
54
|
-
function parseColorValue(colorStr, themeColor, themeTint, themeShade) {
|
|
55
|
-
if (!colorStr && !themeColor) return;
|
|
56
|
-
const color = {};
|
|
57
|
-
if (colorStr && colorStr !== "auto") color.rgb = colorStr;
|
|
58
|
-
else if (colorStr === "auto") color.auto = true;
|
|
59
|
-
const validatedThemeColor = narrowEnum(themeColor, ThemeColorSlotSchema);
|
|
60
|
-
if (validatedThemeColor) color.themeColor = validatedThemeColor;
|
|
61
|
-
if (themeTint) color.themeTint = themeTint;
|
|
62
|
-
if (themeShade) color.themeShade = themeShade;
|
|
63
|
-
return Object.keys(color).length > 0 ? color : void 0;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Parse a border element for page borders
|
|
67
|
-
*/
|
|
68
|
-
function parseBorderSpec(element) {
|
|
69
|
-
if (!element) return;
|
|
70
|
-
const rawStyle = getAttribute(element, "w", "val") ?? "none";
|
|
71
|
-
const border = { style: narrowEnum(rawStyle, BorderStyleSchema) ?? rawStyle };
|
|
72
|
-
const sz = parseNumericAttribute(element, "w", "sz");
|
|
73
|
-
if (sz !== void 0) border.size = sz;
|
|
74
|
-
const space = parseNumericAttribute(element, "w", "space");
|
|
75
|
-
if (space !== void 0) border.space = space;
|
|
76
|
-
const color = parseColorValue(getAttribute(element, "w", "color"), getAttribute(element, "w", "themeColor"), getAttribute(element, "w", "themeTint"), getAttribute(element, "w", "themeShade"));
|
|
77
|
-
if (color) border.color = color;
|
|
78
|
-
const shadow = getAttribute(element, "w", "shadow");
|
|
79
|
-
if (shadow === "1" || shadow === "true") border.shadow = true;
|
|
80
|
-
const frame = getAttribute(element, "w", "frame");
|
|
81
|
-
if (frame === "1" || frame === "true") border.frame = true;
|
|
82
|
-
const artRelationshipId = getAttribute(element, "w", "id")?.trim();
|
|
83
|
-
if (artRelationshipId) border.artRelationshipId = artRelationshipId;
|
|
84
|
-
const topLeftArtRelationshipId = getAttribute(element, "w", "topLeft")?.trim();
|
|
85
|
-
if (topLeftArtRelationshipId) border.topLeftArtRelationshipId = topLeftArtRelationshipId;
|
|
86
|
-
const topRightArtRelationshipId = getAttribute(element, "w", "topRight")?.trim();
|
|
87
|
-
if (topRightArtRelationshipId) border.topRightArtRelationshipId = topRightArtRelationshipId;
|
|
88
|
-
const bottomLeftArtRelationshipId = getAttribute(element, "w", "bottomLeft")?.trim();
|
|
89
|
-
if (bottomLeftArtRelationshipId) border.bottomLeftArtRelationshipId = bottomLeftArtRelationshipId;
|
|
90
|
-
const bottomRightArtRelationshipId = getAttribute(element, "w", "bottomRight")?.trim();
|
|
91
|
-
if (bottomRightArtRelationshipId) border.bottomRightArtRelationshipId = bottomRightArtRelationshipId;
|
|
92
|
-
return border;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
53
|
* Parse page orientation
|
|
96
54
|
*/
|
|
97
55
|
function parseOrientation(orient) {
|
|
@@ -160,7 +118,7 @@ function parseLineNumberRestart(restart) {
|
|
|
160
118
|
* @param sectPr - The w:sectPr element
|
|
161
119
|
* @returns SectionProperties object
|
|
162
120
|
*/
|
|
163
|
-
function parseSectionProperties(sectPr) {
|
|
121
|
+
function parseSectionProperties(sectPr, context) {
|
|
164
122
|
const props = {};
|
|
165
123
|
if (!sectPr) return props;
|
|
166
124
|
const unhandledChildNames = getChildElements(sectPr).map((child) => getLocalName(child.name ?? "")).filter((name) => !serializedSectionPropertyChildNames.has(name));
|
|
@@ -199,11 +157,9 @@ function parseSectionProperties(sectPr) {
|
|
|
199
157
|
if (num !== void 0) props.columnCount = Math.min(num, MAX_SECTION_COLUMNS);
|
|
200
158
|
const space = parseNumericAttribute(cols, "w", "space");
|
|
201
159
|
if (space !== void 0) props.columnSpace = space;
|
|
202
|
-
const equalWidth =
|
|
203
|
-
if (equalWidth
|
|
204
|
-
|
|
205
|
-
const sep = getAttribute(cols, "w", "sep");
|
|
206
|
-
if (sep === "1" || sep === "true") props.separator = true;
|
|
160
|
+
const equalWidth = parseOnOffAttribute(cols, "w", "equalWidth");
|
|
161
|
+
if (equalWidth !== void 0) props.equalWidth = equalWidth;
|
|
162
|
+
if (parseOnOffAttribute(cols, "w", "sep") === true) props.separator = true;
|
|
207
163
|
const colElements = findChildren(cols, "w", "col");
|
|
208
164
|
if (colElements.length > 0) {
|
|
209
165
|
props.columns = [];
|
|
@@ -235,15 +191,15 @@ function parseSectionProperties(sectPr) {
|
|
|
235
191
|
else addUnhandledChildName("textDirection");
|
|
236
192
|
}
|
|
237
193
|
const bidi = findChild(sectPr, "w", "bidi");
|
|
238
|
-
if (bidi) props.bidi = parseBooleanElement(bidi);
|
|
239
|
-
const headerRefs = findChildren(sectPr, "w", "headerReference");
|
|
240
|
-
if (headerRefs.length > 0) props.headerReferences = headerRefs
|
|
241
|
-
const footerRefs = findChildren(sectPr, "w", "footerReference");
|
|
242
|
-
if (footerRefs.length > 0) props.footerReferences = footerRefs
|
|
194
|
+
if (bidi) props.bidi = parseBooleanElement(bidi, "w", context);
|
|
195
|
+
const headerRefs = findChildren(sectPr, "w", "headerReference").flatMap((el) => parseHeaderReference(el, context) ?? []);
|
|
196
|
+
if (headerRefs.length > 0) props.headerReferences = headerRefs;
|
|
197
|
+
const footerRefs = findChildren(sectPr, "w", "footerReference").flatMap((el) => parseFooterReference(el, context) ?? []);
|
|
198
|
+
if (footerRefs.length > 0) props.footerReferences = footerRefs;
|
|
243
199
|
const titlePg = findChild(sectPr, "w", "titlePg");
|
|
244
|
-
if (titlePg) props.titlePg = parseBooleanElement(titlePg);
|
|
200
|
+
if (titlePg) props.titlePg = parseBooleanElement(titlePg, "w", context);
|
|
245
201
|
const evenAndOddHeaders = findChild(sectPr, "w", "evenAndOddHeaders");
|
|
246
|
-
if (evenAndOddHeaders) props.evenAndOddHeaders = parseBooleanElement(evenAndOddHeaders);
|
|
202
|
+
if (evenAndOddHeaders) props.evenAndOddHeaders = parseBooleanElement(evenAndOddHeaders, "w", context);
|
|
247
203
|
const lnNumType = findChild(sectPr, "w", "lnNumType");
|
|
248
204
|
if (lnNumType) {
|
|
249
205
|
props.lineNumbers = {};
|
|
@@ -272,13 +228,13 @@ function parseSectionProperties(sectPr) {
|
|
|
272
228
|
const pgBorders = findChild(sectPr, "w", "pgBorders");
|
|
273
229
|
if (pgBorders) {
|
|
274
230
|
props.pageBorders = {};
|
|
275
|
-
const topBorder = parseBorderSpec(findChild(pgBorders, "w", "top"));
|
|
231
|
+
const topBorder = parseBorderSpec(findChild(pgBorders, "w", "top"), context);
|
|
276
232
|
if (topBorder) props.pageBorders.top = topBorder;
|
|
277
|
-
const bottomBorder = parseBorderSpec(findChild(pgBorders, "w", "bottom"));
|
|
233
|
+
const bottomBorder = parseBorderSpec(findChild(pgBorders, "w", "bottom"), context);
|
|
278
234
|
if (bottomBorder) props.pageBorders.bottom = bottomBorder;
|
|
279
|
-
const leftBorder = parseBorderSpec(findChild(pgBorders, "w", "left"));
|
|
235
|
+
const leftBorder = parseBorderSpec(findChild(pgBorders, "w", "left"), context);
|
|
280
236
|
if (leftBorder) props.pageBorders.left = leftBorder;
|
|
281
|
-
const rightBorder = parseBorderSpec(findChild(pgBorders, "w", "right"));
|
|
237
|
+
const rightBorder = parseBorderSpec(findChild(pgBorders, "w", "right"), context);
|
|
282
238
|
if (rightBorder) props.pageBorders.right = rightBorder;
|
|
283
239
|
const display = getAttribute(pgBorders, "w", "display");
|
|
284
240
|
if (display === "allPages" || display === "firstPage" || display === "notFirstPage") props.pageBorders.display = display;
|
|
@@ -315,11 +271,11 @@ function parseSectionProperties(sectPr) {
|
|
|
315
271
|
if (Object.keys(enProps).length > 0) props.endnotePr = enProps;
|
|
316
272
|
}
|
|
317
273
|
const formProt = findChild(sectPr, "w", "formProt");
|
|
318
|
-
if (formProt) props.formProtection = parseBooleanElement(formProt);
|
|
274
|
+
if (formProt) props.formProtection = parseBooleanElement(formProt, "w", context);
|
|
319
275
|
const noEndnote = findChild(sectPr, "w", "noEndnote");
|
|
320
|
-
if (noEndnote) props.noEndnote = parseBooleanElement(noEndnote);
|
|
276
|
+
if (noEndnote) props.noEndnote = parseBooleanElement(noEndnote, "w", context);
|
|
321
277
|
const rtlGutter = findChild(sectPr, "w", "rtlGutter");
|
|
322
|
-
if (rtlGutter) props.rtlGutter = parseBooleanElement(rtlGutter);
|
|
278
|
+
if (rtlGutter) props.rtlGutter = parseBooleanElement(rtlGutter, "w", context);
|
|
323
279
|
const docGrid = findChild(sectPr, "w", "docGrid");
|
|
324
280
|
if (docGrid) {
|
|
325
281
|
props.docGrid = {};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { document_d_exports } from "../../types/document.js";
|
|
2
2
|
//#region src/docx/serializer/borderSerializer.d.ts
|
|
3
|
-
type ExhaustiveFields<Source, Classified extends keyof Source> = Exclude<keyof Source, Classified> extends never ? Source : never;
|
|
4
3
|
type ClassifiedBorderField = "style" | "color" | "size" | "space" | "shadow" | "frame" | "artRelationshipId" | "topLeftArtRelationshipId" | "topRightArtRelationshipId" | "bottomLeftArtRelationshipId" | "bottomRightArtRelationshipId";
|
|
5
|
-
type ExhaustiveBorder = ExhaustiveFields<document_d_exports.BorderSpec, ClassifiedBorderField>;
|
|
4
|
+
type ExhaustiveBorder = document_d_exports.ExhaustiveFields<document_d_exports.BorderSpec, ClassifiedBorderField>;
|
|
6
5
|
/**
|
|
7
6
|
* Serialize a single border element (`<w:top .../>`, `<w:left .../>`, ...).
|
|
8
7
|
*
|
|
@@ -61,6 +61,25 @@ const COMMENTS_EXTENDED_BASELINE_PREFIXES = [
|
|
|
61
61
|
];
|
|
62
62
|
const XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n";
|
|
63
63
|
/**
|
|
64
|
+
* The order every comment part is written in: top-level comments, then replies.
|
|
65
|
+
*
|
|
66
|
+
* `word/comments.xml` has always been written this way, and the order matters
|
|
67
|
+
* beyond that one part: the save rewrites comments.xml, so the next parse hands
|
|
68
|
+
* the model back in exactly this order. Any other part built by walking the
|
|
69
|
+
* same array — `word/commentsExtended.xml`, and the paraId minting both parts
|
|
70
|
+
* key on — has to walk it in the same order, or the first save writes a part
|
|
71
|
+
* whose order the second save cannot reproduce and saving is not a fixed point.
|
|
72
|
+
*/
|
|
73
|
+
const commentPartOrder = (comments) => {
|
|
74
|
+
const topLevel = [];
|
|
75
|
+
const replies = [];
|
|
76
|
+
for (const comment of comments) {
|
|
77
|
+
const { parentId } = comment;
|
|
78
|
+
(parentId === null || parentId === void 0 ? topLevel : replies).push(comment);
|
|
79
|
+
}
|
|
80
|
+
return [...topLevel, ...replies];
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
64
83
|
* Serialize comments array to comments.xml content. Returns a valid empty
|
|
65
84
|
* `<w:comments/>` document for an empty array so callers can overwrite an
|
|
66
85
|
* existing `word/comments.xml` part when the editor has removed the last
|
|
@@ -68,13 +87,7 @@ const XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"y
|
|
|
68
87
|
* the orphaned comment threads on every save.
|
|
69
88
|
*/
|
|
70
89
|
function serializeComments(comments, sourceBindings) {
|
|
71
|
-
const
|
|
72
|
-
const replies = [];
|
|
73
|
-
for (const c of comments) {
|
|
74
|
-
const { parentId } = c;
|
|
75
|
-
(parentId === null || parentId === void 0 ? topLevel : replies).push(c);
|
|
76
|
-
}
|
|
77
|
-
const body = topLevel.map((comment) => serializeComment(comment)).join("") + replies.map((reply) => serializeComment(reply)).join("");
|
|
90
|
+
const body = commentPartOrder(comments).map((comment) => serializeComment(comment)).join("");
|
|
78
91
|
return XML_DECLARATION + serializePartElement({
|
|
79
92
|
partPath: "word/comments.xml",
|
|
80
93
|
rootName: "w:comments",
|
|
@@ -122,7 +135,7 @@ function ensureThreadedCommentParaIds(comments) {
|
|
|
122
135
|
if (threaded.size === 0) return;
|
|
123
136
|
const used = /* @__PURE__ */ new Set();
|
|
124
137
|
for (const comment of comments) for (const paragraph of comment.content ?? []) if (paragraph.paraId) used.add(paragraph.paraId.toUpperCase());
|
|
125
|
-
for (const comment of comments) {
|
|
138
|
+
for (const comment of commentPartOrder(comments)) {
|
|
126
139
|
if (!threaded.has(comment.id)) continue;
|
|
127
140
|
const last = (comment.content ?? []).at(-1);
|
|
128
141
|
if (!last || last.paraId) continue;
|
|
@@ -153,7 +166,7 @@ function buildCommentExtendedEntries(comments) {
|
|
|
153
166
|
if (paraId) paraIdByCommentId.set(comment.id, paraId);
|
|
154
167
|
}
|
|
155
168
|
const entries = [];
|
|
156
|
-
for (const comment of comments) {
|
|
169
|
+
for (const comment of commentPartOrder(comments)) {
|
|
157
170
|
if (!threaded.has(comment.id)) continue;
|
|
158
171
|
const paraId = paraIdByCommentId.get(comment.id);
|
|
159
172
|
if (!paraId) continue;
|
|
@@ -47,10 +47,6 @@ declare function getDocumentParagraphCount(doc: document_d_exports.Document): nu
|
|
|
47
47
|
* Get table count in document
|
|
48
48
|
*/
|
|
49
49
|
declare function getDocumentTableCount(doc: document_d_exports.Document): number;
|
|
50
|
-
/**
|
|
51
|
-
* Get plain text from document (for comparison/debugging)
|
|
52
|
-
*/
|
|
53
|
-
declare function getDocumentPlainText(doc: document_d_exports.Document): string;
|
|
54
50
|
/**
|
|
55
51
|
* Create an empty document
|
|
56
52
|
*/
|
|
@@ -63,4 +59,4 @@ declare function createSimpleDocument(paragraphs: {
|
|
|
63
59
|
styleId?: string;
|
|
64
60
|
}[]): document_d_exports.Document;
|
|
65
61
|
//#endregion
|
|
66
|
-
export { createEmptyDocument, createSimpleDocument, getDocumentContentCount, getDocumentParagraphCount,
|
|
62
|
+
export { createEmptyDocument, createSimpleDocument, getDocumentContentCount, getDocumentParagraphCount, getDocumentTableCount, hasDocumentContent, hasDocumentSections, hasSectionProperties, serializeDocument, serializeDocumentBody, serializeDocumentBodyElement };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withBlockRangeMarkers } from "../blockRangeMarkers.js";
|
|
1
2
|
import { serializeBlockSdt } from "./blockSdtSerializer.js";
|
|
2
3
|
import { serializeParagraph } from "./paragraphSerializer.js";
|
|
3
4
|
import { serializePartElement } from "./partNamespaces.js";
|
|
@@ -40,6 +41,10 @@ const DOCUMENT_BASELINE_PREFIXES = [
|
|
|
40
41
|
* Serialize a single block content item (paragraph, table, or block-level SDT).
|
|
41
42
|
*/
|
|
42
43
|
function serializeBlockContent(block) {
|
|
44
|
+
return withBlockRangeMarkers(block, serializeBlockBody(block));
|
|
45
|
+
}
|
|
46
|
+
/** The block itself, without the range markers that stood around it. */
|
|
47
|
+
function serializeBlockBody(block) {
|
|
43
48
|
if (block.type === "paragraph") return serializeParagraph(block);
|
|
44
49
|
if (block.type === "table") return serializeTable(block, serializeParagraph);
|
|
45
50
|
return serializeBlockSdt(block, serializeBlockContent);
|
|
@@ -127,21 +132,6 @@ function getDocumentTableCount(doc) {
|
|
|
127
132
|
return doc.package.document.content.filter((b) => b.type === "table").length;
|
|
128
133
|
}
|
|
129
134
|
/**
|
|
130
|
-
* Get plain text from document (for comparison/debugging)
|
|
131
|
-
*/
|
|
132
|
-
function getDocumentPlainText(doc) {
|
|
133
|
-
const texts = [];
|
|
134
|
-
for (const block of doc.package.document.content) if (block.type === "paragraph") {
|
|
135
|
-
for (const content of block.content) if (content.type === "run") {
|
|
136
|
-
for (const item of content.content) if (item.type === "text") texts.push(item.text);
|
|
137
|
-
else if (item.type === "tab") texts.push(" ");
|
|
138
|
-
else if (item.type === "break") texts.push("\n");
|
|
139
|
-
}
|
|
140
|
-
texts.push("\n");
|
|
141
|
-
}
|
|
142
|
-
return texts.join("");
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
135
|
* Create an empty document
|
|
146
136
|
*/
|
|
147
137
|
function createEmptyDocument() {
|
|
@@ -164,4 +154,4 @@ function createSimpleDocument(paragraphs) {
|
|
|
164
154
|
})) } } };
|
|
165
155
|
}
|
|
166
156
|
//#endregion
|
|
167
|
-
export { createEmptyDocument, createSimpleDocument, getDocumentContentCount, getDocumentParagraphCount,
|
|
157
|
+
export { createEmptyDocument, createSimpleDocument, getDocumentContentCount, getDocumentParagraphCount, getDocumentTableCount, hasDocumentContent, hasDocumentSections, hasSectionProperties, serializeDocument, serializeDocumentBody, serializeDocumentBodyElement };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withBlockRangeMarkers } from "../blockRangeMarkers.js";
|
|
1
2
|
import { canReplayHeaderFooterVerbatim, getHeaderFooterVerbatimXml } from "../headerFooterVerbatim.js";
|
|
2
3
|
import { isEmptyParagraph } from "../paragraphParser.js";
|
|
3
4
|
import { captureVerbatimXml } from "../verbatimCapture.js";
|
|
@@ -36,6 +37,10 @@ const HEADER_FOOTER_BASELINE_PREFIXES = [
|
|
|
36
37
|
* header/footer.
|
|
37
38
|
*/
|
|
38
39
|
function serializeBlock(block) {
|
|
40
|
+
return withBlockRangeMarkers(block, serializeBlockBody(block));
|
|
41
|
+
}
|
|
42
|
+
/** The block itself, without the range markers that stood around it. */
|
|
43
|
+
function serializeBlockBody(block) {
|
|
39
44
|
if (block.type === "paragraph") return serializeParagraph(block);
|
|
40
45
|
if (block.type === "table") return serializeTable(block, serializeParagraph);
|
|
41
46
|
return serializeBlockSdt(block, serializeBlock);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { document_d_exports } from "../../types/document.js";
|
|
2
|
+
//#region src/docx/serializer/markupRangeAttributes.d.ts
|
|
3
|
+
declare const markupRangeAttributes: (marker: document_d_exports.MarkupRangeMarker) => string[];
|
|
4
|
+
declare const bookmarkRangeAttributes: (marker: document_d_exports.BookmarkRangeMarker) => string[];
|
|
5
|
+
/** `w:author` is required, so it is always written; `w:date` only when known. */
|
|
6
|
+
declare const moveBookmarkAttributes: (marker: document_d_exports.MoveBookmarkMarker) => string[];
|
|
7
|
+
//#endregion
|
|
8
|
+
export { bookmarkRangeAttributes, markupRangeAttributes, moveBookmarkAttributes };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { escapeXml } from "./xmlUtils.js";
|
|
2
|
+
//#region src/docx/serializer/markupRangeAttributes.ts
|
|
3
|
+
const attribute = (name, value) => `${name}="${escapeXml(String(value))}"`;
|
|
4
|
+
const markupRangeAttributes = (marker) => {
|
|
5
|
+
const attributes = [attribute("w:id", marker.id)];
|
|
6
|
+
if (marker.displacedByCustomXml !== void 0) attributes.push(attribute("w:displacedByCustomXml", marker.displacedByCustomXml));
|
|
7
|
+
return attributes;
|
|
8
|
+
};
|
|
9
|
+
const bookmarkRangeAttributes = (marker) => {
|
|
10
|
+
const attributes = markupRangeAttributes(marker);
|
|
11
|
+
if (marker.colFirst !== void 0) attributes.push(attribute("w:colFirst", marker.colFirst));
|
|
12
|
+
if (marker.colLast !== void 0) attributes.push(attribute("w:colLast", marker.colLast));
|
|
13
|
+
attributes.push(attribute("w:name", marker.name));
|
|
14
|
+
return attributes;
|
|
15
|
+
};
|
|
16
|
+
/** `w:author` is required, so it is always written; `w:date` only when known. */
|
|
17
|
+
const moveBookmarkAttributes = (marker) => {
|
|
18
|
+
const attributes = bookmarkRangeAttributes(marker);
|
|
19
|
+
attributes.push(attribute("w:author", marker.author.trim() || "Unknown"));
|
|
20
|
+
if (marker.date !== void 0 && marker.date.trim().length > 0) attributes.push(attribute("w:date", marker.date.trim()));
|
|
21
|
+
return attributes;
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
export { bookmarkRangeAttributes, markupRangeAttributes, moveBookmarkAttributes };
|