@stll/folio-core 0.37.4 → 0.38.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/apply.js +275 -88
- package/dist/ai-edits/clean-text.js +12 -1
- package/dist/ai-edits/headless.d.ts +110 -0
- package/dist/ai-edits/headless.js +569 -40
- package/dist/ai-edits/index.d.ts +2 -1
- package/dist/ai-edits/snapshot.d.ts +24 -1
- package/dist/ai-edits/snapshot.js +206 -9
- package/dist/ai-edits/table-geometry.d.ts +7 -7
- package/dist/ai-edits/table-geometry.js +43 -27
- package/dist/ai-edits/table-template.d.ts +2 -1
- package/dist/ai-edits/table-template.js +9 -3
- package/dist/ai-edits/types.d.ts +38 -16
- package/dist/compare/__fixtures__/body-sequence.d.ts +4 -0
- package/dist/compare/__fixtures__/body-sequence.js +5 -3
- package/dist/compare/compare.d.ts +26 -2
- package/dist/compare/compare.js +444 -16
- package/dist/compare/content-alignment.js +216 -137
- package/dist/compare/content-types.d.ts +36 -2
- package/dist/compare/content.d.ts +13 -10
- package/dist/compare/content.js +69 -23
- package/dist/compare/formatting.d.ts +4 -22
- package/dist/compare/formatting.js +8 -112
- package/dist/compare/inline-atom-resources.d.ts +6 -0
- package/dist/compare/inline-atom-resources.js +26 -0
- package/dist/compare/inline-atoms.d.ts +35 -0
- package/dist/compare/inline-atoms.js +383 -0
- package/dist/compare/inline-provenance.d.ts +36 -0
- package/dist/compare/inline-provenance.js +334 -0
- package/dist/compare/plan.d.ts +4 -1
- package/dist/compare/plan.js +32 -4
- package/dist/compare/scenario.d.ts +4 -6
- package/dist/compare/section-boundary-properties.d.ts +63 -0
- package/dist/compare/section-boundary-properties.js +235 -0
- package/dist/compare/style-resources.d.ts +30 -0
- package/dist/compare/style-resources.js +404 -0
- package/dist/compare/types.d.ts +50 -11
- package/dist/compare/types.js +3 -1
- package/dist/compare/verification.d.ts +3 -5
- package/dist/compare/verification.js +8 -39
- package/dist/compat/eigenpal.d.ts +4 -4
- package/dist/compat/eigenpal.js +3 -3
- package/dist/document-operations.d.ts +5 -3
- package/dist/document-operations.js +112 -5
- package/dist/docx/blockContentParser.js +14 -2
- package/dist/docx/drawingIdNormalization.js +43 -2
- package/dist/docx/drawingRelationships.d.ts +10 -0
- package/dist/docx/drawingRelationships.js +28 -0
- package/dist/docx/fieldParser.js +1 -1
- package/dist/docx/hyperlinkParser.js +3 -5
- package/dist/docx/imageRawXml.d.ts +8 -0
- package/dist/docx/imageRawXml.js +13 -0
- package/dist/docx/newImage.d.ts +2 -2
- package/dist/docx/newImage.js +3 -3
- package/dist/docx/numberingParser.d.ts +1 -13
- package/dist/docx/numberingParser.js +31 -37
- package/dist/docx/paragraphParser.js +12 -9
- package/dist/docx/paragraphPropertySource.d.ts +8 -5
- package/dist/docx/paragraphPropertySource.js +16 -4
- package/dist/docx/removeHeaderFooterParts.d.ts +14 -0
- package/dist/docx/removeHeaderFooterParts.js +87 -0
- package/dist/docx/rezip.d.ts +3 -1
- package/dist/docx/rezip.js +143 -41
- package/dist/docx/runParser.js +16 -5
- package/dist/docx/sectionParser.js +3 -0
- package/dist/docx/sectionReferenceHistory.d.ts +17 -0
- package/dist/docx/sectionReferenceHistory.js +59 -0
- package/dist/docx/selectiveSave.js +11 -31
- package/dist/docx/selectiveXmlPatch.d.ts +8 -1
- package/dist/docx/selectiveXmlPatch.js +62 -2
- package/dist/docx/serializer/borderSerializer.d.ts +4 -1
- package/dist/docx/serializer/borderSerializer.js +20 -18
- package/dist/docx/serializer/commentSerializer.js +1 -1
- package/dist/docx/serializer/numberingSerializer.js +1 -1
- package/dist/docx/serializer/paragraphSerializer.js +13 -177
- package/dist/docx/serializer/partNamespaces.d.ts +1 -1
- package/dist/docx/serializer/partNamespaces.js +4 -0
- package/dist/docx/serializer/runSerializer.d.ts +1 -5
- package/dist/docx/serializer/runSerializer.js +4 -130
- package/dist/docx/serializer/sectionPropertiesSerializer.js +5 -2
- package/dist/docx/serializer/stylesSerializer.js +1 -1
- package/dist/docx/serializer/textFormattingSerializer.d.ts +17 -0
- package/dist/docx/serializer/textFormattingSerializer.js +144 -0
- package/dist/docx/tableParser.js +21 -11
- package/dist/docx/xmlParser.d.ts +10 -1
- package/dist/docx/xmlParser.js +23 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/internal/compare/inline-presentation.d.ts +20 -0
- package/dist/internal/compare/inline-presentation.js +222 -0
- package/dist/internal/paragraphFormattingSerialization.d.ts +25 -0
- package/dist/internal/paragraphFormattingSerialization.js +164 -0
- package/dist/internal/sectionReferenceResolution.d.ts +21 -0
- package/dist/internal/sectionReferenceResolution.js +72 -0
- package/dist/layout-bridge/convert/toFlowBlocks.js +1 -0
- package/dist/markdown/renderRuns.js +1 -0
- package/dist/markdown/renderTable.js +1 -0
- package/dist/prosemirror/attrs/index.js +20 -1
- package/dist/prosemirror/commands/comments.d.ts +11 -1
- package/dist/prosemirror/commands/comments.js +68 -6
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +9 -3
- package/dist/prosemirror/commands/propertyChangeScope.js +4 -10
- package/dist/prosemirror/conversion/fromProseDoc.js +22 -22
- package/dist/prosemirror/conversion/toProseDoc.js +16 -57
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -0
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +3 -0
- package/dist/prosemirror/imageCommit.js +7 -9
- package/dist/prosemirror/paragraphIndentation.d.ts +21 -0
- package/dist/prosemirror/paragraphIndentation.js +72 -0
- package/dist/prosemirror/plugins/documentNumbering.d.ts +3 -1
- package/dist/prosemirror/plugins/documentNumbering.js +7 -1
- package/dist/prosemirror/plugins/documentStyles.d.ts +3 -1
- package/dist/prosemirror/plugins/documentStyles.js +7 -1
- package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +3 -1
- package/dist/prosemirror/rebaseParagraphRunFormatting.js +2 -1
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +15 -2
- package/dist/prosemirror/runFormattingInlineCarriers.js +33 -3
- package/dist/prosemirror/runStyleFormatting.d.ts +15 -2
- package/dist/prosemirror/runStyleFormatting.js +76 -2
- package/dist/prosemirror/schema/marks.d.ts +3 -1
- package/dist/prosemirror/schema/marks.js +2 -0
- package/dist/prosemirror/schema/nodes.d.ts +3 -0
- package/dist/prosemirror/tableCellRevisionVisibility.d.ts +6 -0
- package/dist/prosemirror/tableCellRevisionVisibility.js +14 -0
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -1
- package/dist/prosemirror/trackedRunInlineAtoms.js +1 -1
- package/dist/redline.js +15 -2
- package/dist/server.d.ts +3 -2
- package/dist/server.js +2 -2
- package/dist/version-comparison.d.ts +2 -0
- package/dist/version-comparison.js +30 -29
- package/package.json +2 -2
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { isUnsafePackagePath } from "./packageParts.js";
|
|
2
|
+
import { parseRelationships, resolveRelativePath } from "./relsParser.js";
|
|
3
|
+
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
4
|
+
import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, cloneElement, getAttribute, getLocalName, getNamespaceUri, parseXmlDocument } from "./xmlParser.js";
|
|
5
|
+
import { panic } from "better-result";
|
|
6
|
+
import { OOXML_NS } from "@stll/docx-utils";
|
|
7
|
+
//#region src/docx/removeHeaderFooterParts.ts
|
|
8
|
+
const DOCUMENT_RELS_PATH = "word/_rels/document.xml.rels";
|
|
9
|
+
const officeRelationshipHasType = (type, kind) => [...OFFICE_RELATIONSHIP_NAMESPACE_URIS].some((namespace) => type === `${namespace}/${kind}`);
|
|
10
|
+
const withoutChildren = (xml, remove) => {
|
|
11
|
+
const root = parseXmlDocument(xml);
|
|
12
|
+
if (!root) return panic("Cannot update malformed package metadata");
|
|
13
|
+
return captureVerbatimXml(cloneElement(root, { elements: (root.elements ?? []).filter((child) => !remove(child)) }));
|
|
14
|
+
};
|
|
15
|
+
/** Remove only parts whose last selection was explicitly resolved out of the document. */
|
|
16
|
+
const removeResolvedHeaderFooterParts = async ({ document, zip, removedReferences, compressionLevel }) => {
|
|
17
|
+
if (removedReferences.length === 0) return;
|
|
18
|
+
const relsFile = zip.file(DOCUMENT_RELS_PATH);
|
|
19
|
+
if (!relsFile) return panic("A resolved header/footer part has no document relationships");
|
|
20
|
+
const relsXml = await relsFile.async("text");
|
|
21
|
+
const relationships = parseRelationships(relsXml);
|
|
22
|
+
const removedIds = /* @__PURE__ */ new Set();
|
|
23
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
24
|
+
for (const { part, relationshipId } of removedReferences) {
|
|
25
|
+
if ((part === "header" ? document.package.headers : document.package.footers)?.has(relationshipId)) continue;
|
|
26
|
+
const relationship = relationships.get(relationshipId);
|
|
27
|
+
if (!relationship) continue;
|
|
28
|
+
if (!officeRelationshipHasType(relationship.type, part) || relationship.targetMode === "External") return panic("Resolved header/footer relationship has an unexpected part type");
|
|
29
|
+
const path = resolveRelativePath(DOCUMENT_RELS_PATH, relationship.target);
|
|
30
|
+
if (isUnsafePackagePath(path)) return panic("Resolved header/footer has an unsafe package path");
|
|
31
|
+
removedIds.add(relationshipId);
|
|
32
|
+
candidates.add(path);
|
|
33
|
+
}
|
|
34
|
+
if (removedIds.size === 0) return;
|
|
35
|
+
for (const relationship of relationships.values()) {
|
|
36
|
+
if (removedIds.has(relationship.id) || relationship.targetMode === "External") continue;
|
|
37
|
+
candidates.delete(resolveRelativePath(DOCUMENT_RELS_PATH, relationship.target));
|
|
38
|
+
}
|
|
39
|
+
for (const file of Object.values(zip.files)) {
|
|
40
|
+
if (file.dir || !file.name.endsWith(".rels") || file.name === DOCUMENT_RELS_PATH) continue;
|
|
41
|
+
const otherRelationships = parseRelationships(await file.async("text"));
|
|
42
|
+
for (const relationship of otherRelationships.values()) if (relationship.targetMode !== "External") candidates.delete(resolveRelativePath(file.name, relationship.target));
|
|
43
|
+
}
|
|
44
|
+
const retiredMedia = /* @__PURE__ */ new Set();
|
|
45
|
+
for (const path of candidates) {
|
|
46
|
+
const slash = path.lastIndexOf("/");
|
|
47
|
+
const relsPath = `${path.slice(0, slash + 1)}_rels/${path.slice(slash + 1)}.rels`;
|
|
48
|
+
const partRels = zip.file(relsPath);
|
|
49
|
+
if (!partRels) continue;
|
|
50
|
+
for (const relationship of parseRelationships(await partRels.async("text")).values()) {
|
|
51
|
+
if (!officeRelationshipHasType(relationship.type, "image") || relationship.targetMode === "External") continue;
|
|
52
|
+
const mediaPath = resolveRelativePath(relsPath, relationship.target);
|
|
53
|
+
if (!isUnsafePackagePath(mediaPath) && mediaPath.startsWith("word/media/")) retiredMedia.add(mediaPath);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const compressionOptions = { level: compressionLevel };
|
|
57
|
+
zip.file(DOCUMENT_RELS_PATH, withoutChildren(relsXml, (child) => getNamespaceUri(child) === OOXML_NS.pr && getLocalName(child.name) === "Relationship" && removedIds.has(getAttribute(child, null, "Id") ?? "")), {
|
|
58
|
+
compression: "DEFLATE",
|
|
59
|
+
compressionOptions
|
|
60
|
+
});
|
|
61
|
+
if (document.package.relationships) {
|
|
62
|
+
document.package.relationships = new Map(document.package.relationships);
|
|
63
|
+
for (const id of removedIds) document.package.relationships.delete(id);
|
|
64
|
+
}
|
|
65
|
+
for (const path of candidates) {
|
|
66
|
+
zip.remove(path);
|
|
67
|
+
const slash = path.lastIndexOf("/");
|
|
68
|
+
zip.remove(`${path.slice(0, slash + 1)}_rels/${path.slice(slash + 1)}.rels`);
|
|
69
|
+
}
|
|
70
|
+
for (const file of Object.values(zip.files)) {
|
|
71
|
+
if (file.dir || !file.name.endsWith(".rels")) continue;
|
|
72
|
+
for (const relationship of parseRelationships(await file.async("text")).values()) if (relationship.targetMode !== "External") retiredMedia.delete(resolveRelativePath(file.name, relationship.target));
|
|
73
|
+
}
|
|
74
|
+
for (const path of retiredMedia) {
|
|
75
|
+
zip.remove(path);
|
|
76
|
+
candidates.add(path);
|
|
77
|
+
}
|
|
78
|
+
const contentTypes = zip.file("[Content_Types].xml");
|
|
79
|
+
if (!contentTypes) return panic("The package has no content types");
|
|
80
|
+
const contentTypesXml = await contentTypes.async("text");
|
|
81
|
+
zip.file("[Content_Types].xml", withoutChildren(contentTypesXml, (child) => getNamespaceUri(child) === OOXML_NS.ct && getLocalName(child.name) === "Override" && candidates.has((getAttribute(child, null, "PartName") ?? "").replace(/^\//u, ""))), {
|
|
82
|
+
compression: "DEFLATE",
|
|
83
|
+
compressionOptions
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
//#endregion
|
|
87
|
+
export { removeResolvedHeaderFooterParts };
|
package/dist/docx/rezip.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ declare function findMaxRId(relsXml: string): number;
|
|
|
15
15
|
* These are newly created hyperlinks that need relationship entries.
|
|
16
16
|
*/
|
|
17
17
|
declare function collectHyperlinksWithoutRId(blocks: BlockContent[]): Hyperlink[];
|
|
18
|
+
/** The selective save boundary must use the same resource census as full repack. */
|
|
19
|
+
declare const hasUnmaterializedInlineResources: (blocks: BlockContent[]) => boolean;
|
|
18
20
|
/**
|
|
19
21
|
* Options for repacking DOCX
|
|
20
22
|
*/
|
|
@@ -197,4 +199,4 @@ declare function createEmptyDocx(): Promise<ArrayBuffer>;
|
|
|
197
199
|
*/
|
|
198
200
|
declare function createDocx(doc: document_d_exports.Document): Promise<ArrayBuffer>;
|
|
199
201
|
//#endregion
|
|
200
|
-
export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, DocxPackageFidelityError, RepackOptions, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, collectHyperlinksWithoutRId, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, isDocxBuffer, notePartRelsPath, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx, withoutAttachedTemplate };
|
|
202
|
+
export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, DocxPackageFidelityError, RepackOptions, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, collectHyperlinksWithoutRId, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, hasUnmaterializedInlineResources, isDocxBuffer, notePartRelsPath, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx, withoutAttachedTemplate };
|
package/dist/docx/rezip.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { consumeTrackedSectionEndpointRemoval } from "../internal/sectionEndpointResolution.js";
|
|
2
|
+
import { consumeSectionReferenceResolution } from "../internal/sectionReferenceResolution.js";
|
|
2
3
|
import { isAllowedExternalWatermarkImageUrl } from "../watermark/index.js";
|
|
3
4
|
import { normalizeAppVersionInExtendedProperties } from "./appVersionNormalization.js";
|
|
4
5
|
import { withoutOrphanCommentRanges } from "./commentRangeIntegrity.js";
|
|
5
6
|
import { applyReplyThreadMarkers } from "./commentReplyMarkers.js";
|
|
7
|
+
import { normalizeDrawingIds } from "./drawingIdNormalization.js";
|
|
8
|
+
import { rebindDrawingImageRelationship } from "./drawingRelationships.js";
|
|
6
9
|
import { parseEndnotes, parseFootnotes } from "./footnoteParser.js";
|
|
7
10
|
import { assertValidFolioDocumentModel } from "./modelValidation.js";
|
|
8
11
|
import { isNewDataUrlDrawing } from "./newImage.js";
|
|
@@ -10,8 +13,9 @@ import { parseNumbering } from "./numberingParser.js";
|
|
|
10
13
|
import { isUnsafePackagePath, reconcilePackageReferences, removeUnsafeEntries } from "./packageParts.js";
|
|
11
14
|
import { normalizeParaIdRangeInXmlParts } from "./paraIdRangeNormalization.js";
|
|
12
15
|
import { RELATIONSHIP_TYPES, parseRelationships, resolveRelativePath } from "./relsParser.js";
|
|
16
|
+
import { removeResolvedHeaderFooterParts } from "./removeHeaderFooterParts.js";
|
|
13
17
|
import { normalizeRevisionIdsInXmlParts } from "./revisionIdNormalization.js";
|
|
14
|
-
import {
|
|
18
|
+
import { buildPatchedNotePartXml, collectChangedNoteParaIds, collectParaIds, patchNumberingDefinitions } from "./selectiveXmlPatch.js";
|
|
15
19
|
import { ensureThreadedCommentParaIds, serializeComments, serializeCommentsExtended } from "./serializer/commentSerializer.js";
|
|
16
20
|
import { serializeDocument } from "./serializer/documentSerializer.js";
|
|
17
21
|
import { serializeFontTableXml } from "./serializer/fontTableSerializer.js";
|
|
@@ -23,7 +27,7 @@ import { serializeSettingsXml } from "./serializer/settingsSerializer.js";
|
|
|
23
27
|
import { serializeStyle, serializeStylesXml } from "./serializer/stylesSerializer.js";
|
|
24
28
|
import { serializeThemeXml } from "./serializer/themeSerializer.js";
|
|
25
29
|
import { escapeXml } from "./serializer/xmlUtils.js";
|
|
26
|
-
import { WORDPROCESSINGML_NAMESPACE_URIS, findChild, getAttribute, getChildElements, getLocalName, getNamespaceUri, matchesName, parseXml, parseXmlDocument } from "./xmlParser.js";
|
|
30
|
+
import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, WORDPROCESSINGML_NAMESPACE_URIS, findChild, getAttribute, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespaceUri, matchesName, parseXml, parseXmlDocument } from "./xmlParser.js";
|
|
27
31
|
import { assertXmlResourceLimits } from "./xmlResourceLimits.js";
|
|
28
32
|
import { panic } from "better-result";
|
|
29
33
|
import { validateDocxPackage } from "@stll/docx-core";
|
|
@@ -99,17 +103,21 @@ const countDocumentSections = (xml) => {
|
|
|
99
103
|
};
|
|
100
104
|
const extractHeaderFooterReferences = (xml) => {
|
|
101
105
|
const references = [];
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
assertXmlResourceLimits(xml);
|
|
107
|
+
const pending = [parseXml(xml)];
|
|
108
|
+
while (pending.length > 0) {
|
|
109
|
+
const node = pending.pop();
|
|
110
|
+
if (!node) break;
|
|
111
|
+
const element = getLocalName(node.name);
|
|
112
|
+
if ((element === "headerReference" || element === "footerReference") && isWordprocessingElement(node, element)) {
|
|
113
|
+
const rId = getAttributeByNamespaceUri(node, OFFICE_RELATIONSHIP_NAMESPACE_URIS, "id");
|
|
114
|
+
if (rId) references.push({
|
|
115
|
+
element,
|
|
116
|
+
type: getAttributeByNamespaceUri(node, WORDPROCESSINGML_NAMESPACE_URIS, "type") ?? "default",
|
|
117
|
+
rId
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
pending.push(...getChildElements(node));
|
|
113
121
|
}
|
|
114
122
|
return references;
|
|
115
123
|
};
|
|
@@ -126,7 +134,7 @@ const consumeReferenceCount = (counts, key) => {
|
|
|
126
134
|
counts.set(key, count - 1);
|
|
127
135
|
return true;
|
|
128
136
|
};
|
|
129
|
-
function assertDocumentPackageFidelity(originalDocumentXml, serializedDocumentXml, doc, sectionEndpointRemoval) {
|
|
137
|
+
function assertDocumentPackageFidelity({ originalDocumentXml, serializedDocumentXml, doc, sectionEndpointRemoval, sectionReferenceRemovals = [] }) {
|
|
130
138
|
const originalSectionCount = countDocumentSections(originalDocumentXml);
|
|
131
139
|
const serializedSectionCount = countDocumentSections(serializedDocumentXml);
|
|
132
140
|
const trackedRemovedSectionCount = sectionEndpointRemoval ? sectionEndpointRemoval.sourceParagraphEndpointCount - sectionEndpointRemoval.expectedParagraphEndpointCount : 0;
|
|
@@ -136,6 +144,9 @@ function assertDocumentPackageFidelity(originalDocumentXml, serializedDocumentXm
|
|
|
136
144
|
for (const reference of extractHeaderFooterReferences(serializedDocumentXml)) incrementReferenceCount(serializedReferenceCounts, headerFooterReferenceKey(reference));
|
|
137
145
|
const removedReferenceCounts = /* @__PURE__ */ new Map();
|
|
138
146
|
if (exactTrackedSectionRemoval && sectionEndpointRemoval) for (const { part, type, relationshipId } of sectionEndpointRemoval.removedReferences) incrementReferenceCount(removedReferenceCounts, `${part}Reference:${type}:${relationshipId}`);
|
|
147
|
+
const resolvedReferenceCounts = /* @__PURE__ */ new Map();
|
|
148
|
+
for (const { part, type, relationshipId } of sectionReferenceRemovals) incrementReferenceCount(resolvedReferenceCounts, `${part}Reference:${type}:${relationshipId}`);
|
|
149
|
+
for (const [key, count] of resolvedReferenceCounts) removedReferenceCounts.set(key, Math.max(count, removedReferenceCounts.get(key) ?? 0));
|
|
139
150
|
const missingRefs = [];
|
|
140
151
|
for (const reference of extractHeaderFooterReferences(originalDocumentXml)) {
|
|
141
152
|
if (!hasParsedHeaderFooterPart(doc, reference)) continue;
|
|
@@ -314,7 +325,7 @@ async function registerImageExtensions(zip, extensions, compressionLevel) {
|
|
|
314
325
|
function collectNewImages(blocks) {
|
|
315
326
|
const images = [];
|
|
316
327
|
const collectFromRun = (run) => {
|
|
317
|
-
for (const c of run.content) if (isNewDataUrlDrawing(c)) images.push(c
|
|
328
|
+
for (const c of run.content) if (isNewDataUrlDrawing(c)) images.push(c);
|
|
318
329
|
};
|
|
319
330
|
for (const block of blocks) if (block.type === "paragraph") {
|
|
320
331
|
for (const item of block.content) if (item.type === "run") collectFromRun(item);
|
|
@@ -370,7 +381,8 @@ async function processNewImages(parts, zip, compressionLevel) {
|
|
|
370
381
|
let maxId = findMaxRId(relsXml);
|
|
371
382
|
const relEntries = [];
|
|
372
383
|
const partPath = relsPath.replace("/_rels/", "/").replace(/\.rels$/u, "");
|
|
373
|
-
for (const
|
|
384
|
+
for (const drawing of newImages) {
|
|
385
|
+
const { image } = drawing;
|
|
374
386
|
if (!image.src) continue;
|
|
375
387
|
const { data, extension } = decodeDataUrl(image.src);
|
|
376
388
|
maxImageNum++;
|
|
@@ -383,6 +395,11 @@ async function processNewImages(parts, zip, compressionLevel) {
|
|
|
383
395
|
});
|
|
384
396
|
relEntries.push(`<Relationship Id="${newRId}" Type="${RELATIONSHIP_TYPES.image}" Target="${escapeXml(relativeTargetForPart(partPath, mediaPath))}"/>`);
|
|
385
397
|
extensionsAdded.add(extension);
|
|
398
|
+
if (drawing.rawXml) drawing.rawXml = rebindDrawingImageRelationship({
|
|
399
|
+
xml: drawing.rawXml,
|
|
400
|
+
previousId: image.rId ?? "",
|
|
401
|
+
nextId: newRId
|
|
402
|
+
}) ?? panic("A detached drawing lost its embedded image relationship");
|
|
386
403
|
image.rId = newRId;
|
|
387
404
|
}
|
|
388
405
|
if (relEntries.length > 0) {
|
|
@@ -395,18 +412,51 @@ async function processNewImages(parts, zip, compressionLevel) {
|
|
|
395
412
|
}
|
|
396
413
|
await registerImageExtensions(zip, extensionsAdded, compressionLevel);
|
|
397
414
|
}
|
|
415
|
+
/** Materialize detached embedded media without replacing any existing package part. */
|
|
416
|
+
const materializeEmbeddedMedia = async ({ document, zip, compressionLevel }) => {
|
|
417
|
+
const extensions = /* @__PURE__ */ new Set();
|
|
418
|
+
for (const media of document.package.media?.values() ?? []) {
|
|
419
|
+
if (findZipEntryCaseInsensitive(zip, media.path.toLowerCase())) continue;
|
|
420
|
+
if (isUnsafePackagePath(media.path) || !media.path.startsWith("word/media/")) panic("Detached media has an invalid package path");
|
|
421
|
+
const extension = media.path.split(".").at(-1)?.toLowerCase();
|
|
422
|
+
if (!extension || !/^[a-z0-9]+$/u.test(extension)) panic("Embedded media has an invalid extension");
|
|
423
|
+
zip.file(media.path, media.data, {
|
|
424
|
+
compression: "DEFLATE",
|
|
425
|
+
compressionOptions: { level: compressionLevel }
|
|
426
|
+
});
|
|
427
|
+
extensions.add(extension);
|
|
428
|
+
}
|
|
429
|
+
await registerImageExtensions(zip, extensions, compressionLevel);
|
|
430
|
+
};
|
|
398
431
|
/**
|
|
399
432
|
* Collect all hyperlinks that have an href but no rId from block content.
|
|
400
433
|
* These are newly created hyperlinks that need relationship entries.
|
|
401
434
|
*/
|
|
402
435
|
function collectHyperlinksWithoutRId(blocks) {
|
|
403
436
|
const hyperlinks = [];
|
|
404
|
-
|
|
405
|
-
for (const item of
|
|
406
|
-
|
|
437
|
+
const collectInlineHyperlinks = (content) => {
|
|
438
|
+
for (const item of content) switch (item.type) {
|
|
439
|
+
case "hyperlink":
|
|
440
|
+
if (item.href && !item.rId && !item.anchor) hyperlinks.push(item);
|
|
441
|
+
break;
|
|
442
|
+
case "simpleField":
|
|
443
|
+
case "inlineSdt":
|
|
444
|
+
case "insertion":
|
|
445
|
+
case "deletion":
|
|
446
|
+
case "moveFrom":
|
|
447
|
+
case "moveTo":
|
|
448
|
+
collectInlineHyperlinks(item.content);
|
|
449
|
+
break;
|
|
450
|
+
default: break;
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
for (const block of blocks) if (block.type === "paragraph") collectInlineHyperlinks(block.content);
|
|
454
|
+
else if (block.type === "table") for (const row of block.rows) for (const cell of row.cells) hyperlinks.push(...collectHyperlinksWithoutRId(cell.content));
|
|
407
455
|
else hyperlinks.push(...collectHyperlinksWithoutRId(block.content));
|
|
408
456
|
return hyperlinks;
|
|
409
457
|
}
|
|
458
|
+
/** The selective save boundary must use the same resource census as full repack. */
|
|
459
|
+
const hasUnmaterializedInlineResources = (blocks) => collectNewImages(blocks).length > 0 || collectHyperlinksWithoutRId(blocks).length > 0;
|
|
410
460
|
/**
|
|
411
461
|
* Process newly created hyperlinks in every part: assign rIds and add
|
|
412
462
|
* relationship entries to the owning part's rels. Mutates the hyperlinks' rId
|
|
@@ -512,15 +562,42 @@ const cloneDocxZip = (source) => {
|
|
|
512
562
|
clone.files = { ...source.files };
|
|
513
563
|
return clone;
|
|
514
564
|
};
|
|
515
|
-
const
|
|
565
|
+
const normalizeExportDrawingIds = ({ package: docxPackage }) => {
|
|
566
|
+
normalizeDrawingIds({
|
|
567
|
+
documentBody: docxPackage.document,
|
|
568
|
+
...docxPackage.headers !== void 0 ? { headers: docxPackage.headers } : {},
|
|
569
|
+
...docxPackage.footers !== void 0 ? { footers: docxPackage.footers } : {},
|
|
570
|
+
...docxPackage.footnotes !== void 0 ? { footnotes: docxPackage.footnotes } : {},
|
|
571
|
+
...docxPackage.endnotes !== void 0 ? { endnotes: docxPackage.endnotes } : {}
|
|
572
|
+
});
|
|
573
|
+
};
|
|
574
|
+
const finishRepack = async ({ document, originalZip, outputZip, originalDocumentXml, originalCorePropertiesXml, compressionLevel, updateModifiedDate, modifiedBy, changedNoteParaIds, sectionEndpointRemoval, sectionReferenceRemovals }) => {
|
|
516
575
|
await materializeNewHeaderFooterParts(document, outputZip, compressionLevel);
|
|
576
|
+
await materializeEmbeddedMedia({
|
|
577
|
+
document,
|
|
578
|
+
zip: outputZip,
|
|
579
|
+
compressionLevel
|
|
580
|
+
});
|
|
517
581
|
const parts = collectDocxParts(document, outputZip);
|
|
582
|
+
normalizeExportDrawingIds(document);
|
|
518
583
|
await processNewImages(parts, outputZip, compressionLevel);
|
|
519
584
|
await processNewHyperlinks(parts, outputZip, compressionLevel);
|
|
520
585
|
assertValidFolioDocumentModel(document, "Cannot repack invalid DOCX document model");
|
|
521
586
|
applyReplyThreadMarkers(document);
|
|
522
587
|
const documentXml = serializeDocument(document, originalDocumentXml === void 0 ? void 0 : readRootNamespaceBindings(originalDocumentXml));
|
|
523
|
-
if (originalDocumentXml) assertDocumentPackageFidelity(
|
|
588
|
+
if (originalDocumentXml) assertDocumentPackageFidelity({
|
|
589
|
+
originalDocumentXml,
|
|
590
|
+
serializedDocumentXml: documentXml,
|
|
591
|
+
doc: document,
|
|
592
|
+
...sectionEndpointRemoval !== void 0 && { sectionEndpointRemoval },
|
|
593
|
+
...sectionReferenceRemovals !== void 0 && { sectionReferenceRemovals }
|
|
594
|
+
});
|
|
595
|
+
await removeResolvedHeaderFooterParts({
|
|
596
|
+
document,
|
|
597
|
+
zip: outputZip,
|
|
598
|
+
removedReferences: [...sectionReferenceRemovals ?? [], ...sectionEndpointRemoval?.removedReferences ?? []],
|
|
599
|
+
compressionLevel
|
|
600
|
+
});
|
|
524
601
|
outputZip.file("word/document.xml", documentXml, {
|
|
525
602
|
compression: "DEFLATE",
|
|
526
603
|
compressionOptions: { level: compressionLevel }
|
|
@@ -550,7 +627,7 @@ const finishRepack = async ({ document, originalZip, outputZip, originalDocument
|
|
|
550
627
|
}
|
|
551
628
|
return generateDocxZip(outputZip, compressionLevel);
|
|
552
629
|
};
|
|
553
|
-
async function repackDocxWithSectionEndpointRemoval({ document: doc, options, sectionEndpointRemoval }) {
|
|
630
|
+
async function repackDocxWithSectionEndpointRemoval({ document: doc, options, sectionEndpointRemoval, sectionReferenceRemovals }) {
|
|
554
631
|
if (!doc.originalBuffer) panic("Cannot repack document: no original buffer for round-trip. Use createDocx() for new documents.");
|
|
555
632
|
const { compressionLevel = 6, updateModifiedDate = true, modifiedBy, changedNoteParaIds } = options;
|
|
556
633
|
const exportDocument = withoutOrphanCommentRanges(doc);
|
|
@@ -568,15 +645,18 @@ async function repackDocxWithSectionEndpointRemoval({ document: doc, options, se
|
|
|
568
645
|
updateModifiedDate,
|
|
569
646
|
...modifiedBy !== void 0 ? { modifiedBy } : {},
|
|
570
647
|
...changedNoteParaIds !== void 0 ? { changedNoteParaIds } : {},
|
|
571
|
-
...sectionEndpointRemoval !== void 0 ? { sectionEndpointRemoval } : {}
|
|
648
|
+
...sectionEndpointRemoval !== void 0 ? { sectionEndpointRemoval } : {},
|
|
649
|
+
...sectionReferenceRemovals !== void 0 ? { sectionReferenceRemovals } : {}
|
|
572
650
|
});
|
|
573
651
|
}
|
|
574
652
|
function repackDocx(doc, options = {}) {
|
|
575
653
|
const sectionEndpointRemoval = consumeTrackedSectionEndpointRemoval(doc);
|
|
654
|
+
const sectionReferenceRemovals = consumeSectionReferenceResolution(doc);
|
|
576
655
|
return repackDocxWithSectionEndpointRemoval({
|
|
577
656
|
document: doc,
|
|
578
657
|
options,
|
|
579
|
-
...sectionEndpointRemoval ? { sectionEndpointRemoval } : {}
|
|
658
|
+
...sectionEndpointRemoval ? { sectionEndpointRemoval } : {},
|
|
659
|
+
sectionReferenceRemovals
|
|
580
660
|
});
|
|
581
661
|
}
|
|
582
662
|
/**
|
|
@@ -604,13 +684,23 @@ async function repackDocxFromRaw(doc, rawContent, options = {}) {
|
|
|
604
684
|
});
|
|
605
685
|
}
|
|
606
686
|
await materializeNewHeaderFooterParts(exportDocument, newZip, compressionLevel);
|
|
687
|
+
await materializeEmbeddedMedia({
|
|
688
|
+
document: exportDocument,
|
|
689
|
+
zip: newZip,
|
|
690
|
+
compressionLevel
|
|
691
|
+
});
|
|
607
692
|
const parts = collectDocxParts(exportDocument, newZip);
|
|
693
|
+
normalizeExportDrawingIds(exportDocument);
|
|
608
694
|
await processNewImages(parts, newZip, compressionLevel);
|
|
609
695
|
await processNewHyperlinks(parts, newZip, compressionLevel);
|
|
610
696
|
assertValidFolioDocumentModel(exportDocument, "Cannot repack invalid DOCX document model");
|
|
611
697
|
applyReplyThreadMarkers(exportDocument);
|
|
612
698
|
const documentXml = serializeDocument(exportDocument, rawContent.documentXml ? readRootNamespaceBindings(rawContent.documentXml) : void 0);
|
|
613
|
-
if (rawContent.documentXml) assertDocumentPackageFidelity(
|
|
699
|
+
if (rawContent.documentXml) assertDocumentPackageFidelity({
|
|
700
|
+
originalDocumentXml: rawContent.documentXml,
|
|
701
|
+
serializedDocumentXml: documentXml,
|
|
702
|
+
doc: exportDocument
|
|
703
|
+
});
|
|
614
704
|
newZip.file("word/document.xml", documentXml, {
|
|
615
705
|
compression: "DEFLATE",
|
|
616
706
|
compressionOptions: { level: compressionLevel }
|
|
@@ -1056,9 +1146,8 @@ function hasModelDrivenPictureWatermark(doc) {
|
|
|
1056
1146
|
* by coverage) carries the source header's rId, which is meaningless in a
|
|
1057
1147
|
* sibling header's `word/_rels/header*.xml.rels`. Per header: keep the rId if
|
|
1058
1148
|
* it already resolves; otherwise reuse an existing relationship to the same
|
|
1059
|
-
* media target, or mint a new one.
|
|
1060
|
-
*
|
|
1061
|
-
* byte-exact and skipped.
|
|
1149
|
+
* media target, or mint a new one. Media bytes are materialized before this step. Raw watermark XML
|
|
1150
|
+
* changes only when its image relationship must be rebound.
|
|
1062
1151
|
*/
|
|
1063
1152
|
async function rebindWatermarkRelIds(doc, zip, compressionLevel) {
|
|
1064
1153
|
const rels = doc.package.relationships;
|
|
@@ -1067,11 +1156,12 @@ async function rebindWatermarkRelIds(doc, zip, compressionLevel) {
|
|
|
1067
1156
|
const pending = [];
|
|
1068
1157
|
for (const [rId, hf] of headers) {
|
|
1069
1158
|
const watermark = hf.watermark;
|
|
1070
|
-
if (!watermark || watermark.kind !== "picture"
|
|
1159
|
+
if (!watermark || watermark.kind !== "picture") continue;
|
|
1071
1160
|
const rel = rels.get(rId);
|
|
1072
1161
|
if (!rel?.target) continue;
|
|
1073
1162
|
pending.push({
|
|
1074
1163
|
watermark,
|
|
1164
|
+
header: hf,
|
|
1075
1165
|
relsPath: headerFooterRelsPath(rel.target),
|
|
1076
1166
|
partPath: headerFooterFilename(rel.target)
|
|
1077
1167
|
});
|
|
@@ -1099,7 +1189,7 @@ async function rebindWatermarkRelIds(doc, zip, compressionLevel) {
|
|
|
1099
1189
|
}
|
|
1100
1190
|
};
|
|
1101
1191
|
const changedPaths = /* @__PURE__ */ new Set();
|
|
1102
|
-
for (const { watermark, relsPath, partPath } of pending) {
|
|
1192
|
+
for (const { watermark, header, relsPath, partPath } of pending) {
|
|
1103
1193
|
let canonical;
|
|
1104
1194
|
if (watermark.imageTarget !== void 0) if (watermark.imageTargetExternal) canonical = isAllowedExternalWatermarkImageUrl(watermark.imageTarget) ? {
|
|
1105
1195
|
mode: "external",
|
|
@@ -1129,6 +1219,11 @@ async function rebindWatermarkRelIds(doc, zip, compressionLevel) {
|
|
|
1129
1219
|
relsXmlByPath.set(relsPath, relsXml.replace("</Relationships>", `${relXml}</Relationships>`));
|
|
1130
1220
|
changedPaths.add(relsPath);
|
|
1131
1221
|
}
|
|
1222
|
+
if (header.rawWatermarkXml && watermark.imageRId !== resolvedRId) header.rawWatermarkXml = rebindDrawingImageRelationship({
|
|
1223
|
+
xml: header.rawWatermarkXml,
|
|
1224
|
+
previousId: watermark.imageRId,
|
|
1225
|
+
nextId: resolvedRId
|
|
1226
|
+
}) ?? panic("A watermark lost its embedded image relationship");
|
|
1132
1227
|
watermark.imageRId = resolvedRId;
|
|
1133
1228
|
}
|
|
1134
1229
|
const compressionOptions = { level: compressionLevel };
|
|
@@ -1257,7 +1352,17 @@ async function serializeNumberingIntoZip(doc, originalZip, newZip, compressionLe
|
|
|
1257
1352
|
const numbering = doc.package.numbering;
|
|
1258
1353
|
if (!numbering || numbering.abstractNums.length === 0 && numbering.nums.length === 0) return;
|
|
1259
1354
|
const file = findNotePartEntry(originalZip, "word/numbering.xml");
|
|
1260
|
-
if (!file)
|
|
1355
|
+
if (!file) {
|
|
1356
|
+
await materializeNewNotePart({
|
|
1357
|
+
contentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
|
|
1358
|
+
newZip,
|
|
1359
|
+
partPath: "word/numbering.xml",
|
|
1360
|
+
relationshipType: RELATIONSHIP_TYPES.numbering,
|
|
1361
|
+
serializedPart: serializeNumberingXml(numbering),
|
|
1362
|
+
compressionLevel
|
|
1363
|
+
});
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1261
1366
|
let baseline = doc.originalBuffer ? numberingBaselines.get(doc) : void 0;
|
|
1262
1367
|
if (!baseline || baseline.originalBuffer !== doc.originalBuffer) {
|
|
1263
1368
|
const originalXml = await file.async("text");
|
|
@@ -1269,15 +1374,12 @@ async function serializeNumberingIntoZip(doc, originalZip, newZip, compressionLe
|
|
|
1269
1374
|
if (doc.originalBuffer) numberingBaselines.set(doc, baseline);
|
|
1270
1375
|
}
|
|
1271
1376
|
const currentXml = serializeNumberingXml(numbering);
|
|
1272
|
-
const
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
if (spliced === null) return;
|
|
1279
|
-
const patched = appendNumberingDefs(spliced, currentXml, added);
|
|
1280
|
-
if (patched === null) return;
|
|
1377
|
+
const patched = patchNumberingDefinitions({
|
|
1378
|
+
originalXml: baseline.originalXml,
|
|
1379
|
+
baselineXml: baseline.serializedXml,
|
|
1380
|
+
currentXml
|
|
1381
|
+
});
|
|
1382
|
+
if (patched === null || patched === baseline.originalXml) return;
|
|
1281
1383
|
newZip.file(file.name, patched, {
|
|
1282
1384
|
compression: "DEFLATE",
|
|
1283
1385
|
compressionOptions: { level: compressionLevel }
|
|
@@ -1584,4 +1686,4 @@ const assertStyleNumberingReferences = (doc) => {
|
|
|
1584
1686
|
for (const numbering of doc.package.numbering?.nums ?? []) if (!availableAbstract.has(numbering.abstractNumId)) panic(`Numbering definition ${numbering.numId} references missing abstract numbering`);
|
|
1585
1687
|
};
|
|
1586
1688
|
//#endregion
|
|
1587
|
-
export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, DocxPackageFidelityError, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, collectHyperlinksWithoutRId, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, isDocxBuffer, notePartRelsPath, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx, withoutAttachedTemplate };
|
|
1689
|
+
export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, DocxPackageFidelityError, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, collectHyperlinksWithoutRId, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, hasUnmaterializedInlineResources, isDocxBuffer, notePartRelsPath, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx, withoutAttachedTemplate };
|
package/dist/docx/runParser.js
CHANGED
|
@@ -2,6 +2,7 @@ import { isValidHexColor } from "../utils/colorResolver.js";
|
|
|
2
2
|
import { parseHorizontalScalePercent } from "../utils/horizontalScale.js";
|
|
3
3
|
import { parseGroupDrawing } from "./groupDrawingParser.js";
|
|
4
4
|
import { parseImage } from "./imageParser.js";
|
|
5
|
+
import { imageRawXmlFingerprint } from "./imageRawXml.js";
|
|
5
6
|
import { EmphasisMarkSchema, FontHintSchema, FontThemeSchema, HighlightColorSchema, PositionalTabAlignmentSchema, PositionalTabLeaderSchema, PositionalTabRelativeToSchema, ShadingPatternSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum } from "./parserEnums.js";
|
|
6
7
|
import { parseShapeFromDrawing, shouldPreserveRawShapeDrawing } from "./shapeParser.js";
|
|
7
8
|
import { isTextBoxDrawing } from "./textBoxParser.js";
|
|
@@ -10,7 +11,7 @@ import { resolveThemeFontRef } from "./themeParser.js";
|
|
|
10
11
|
import { parsePropertyChangeInfo } from "./trackedChangeInfo.js";
|
|
11
12
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
12
13
|
import { parseVmlImageContent } from "./vmlImageParser.js";
|
|
13
|
-
import { cloneWithXmlnsDeclarations, findAllDeep, findChild, findChildren, getAttribute, getChildElements, getLocalName, getTextContent, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute } from "./xmlParser.js";
|
|
14
|
+
import { cloneWithXmlnsDeclarations, findAllDeep, findChild, findChildren, getAttribute, getChildElements, getLocalName, getTextContent, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, selectAlternateContentBranch } from "./xmlParser.js";
|
|
14
15
|
import { DRAWING_RAW_XML_MODES } from "@stll/docx-core/model";
|
|
15
16
|
//#region src/docx/runParser.ts
|
|
16
17
|
/**
|
|
@@ -76,6 +77,9 @@ function collectFirstRunPropertyChildren(rPr) {
|
|
|
76
77
|
case "color":
|
|
77
78
|
children.color ??= child;
|
|
78
79
|
break;
|
|
80
|
+
case "noProof":
|
|
81
|
+
children.noProof ??= child;
|
|
82
|
+
break;
|
|
79
83
|
case "cs":
|
|
80
84
|
children.cs ??= child;
|
|
81
85
|
break;
|
|
@@ -215,6 +219,8 @@ function parseRunProperties(rPr, theme, _styles) {
|
|
|
215
219
|
if (caps) formatting.allCaps = parseBooleanElement(caps);
|
|
216
220
|
const vanish = propertyChildren.vanish;
|
|
217
221
|
if (vanish) formatting.hidden = parseBooleanElement(vanish);
|
|
222
|
+
const noProof = propertyChildren.noProof;
|
|
223
|
+
if (noProof) formatting.noProof = parseBooleanElement(noProof);
|
|
218
224
|
const color = propertyChildren.color;
|
|
219
225
|
if (color) formatting.color = parseColorValue(getAttribute(color, "w", "val"), getAttribute(color, "w", "themeColor"), getAttribute(color, "w", "themeTint"), getAttribute(color, "w", "themeShade"));
|
|
220
226
|
const highlight = propertyChildren.highlight;
|
|
@@ -479,7 +485,8 @@ function parseDrawingContent(element, rels, media) {
|
|
|
479
485
|
if (groupImage) return {
|
|
480
486
|
type: "drawing",
|
|
481
487
|
image: groupImage,
|
|
482
|
-
rawXml: captureVerbatimXml(element)
|
|
488
|
+
rawXml: captureVerbatimXml(element),
|
|
489
|
+
rawImageFingerprint: imageRawXmlFingerprint(groupImage)
|
|
483
490
|
};
|
|
484
491
|
if (shouldPreserveRawShapeDrawing(element)) return {
|
|
485
492
|
type: "drawing",
|
|
@@ -506,7 +513,8 @@ function parseDrawingContent(element, rels, media) {
|
|
|
506
513
|
type: "drawing",
|
|
507
514
|
image
|
|
508
515
|
};
|
|
509
|
-
|
|
516
|
+
drawing.rawXml = captureVerbatimXml(element);
|
|
517
|
+
drawing.rawImageFingerprint = imageRawXmlFingerprint(image);
|
|
510
518
|
return drawing;
|
|
511
519
|
}
|
|
512
520
|
/**
|
|
@@ -595,7 +603,7 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
|
|
|
595
603
|
contents.push(fallbackVml);
|
|
596
604
|
break;
|
|
597
605
|
}
|
|
598
|
-
const targetEl =
|
|
606
|
+
const targetEl = selectAlternateContentBranch(child);
|
|
599
607
|
if (targetEl) for (const innerChild of getChildElements(targetEl)) {
|
|
600
608
|
const innerName = getLocalName(innerChild.name);
|
|
601
609
|
if (innerName === "drawing") {
|
|
@@ -610,7 +618,10 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
|
|
|
610
618
|
innerVml.rawXml = captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns));
|
|
611
619
|
contents.push(innerVml);
|
|
612
620
|
}
|
|
613
|
-
}
|
|
621
|
+
} else contents.push(...parseRunContents({
|
|
622
|
+
...targetEl,
|
|
623
|
+
elements: [innerChild]
|
|
624
|
+
}, rels, media, rootXmlns));
|
|
614
625
|
}
|
|
615
626
|
break;
|
|
616
627
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parseFooterReference, parseHeaderReference } from "./headerFooterRefParser.js";
|
|
2
2
|
import { parseEndnoteProperties, parseFootnoteProperties } from "./notePropertiesParser.js";
|
|
3
3
|
import { BorderStyleSchema, NumberFormatSchema, ThemeColorSlotSchema, narrowEnum } from "./parserEnums.js";
|
|
4
|
+
import { parseSectionReferenceHistory } from "./sectionReferenceHistory.js";
|
|
4
5
|
import { parsePropertyChangeInfo } from "./trackedChangeInfo.js";
|
|
5
6
|
import { findChild, findChildren, getAttribute, getChildElements, getLocalName, parseBooleanElement, parseNumericAttribute } from "./xmlParser.js";
|
|
6
7
|
//#region src/docx/sectionParser.ts
|
|
@@ -348,6 +349,8 @@ function parseSectionProperties(sectPr) {
|
|
|
348
349
|
info: parsePropertyChangeInfo(changeElement)
|
|
349
350
|
};
|
|
350
351
|
if (previousSectPr) change.previousProperties = parseSectionProperties(previousSectPr);
|
|
352
|
+
const previousReferences = parseSectionReferenceHistory(changeElement);
|
|
353
|
+
if (previousReferences !== void 0) change.previousReferences = previousReferences;
|
|
351
354
|
return change;
|
|
352
355
|
});
|
|
353
356
|
if (propertyChanges.length > 0) props.propertyChanges = propertyChanges;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { XmlElement } from "./xmlParser.js";
|
|
3
|
+
//#region src/docx/sectionReferenceHistory.d.ts
|
|
4
|
+
type PreviousReferences = NonNullable<document_d_exports.SectionPropertyChange["previousReferences"]>;
|
|
5
|
+
declare const InvalidSectionReferenceHistoryError_base: import("better-result").TaggedErrorClass<"InvalidSectionReferenceHistoryError">;
|
|
6
|
+
declare class InvalidSectionReferenceHistoryError extends InvalidSectionReferenceHistoryError_base<{
|
|
7
|
+
message: string;
|
|
8
|
+
}> {}
|
|
9
|
+
declare const parseSectionReferenceHistory: (change: XmlElement) => PreviousReferences | undefined;
|
|
10
|
+
declare const serializeSectionReferenceHistory: (references: PreviousReferences | undefined) => string;
|
|
11
|
+
declare const sectionReferenceSelection: (properties: document_d_exports.SectionProperties) => PreviousReferences;
|
|
12
|
+
declare const sectionReferenceHistory: ({ previous, target }: {
|
|
13
|
+
previous: document_d_exports.SectionProperties;
|
|
14
|
+
target: document_d_exports.SectionProperties;
|
|
15
|
+
}) => PreviousReferences | undefined;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { InvalidSectionReferenceHistoryError, parseSectionReferenceHistory, sectionReferenceHistory, sectionReferenceSelection, serializeSectionReferenceHistory };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { canonicalJson } from "../utils/canonicalJson.js";
|
|
2
|
+
import { escapeXml } from "./serializer/xmlUtils.js";
|
|
3
|
+
import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, WORDPROCESSINGML_NAMESPACE_URIS, findChildrenByNamespaceUri, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespaceUri } from "./xmlParser.js";
|
|
4
|
+
import { TaggedError } from "better-result";
|
|
5
|
+
//#region src/docx/sectionReferenceHistory.ts
|
|
6
|
+
const SECTION_REFERENCE_HISTORY_NAMESPACE = "urn:stella:folio:section-reference-history:1";
|
|
7
|
+
const HISTORY_NAMESPACES = /* @__PURE__ */ new Set([SECTION_REFERENCE_HISTORY_NAMESPACE]);
|
|
8
|
+
var InvalidSectionReferenceHistoryError = class extends TaggedError("InvalidSectionReferenceHistoryError") {};
|
|
9
|
+
const invalidHistory = () => {
|
|
10
|
+
throw new InvalidSectionReferenceHistoryError({ message: "Invalid section reference revision history." });
|
|
11
|
+
};
|
|
12
|
+
const parseSectionReferenceHistory = (change) => {
|
|
13
|
+
const histories = findChildrenByNamespaceUri(change, HISTORY_NAMESPACES, "previousReferences");
|
|
14
|
+
const history = histories.at(0);
|
|
15
|
+
if (!history) return void 0;
|
|
16
|
+
if (histories.length !== 1) return invalidHistory();
|
|
17
|
+
const headers = [];
|
|
18
|
+
const footers = [];
|
|
19
|
+
for (const child of getChildElements(history)) {
|
|
20
|
+
if (!WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(child) ?? "")) return invalidHistory();
|
|
21
|
+
const name = getLocalName(child.name);
|
|
22
|
+
if (name !== "headerReference" && name !== "footerReference") return invalidHistory();
|
|
23
|
+
const type = getAttributeByNamespaceUri(child, WORDPROCESSINGML_NAMESPACE_URIS, "type");
|
|
24
|
+
const rId = getAttributeByNamespaceUri(child, OFFICE_RELATIONSHIP_NAMESPACE_URIS, "id");
|
|
25
|
+
if (type !== "default" && type !== "first" && type !== "even" || !rId || rId.trim() !== rId) return invalidHistory();
|
|
26
|
+
const references = name === "headerReference" ? headers : footers;
|
|
27
|
+
if (references.some((reference) => reference.type === type)) return invalidHistory();
|
|
28
|
+
references.push({
|
|
29
|
+
type,
|
|
30
|
+
rId
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
...headers.length > 0 && { headerReferences: headers },
|
|
35
|
+
...footers.length > 0 && { footerReferences: footers }
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
const serializeSectionReferenceHistory = (references) => {
|
|
39
|
+
if (references === void 0) return "";
|
|
40
|
+
const serializeReferences = (kind, values) => {
|
|
41
|
+
if (new Set(values.map(({ type }) => type)).size !== values.length) return invalidHistory();
|
|
42
|
+
return values.map(({ type, rId }) => {
|
|
43
|
+
if (!rId || rId.trim() !== rId) return invalidHistory();
|
|
44
|
+
return `<w:${kind}Reference w:type="${type}" r:id="${escapeXml(rId)}"/>`;
|
|
45
|
+
}).join("");
|
|
46
|
+
};
|
|
47
|
+
const content = serializeReferences("header", references.headerReferences ?? []) + serializeReferences("footer", references.footerReferences ?? []);
|
|
48
|
+
return `<frh:previousReferences xmlns:frh="${SECTION_REFERENCE_HISTORY_NAMESPACE}" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="frh" mc:PreserveElements="frh:previousReferences">${content}</frh:previousReferences>`;
|
|
49
|
+
};
|
|
50
|
+
const sectionReferenceSelection = (properties) => ({
|
|
51
|
+
...properties.headerReferences !== void 0 && { headerReferences: properties.headerReferences },
|
|
52
|
+
...properties.footerReferences !== void 0 && { footerReferences: properties.footerReferences }
|
|
53
|
+
});
|
|
54
|
+
const sectionReferenceHistory = ({ previous, target }) => {
|
|
55
|
+
const before = sectionReferenceSelection(previous);
|
|
56
|
+
return canonicalJson(before) === canonicalJson(sectionReferenceSelection(target)) ? void 0 : before;
|
|
57
|
+
};
|
|
58
|
+
//#endregion
|
|
59
|
+
export { InvalidSectionReferenceHistoryError, parseSectionReferenceHistory, sectionReferenceHistory, sectionReferenceSelection, serializeSectionReferenceHistory };
|