@stll/folio-core 0.59.0 → 0.61.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/README.md +23 -1
- package/dist/ai-edits/apply.js +139 -32
- package/dist/ai-edits/headless.d.ts +3 -0
- package/dist/ai-edits/headless.js +57 -63
- package/dist/ai-edits/note-story-inventory.d.ts +7 -0
- package/dist/ai-edits/note-story-inventory.js +11 -0
- package/dist/ai-edits/read.js +16 -9
- package/dist/ai-edits/revisionStretches.js +52 -1
- package/dist/ai-edits/table-row-column-mutations.js +6 -1
- package/dist/compare/reproducible-package.js +2 -1
- package/dist/compare/verification.js +1 -0
- package/dist/compat/eigenpal.d.ts +4 -2
- package/dist/compat/eigenpal.js +2 -2
- package/dist/controller/canonicalClipboard.js +2 -1
- package/dist/controller/canonicalListRendering.d.ts +4 -6
- package/dist/controller/canonicalListRendering.js +3 -157
- package/dist/controller/canonicalSession.js +3 -2
- package/dist/controller/canonicalStructure.js +10 -7
- package/dist/controller/fontReadiness.js +1 -1
- package/dist/controller/headerFooterEditorManager.d.ts +1 -0
- package/dist/controller/headerFooterEditorManager.js +11 -0
- package/dist/controller/hiddenEditorManager.d.ts +1 -1
- package/dist/controller/hiddenEditorManager.js +3 -1
- package/dist/controller/layoutPipeline.d.ts +3 -1
- package/dist/controller/layoutPipeline.js +19 -4
- package/dist/controller/layoutSession.d.ts +4 -1
- package/dist/display-list/build/fontTable.js +7 -5
- package/dist/display-list/build/furniture.d.ts +1 -0
- package/dist/display-list/build/furniture.js +1 -0
- package/dist/display-list/build/imagePrimitives.js +3 -6
- package/dist/display-list/build/watermarkPrimitives.d.ts +2 -1
- package/dist/display-list/build/watermarkPrimitives.js +9 -3
- package/dist/display-list/editor/pageRenderer.js +1 -0
- package/dist/docx/bookmarkIdNormalization.d.ts +10 -0
- package/dist/docx/bookmarkIdNormalization.js +100 -0
- package/dist/docx/bookmarkParser.js +4 -2
- package/dist/docx/canonicalStoryRepack.js +3 -2
- package/dist/docx/commentTextParagraph.d.ts +6 -0
- package/dist/docx/commentTextParagraph.js +15 -0
- package/dist/docx/conformance.js +6 -1
- package/dist/docx/diagramPreview.js +2 -1
- package/dist/docx/documentParser.d.ts +13 -1
- package/dist/docx/documentParser.js +1 -1
- package/dist/docx/encryption/compoundFile.d.ts +0 -8
- package/dist/docx/encryption/compoundFile.js +2 -1
- package/dist/docx/encryption/cryptoBytes.d.ts +2 -2
- package/dist/docx/encryption/cryptoBytes.js +3 -18
- package/dist/docx/encryption/encryptionInfo.d.ts +0 -5
- package/dist/docx/encryption/encryptionInfo.js +2 -1
- package/dist/docx/ensureParaIds.js +2 -1
- package/dist/docx/groupDrawingParser.js +6 -1
- package/dist/docx/importedIdCollisions.d.ts +136 -0
- package/dist/docx/importedIdCollisions.js +215 -0
- package/dist/docx/listModelNormalization.d.ts +10 -0
- package/dist/docx/listModelNormalization.js +166 -0
- package/dist/docx/metadataPrivacy.js +3 -2
- package/dist/docx/metafileRaster.js +2 -1
- package/dist/docx/numberingParser.js +2 -2
- package/dist/docx/packageParts.js +6 -5
- package/dist/docx/paragraphParser.js +6 -4
- package/dist/docx/paragraphPropertySource.js +1 -2
- package/dist/docx/parseContext.d.ts +2 -1
- package/dist/docx/parseContext.js +1 -0
- package/dist/docx/parseWarningMessage.js +1 -0
- package/dist/docx/parser.js +10 -5
- package/dist/docx/preservedRunContent.js +11 -2
- package/dist/docx/relsParser.js +1 -1
- package/dist/docx/removeHeaderFooterParts.js +8 -7
- package/dist/docx/replyToComment.js +3 -13
- package/dist/docx/rezip.js +90 -90
- package/dist/docx/runParser.js +12 -6
- package/dist/docx/runPropertyPresence.d.ts +11 -0
- package/dist/docx/runPropertyPresence.js +14 -0
- package/dist/docx/selectedXmlIdentityScan.d.ts +10 -0
- package/dist/docx/selectedXmlIdentityScan.js +36 -0
- package/dist/docx/selectiveSave.js +19 -17
- package/dist/docx/selectiveXmlPatch.js +44 -52
- package/dist/docx/serializer/commentSerializer.d.ts +3 -1
- package/dist/docx/serializer/commentSerializer.js +11 -2
- package/dist/docx/serializer/runSerializer.js +5 -4
- package/dist/docx/serializer/tableSerializer.js +2 -1
- package/dist/docx/serializer/trackedChangeAttributes.d.ts +1 -3
- package/dist/docx/serializer/trackedChangeAttributes.js +1 -11
- package/dist/docx/server/applyDocxXmlPatchProposal.js +2 -2
- package/dist/docx/server/boundedArchive.js +2 -1
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +2 -1
- package/dist/docx/server/inspectDocxPackage.js +5 -1
- package/dist/docx/server/validateDocxConformance.js +2 -1
- package/dist/docx/tableParser.js +7 -4
- package/dist/docx/tableRowPropertyPresence.d.ts +9 -0
- package/dist/docx/tableRowPropertyPresence.js +15 -0
- package/dist/docx/unzip.js +2 -1
- package/dist/docx/xmlEncoding.js +6 -5
- package/dist/fonts/embeddedFontFingerprint.d.ts +5 -0
- package/dist/fonts/embeddedFontFingerprint.js +18 -0
- package/dist/fonts/embeddedFonts.d.ts +1 -1
- package/dist/fonts/embeddedFonts.js +1 -1
- package/dist/fonts/fontsourceFaces.js +2 -1
- package/dist/fonts/headlessMeasure.js +0 -0
- package/dist/fonts/sfnt/parse.js +6 -9
- package/dist/fonts/sfnt/subset.js +2 -1
- package/dist/fonts/sfnt/tables.js +2 -3
- package/dist/fonts/sfnt/woff.js +2 -1
- package/dist/headless-layout.js +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -2
- package/dist/internal/revisionResolutionInline.d.ts +2 -1
- package/dist/internal/revisionResolutionInline.js +22 -1
- package/dist/internal/wholeStoryRevisionResolution.js +24 -7
- package/dist/layout-bridge/convert/flowConversionShared.d.ts +4 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +5 -1
- package/dist/layout-bridge/convert/footnoteLayout.js +16 -3
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +6 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +13 -14
- package/dist/layout-bridge/convert/paragraphConversion.js +8 -1
- package/dist/layout-bridge/convert/paragraphRuns.js +2 -1
- package/dist/layout-bridge/convert/toFlowBlocks.js +25 -1
- package/dist/layout-engine/measure/cache.js +39 -7
- package/dist/layout-engine/measure/crossRunWords.js +12 -2
- package/dist/layout-engine/measure/lineTypography.js +1 -0
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +1 -0
- package/dist/layout-engine/measure/listMarkerWidth.js +1 -0
- package/dist/layout-engine/measure/measureContainer.js +3 -4
- package/dist/layout-engine/measure/measureHelpers.d.ts +6 -9
- package/dist/layout-engine/measure/measureHelpers.js +21 -18
- package/dist/layout-engine/measure/measureParagraph.js +2 -0
- package/dist/layout-engine/measure/measureTypes.d.ts +2 -0
- package/dist/layout-engine/types.d.ts +4 -1
- package/dist/layout-painter/renderPage.d.ts +1 -0
- package/dist/layout-painter/renderPage.js +11 -3
- package/dist/layout-painter/renderParagraph.js +13 -3
- package/dist/layout-painter/renderWatermark.d.ts +1 -0
- package/dist/layout-painter/renderWatermark.js +7 -3
- package/dist/managers/AutoSaveManager.js +2 -1
- package/dist/managers/EditorModeManager.d.ts +10 -1
- package/dist/managers/EditorModeManager.js +15 -1
- package/dist/managers/TableSelectionManager.d.ts +1 -1
- package/dist/managers/autoSaveCodec.js +2 -6
- package/dist/managers/editorShortcuts.d.ts +2 -1
- package/dist/managers/editorShortcuts.js +2 -1
- package/dist/managers/reviewDisplayMode.d.ts +8 -0
- package/dist/managers/reviewDisplayMode.js +15 -0
- package/dist/paged-layout/pageText.d.ts +11 -2
- package/dist/paged-layout/pageText.js +8 -3
- package/dist/pdf/fonts.js +5 -14
- package/dist/pdf/images.js +3 -2
- package/dist/pdf/objects.d.ts +0 -11
- package/dist/pdf/objects.js +7 -9
- package/dist/pdf/writePdf.js +2 -1
- package/dist/prosemirror/canonicalSelectionRange.d.ts +4 -1
- package/dist/prosemirror/canonicalSelectionRange.js +9 -3
- package/dist/prosemirror/commands/comments.js +51 -13
- package/dist/prosemirror/containerFinalParagraph.d.ts +8 -8
- package/dist/prosemirror/containerFinalParagraph.js +8 -8
- package/dist/prosemirror/conversion/fromProseDoc.js +9 -4
- package/dist/prosemirror/conversion/toProseDoc.js +4 -4
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts +8 -2
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +39 -3
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.d.ts +8 -2
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +4 -3
- package/dist/prosemirror/extensions/features/ImageDragExtension.js +4 -3
- package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.js +4 -3
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +9 -3
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +6 -5
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +8 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +11 -3
- package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +4 -3
- package/dist/prosemirror/extensions/marks/markUtils.js +1 -1
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +16 -2
- package/dist/prosemirror/findReplaceSelection.d.ts +11 -1
- package/dist/prosemirror/findReplaceSelection.js +15 -1
- package/dist/prosemirror/markupViewProjection.d.ts +10 -1
- package/dist/prosemirror/markupViewProjection.js +21 -3
- package/dist/prosemirror/pluginKeyOwner.d.ts +13 -0
- package/dist/prosemirror/pluginKeyOwner.js +35 -0
- package/dist/prosemirror/plugins/aiCitationDecorations.d.ts +11 -4
- package/dist/prosemirror/plugins/aiCitationDecorations.js +16 -3
- package/dist/prosemirror/plugins/aiSuggestionDecorations.d.ts +11 -4
- package/dist/prosemirror/plugins/aiSuggestionDecorations.js +18 -3
- package/dist/prosemirror/plugins/anonymizationDecorations.d.ts +11 -11
- package/dist/prosemirror/plugins/anonymizationDecorations.js +23 -13
- package/dist/prosemirror/plugins/autocompleteSuggestion.d.ts +8 -2
- package/dist/prosemirror/plugins/autocompleteSuggestion.js +27 -5
- package/dist/prosemirror/plugins/contentControlWidgets.d.ts +8 -2
- package/dist/prosemirror/plugins/contentControlWidgets.js +4 -3
- package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +6 -5
- package/dist/prosemirror/plugins/createDocScanPlugin.js +1 -1
- package/dist/prosemirror/plugins/documentNumbering.js +5 -3
- package/dist/prosemirror/plugins/documentStyleState.d.ts +8 -2
- package/dist/prosemirror/plugins/documentStyleState.js +2 -2
- package/dist/prosemirror/plugins/documentStyles.js +1 -1
- package/dist/prosemirror/plugins/selectionTracker.d.ts +8 -2
- package/dist/prosemirror/plugins/selectionTracker.js +4 -3
- package/dist/prosemirror/plugins/suggestionMode.d.ts +8 -2
- package/dist/prosemirror/plugins/suggestionMode.js +4 -3
- package/dist/prosemirror/plugins/templateDirectives.d.ts +8 -2
- package/dist/prosemirror/plugins/templateDirectives.js +19 -4
- package/dist/prosemirror/plugins/templatePreviewValues.d.ts +8 -2
- package/dist/prosemirror/plugins/templatePreviewValues.js +27 -5
- package/dist/prosemirror/plugins/templateSlashMenu.d.ts +8 -2
- package/dist/prosemirror/plugins/templateSlashMenu.js +23 -5
- package/dist/prosemirror/plugins/transactionInvariants.js +29 -4
- package/dist/prosemirror/replacedAnnotations.d.ts +15 -1
- package/dist/prosemirror/replacedAnnotations.js +71 -12
- package/dist/prosemirror/runFormattingReconciliation.d.ts +3 -1
- package/dist/prosemirror/runFormattingReconciliation.js +10 -2
- package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +5 -7
- package/dist/prosemirror/runIdentityAcrossRevisions.js +66 -38
- package/dist/prosemirror/runPropertyChangeResolution.js +13 -1
- package/dist/utils/base64.d.ts +1 -31
- package/dist/utils/base64.js +19 -33
- package/dist/utils/clipboard.js +5 -4
- package/dist/utils/colorResolver.js +11 -2
- package/dist/utils/findReplace.d.ts +5 -0
- package/dist/utils/fontLoader.d.ts +2 -2
- package/dist/utils/fontLoader.js +2 -7
- package/dist/utils/fontResolver.d.ts +7 -3
- package/dist/utils/fontResolver.js +6 -8
- package/dist/utils/replaceText.js +21 -4
- package/dist/utils/sliceText.d.ts +2 -0
- package/dist/utils/sliceText.js +2 -0
- package/dist/utils/textEncoding.d.ts +2 -0
- package/dist/utils/textEncoding.js +2 -0
- package/dist/utils/tiffConverter.d.ts +0 -9
- package/dist/utils/tiffConverter.js +2 -4
- package/package.json +73 -31
package/dist/docx/runParser.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseHorizontalScalePercent } from "../utils/horizontalScale.js";
|
|
2
|
+
import { sliceText } from "../utils/sliceText.js";
|
|
2
3
|
import { NO_MODELLED_ATTRIBUTES, attributeRemainder } from "./attributeRemainder.js";
|
|
3
4
|
import { CAPTURE, dispatchChildrenWithContext, keptUnless, ownedElsewhere, sequencePositions } from "./containerChildren.js";
|
|
4
5
|
import { parseDiagramPreview } from "./diagramPreview.js";
|
|
@@ -10,6 +11,7 @@ import { EmphasisMarkSchema, FontHintSchema, FontThemeSchema, HighlightColorSche
|
|
|
10
11
|
import { preserveRunChild } from "./preservedRunContent.js";
|
|
11
12
|
import { standalonePreviewLedger } from "./previewBudget.js";
|
|
12
13
|
import { parseBoundaryJoins } from "./reviewResolutionProvenance.js";
|
|
14
|
+
import { isEmptyRunPropertiesAttribute, omitsPreviousRunProperties, retainsEmptyRunProperties } from "./runPropertyPresence.js";
|
|
13
15
|
import { parseShading } from "./shadingParser.js";
|
|
14
16
|
import { captureShapeAlternateContent } from "./shapeAlternateContent.js";
|
|
15
17
|
import { parseShapeFromDrawing, shouldPreserveRawShapeDrawing } from "./shapeParser.js";
|
|
@@ -48,7 +50,11 @@ import { DRAWING_RAW_XML_MODES } from "@stll/docx-core/model";
|
|
|
48
50
|
* attacker-sized string per run.
|
|
49
51
|
*/
|
|
50
52
|
const MAX_LANGUAGE_TAG_LENGTH = 35;
|
|
51
|
-
const truncateLanguageTag = (value) => value === void 0 ? void 0 :
|
|
53
|
+
const truncateLanguageTag = (value) => value === void 0 ? void 0 : sliceText({
|
|
54
|
+
text: value,
|
|
55
|
+
units: "codeUnits",
|
|
56
|
+
end: MAX_LANGUAGE_TAG_LENGTH
|
|
57
|
+
});
|
|
52
58
|
function parseColorValue({ rgb, themeColor, themeTint, themeShade, element }) {
|
|
53
59
|
const color = {};
|
|
54
60
|
if (rgb && rgb !== "auto") color.rgb = rgb;
|
|
@@ -429,7 +435,7 @@ function parseRunProperties(rPr, theme, owner) {
|
|
|
429
435
|
});
|
|
430
436
|
if (preserved) formatting.preserved = preserved;
|
|
431
437
|
const children = getChildElements(rPr);
|
|
432
|
-
if (owner
|
|
438
|
+
if (owner !== RUN_PROPERTY_OWNERS.standalone && Object.keys(formatting).length === 0 && children.length > 0 && children.every((child) => WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(child) ?? "") && Object.hasOwn(OWNED_BY_A_SIBLING_RECORD[owner], getLocalName(child.name)))) return;
|
|
433
439
|
return formatting;
|
|
434
440
|
}
|
|
435
441
|
function parseRunPropertyChanges(rPr, theme, currentFormatting) {
|
|
@@ -443,7 +449,7 @@ function parseRunPropertyChanges(rPr, theme, currentFormatting) {
|
|
|
443
449
|
const boundaryJoins = parseBoundaryJoins(changeElement);
|
|
444
450
|
if (boundaryJoins !== void 0) change.boundaryJoins = boundaryJoins;
|
|
445
451
|
const previousFormatting = parseRunProperties(previousRPr, theme, RUN_PROPERTY_OWNERS.standalone);
|
|
446
|
-
if (previousFormatting) change.previousFormatting = previousFormatting;
|
|
452
|
+
if (previousFormatting && (!omitsPreviousRunProperties(changeElement) || Object.keys(previousFormatting).length > 0)) change.previousFormatting = previousFormatting;
|
|
447
453
|
if (currentFormatting) change.currentFormatting = currentFormatting;
|
|
448
454
|
return change;
|
|
449
455
|
});
|
|
@@ -789,7 +795,7 @@ function parseRun(node, _styles, theme, rels = null, media = null, rootXmlns = {
|
|
|
789
795
|
};
|
|
790
796
|
const rPr = findChild(node, "w", "rPr");
|
|
791
797
|
if (rPr) {
|
|
792
|
-
const formattingResult = parseRunProperties(rPr, theme, RUN_PROPERTY_OWNERS.run);
|
|
798
|
+
const formattingResult = parseRunProperties(rPr, theme, RUN_PROPERTY_OWNERS.run) ?? (retainsEmptyRunProperties(node) ? {} : void 0);
|
|
793
799
|
if (formattingResult) run.formatting = formattingResult;
|
|
794
800
|
const propertyChangesResult = parseRunPropertyChanges(rPr, theme, run.formatting);
|
|
795
801
|
if (propertyChangesResult) run.propertyChanges = propertyChangesResult;
|
|
@@ -798,8 +804,8 @@ function parseRun(node, _styles, theme, rels = null, media = null, rootXmlns = {
|
|
|
798
804
|
const remainder = attributeRemainder({
|
|
799
805
|
element: node,
|
|
800
806
|
modelled: NO_MODELLED_ATTRIBUTES
|
|
801
|
-
});
|
|
802
|
-
if (remainder) run.preservedAttributes = remainder;
|
|
807
|
+
})?.filter((attribute) => !isEmptyRunPropertiesAttribute(attribute));
|
|
808
|
+
if (remainder?.length) run.preservedAttributes = remainder;
|
|
803
809
|
return run;
|
|
804
810
|
}
|
|
805
811
|
/**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { XmlElement } from "./xmlParser.js";
|
|
3
|
+
//#region src/docx/runPropertyPresence.d.ts
|
|
4
|
+
declare const EMPTY_RUN_PROPERTIES_ATTRIBUTE = "emptyRunProperties";
|
|
5
|
+
declare const retainsEmptyRunProperties: (element: XmlElement) => boolean;
|
|
6
|
+
declare const omitsPreviousRunProperties: (element: XmlElement) => boolean;
|
|
7
|
+
declare const serializePreviousRunProperties: (properties: document_d_exports.TextFormatting | undefined) => string;
|
|
8
|
+
declare const serializeEmptyRunProperties: (run: document_d_exports.Run) => string[];
|
|
9
|
+
declare const isEmptyRunPropertiesAttribute: ({ namespace, name }: Pick<document_d_exports.PreservedAttribute, "namespace" | "name">) => boolean;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { EMPTY_RUN_PROPERTIES_ATTRIBUTE, isEmptyRunPropertiesAttribute, omitsPreviousRunProperties, retainsEmptyRunProperties, serializeEmptyRunProperties, serializePreviousRunProperties };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { OOXML_NAMESPACES } from "./serializer/partNamespaces.js";
|
|
2
|
+
import { getAttributeByNamespaceUri } from "./xmlParser.js";
|
|
3
|
+
import { EMPTY_PROPERTY_SETS } from "@stll/docx-core/ops";
|
|
4
|
+
//#region src/docx/runPropertyPresence.ts
|
|
5
|
+
const EMPTY_RUN_PROPERTIES_ATTRIBUTE = "emptyRunProperties";
|
|
6
|
+
const PREVIOUS_PROPERTIES_ATTRIBUTE = "previousProperties";
|
|
7
|
+
const HISTORY_NAMESPACES = /* @__PURE__ */ new Set([OOXML_NAMESPACES.folio.uri]);
|
|
8
|
+
const retainsEmptyRunProperties = (element) => getAttributeByNamespaceUri(element, HISTORY_NAMESPACES, EMPTY_RUN_PROPERTIES_ATTRIBUTE) === EMPTY_PROPERTY_SETS.KEEP;
|
|
9
|
+
const omitsPreviousRunProperties = (element) => getAttributeByNamespaceUri(element, HISTORY_NAMESPACES, PREVIOUS_PROPERTIES_ATTRIBUTE) === EMPTY_PROPERTY_SETS.OMIT;
|
|
10
|
+
const serializePreviousRunProperties = (properties) => properties === void 0 ? ` folio:${PREVIOUS_PROPERTIES_ATTRIBUTE}="${EMPTY_PROPERTY_SETS.OMIT}"` : "";
|
|
11
|
+
const serializeEmptyRunProperties = (run) => run.propertyChanges?.length && run.formatting !== void 0 && Object.keys(run.formatting).length === 0 ? [`folio:${EMPTY_RUN_PROPERTIES_ATTRIBUTE}="${EMPTY_PROPERTY_SETS.KEEP}"`] : [];
|
|
12
|
+
const isEmptyRunPropertiesAttribute = ({ namespace, name }) => namespace === OOXML_NAMESPACES.folio.uri && name === "emptyRunProperties";
|
|
13
|
+
//#endregion
|
|
14
|
+
export { EMPTY_RUN_PROPERTIES_ATTRIBUTE, isEmptyRunPropertiesAttribute, omitsPreviousRunProperties, retainsEmptyRunProperties, serializeEmptyRunProperties, serializePreviousRunProperties };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { scanStreamingXmlElements } from "./streamingXmlParser.js";
|
|
2
|
+
//#region src/docx/selectedXmlIdentityScan.d.ts
|
|
3
|
+
/** Identity census follows the parser's first-Choice, otherwise-Fallback policy. */
|
|
4
|
+
declare const scanSelectedStreamingXmlElements: (xml: string, visitor: Parameters<typeof scanStreamingXmlElements>[1]) => {
|
|
5
|
+
status: "unsupported";
|
|
6
|
+
} | {
|
|
7
|
+
status: "scanned";
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { scanSelectedStreamingXmlElements };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { scanStreamingXmlElements } from "./streamingXmlParser.js";
|
|
2
|
+
import { NAMESPACES, getLocalName, getNamespaceUri, selectAlternateContentBranch } from "./xmlParser.js";
|
|
3
|
+
//#region src/docx/selectedXmlIdentityScan.ts
|
|
4
|
+
const isCompatibilityElement = (element, name) => getNamespaceUri(element) === NAMESPACES.mc && getLocalName(element.name) === name;
|
|
5
|
+
/** Identity census follows the parser's first-Choice, otherwise-Fallback policy. */
|
|
6
|
+
const scanSelectedStreamingXmlElements = (xml, visitor) => {
|
|
7
|
+
if (!xml.includes("AlternateContent")) return scanStreamingXmlElements(xml, visitor);
|
|
8
|
+
const alternatives = /* @__PURE__ */ new Map();
|
|
9
|
+
const positions = /* @__PURE__ */ new WeakMap();
|
|
10
|
+
const scanned = scanStreamingXmlElements(xml, ({ element, parent, nameEnd }) => {
|
|
11
|
+
positions.set(element, nameEnd);
|
|
12
|
+
if (isCompatibilityElement(element, "AlternateContent")) {
|
|
13
|
+
element.elements = [];
|
|
14
|
+
alternatives.set(nameEnd, element);
|
|
15
|
+
}
|
|
16
|
+
if (!parent || !isCompatibilityElement(parent, "AlternateContent")) return;
|
|
17
|
+
parent.elements?.push(element);
|
|
18
|
+
});
|
|
19
|
+
if (scanned.status === "unsupported") return scanned;
|
|
20
|
+
const selectedPositions = /* @__PURE__ */ new Map();
|
|
21
|
+
for (const [position, alternative] of alternatives) {
|
|
22
|
+
const selected = selectAlternateContentBranch(alternative);
|
|
23
|
+
selectedPositions.set(position, selected === void 0 ? void 0 : positions.get(selected));
|
|
24
|
+
}
|
|
25
|
+
const active = /* @__PURE__ */ new WeakMap();
|
|
26
|
+
return scanStreamingXmlElements(xml, (entry) => {
|
|
27
|
+
const { element, parent, nameEnd } = entry;
|
|
28
|
+
positions.set(element, nameEnd);
|
|
29
|
+
let included = parent === void 0 || active.get(parent) === true;
|
|
30
|
+
if (parent && isCompatibilityElement(parent, "AlternateContent")) included &&= nameEnd === selectedPositions.get(positions.get(parent) ?? -1);
|
|
31
|
+
active.set(element, included);
|
|
32
|
+
if (included) visitor(entry);
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { scanSelectedStreamingXmlElements };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeText, encodeUtf8 } from "../utils/textEncoding.js";
|
|
1
2
|
import { parseCommentsExtended } from "./commentParser.js";
|
|
2
3
|
import { withoutOrphanCommentRanges } from "./commentRangeIntegrity.js";
|
|
3
4
|
import { hasUnsynthesizedReplyRanges } from "./commentReplyMarkers.js";
|
|
@@ -10,7 +11,7 @@ import { RELATIONSHIP_TYPES } from "./relsParser.js";
|
|
|
10
11
|
import { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_PART_LOWER, addCommentsExtendedOverride, addCommentsExtendedRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, hasUnmaterializedHyperlinkBindings, hasUnmaterializedInlineResources, planAddedStyles, publishCanonicalImageResources, updateCoreProperties, withoutAttachedTemplate } from "./rezip.js";
|
|
11
12
|
import "./selectiveSaveFlags.js";
|
|
12
13
|
import { buildPatchedDocumentXml, buildPatchedNoteXml, collectAddedNumberingDefs, collectParaIds, patchNumberingDefinitions } from "./selectiveXmlPatch.js";
|
|
13
|
-
import { hasOwnedCommentsPart, planCommentParts, serializeComments, serializeCommentsExtended } from "./serializer/commentSerializer.js";
|
|
14
|
+
import { canReplayCommentsPart, hasOwnedCommentsPart, planCommentParts, serializeComments, serializeCommentsExtended } from "./serializer/commentSerializer.js";
|
|
14
15
|
import { serializeDocument } from "./serializer/documentSerializer.js";
|
|
15
16
|
import { serializeEndnotes, serializeFootnotes } from "./serializer/noteSerializer.js";
|
|
16
17
|
import { serializeNumberingXml } from "./serializer/numberingSerializer.js";
|
|
@@ -46,7 +47,7 @@ async function patchNoteParts(zip, doc, candidateIds, updates) {
|
|
|
46
47
|
const patchPart = async (conventionalLowerPath, serialize) => {
|
|
47
48
|
const file = findPart(conventionalLowerPath);
|
|
48
49
|
if (!file) return true;
|
|
49
|
-
const originalXml = await file.async("
|
|
50
|
+
const originalXml = await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
50
51
|
const originalIds = collectParaIds(originalXml);
|
|
51
52
|
const partIds = /* @__PURE__ */ new Set();
|
|
52
53
|
for (const id of remainingIds) if (originalIds.has(id)) partIds.add(id);
|
|
@@ -93,7 +94,7 @@ async function patchCommentsExtended(zip, plan, updates) {
|
|
|
93
94
|
const desiredXml = serializeCommentsExtended(plan);
|
|
94
95
|
const existing = findZipEntryCaseInsensitive(zip, COMMENTS_EXTENDED_PART_LOWER);
|
|
95
96
|
if (!desiredXml) return existing === null;
|
|
96
|
-
const baselineInfo = existing ? parseCommentsExtended(await existing.async("
|
|
97
|
+
const baselineInfo = existing ? parseCommentsExtended(await existing.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }))) : /* @__PURE__ */ new Map();
|
|
97
98
|
if (commentsExtendedInfoEqual(parseCommentsExtended(desiredXml), baselineInfo)) return true;
|
|
98
99
|
updates.set(existing?.name ?? "word/commentsExtended.xml", desiredXml);
|
|
99
100
|
return ensureCommentsExtendedPackaging(zip, updates);
|
|
@@ -106,12 +107,12 @@ async function patchCommentsExtended(zip, plan, updates) {
|
|
|
106
107
|
*/
|
|
107
108
|
async function ensureCommentsExtendedPackaging(zip, updates) {
|
|
108
109
|
const ctPath = "[Content_Types].xml";
|
|
109
|
-
const ctXml = updates.get(ctPath) ?? await zip.file(ctPath)?.async("
|
|
110
|
+
const ctXml = updates.get(ctPath) ?? await zip.file(ctPath)?.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
110
111
|
if (ctXml === void 0) return false;
|
|
111
112
|
const nextCt = addCommentsExtendedOverride(ctXml);
|
|
112
113
|
if (nextCt !== ctXml) updates.set(ctPath, nextCt);
|
|
113
114
|
const relsPath = "word/_rels/document.xml.rels";
|
|
114
|
-
const relsXml = updates.get(relsPath) ?? await zip.file(relsPath)?.async("
|
|
115
|
+
const relsXml = updates.get(relsPath) ?? await zip.file(relsPath)?.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
115
116
|
if (relsXml === void 0) return false;
|
|
116
117
|
const nextRels = addCommentsExtendedRelationship(relsXml);
|
|
117
118
|
if (nextRels !== relsXml) updates.set(relsPath, nextRels);
|
|
@@ -146,7 +147,7 @@ async function patchNumberingPart(zip, doc, updates) {
|
|
|
146
147
|
}
|
|
147
148
|
}
|
|
148
149
|
if (!file) return false;
|
|
149
|
-
const originalXml = await file.async("
|
|
150
|
+
const originalXml = await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
150
151
|
const baselineXml = serializeNumberingXml(parseNumbering(originalXml).definitions);
|
|
151
152
|
const currentXml = serializeNumberingXml(numbering);
|
|
152
153
|
if (currentXml === baselineXml) return true;
|
|
@@ -177,7 +178,7 @@ const queueSettingsUpdates = async (zip, updates) => {
|
|
|
177
178
|
const settingsFile = zip.file("word/settings.xml");
|
|
178
179
|
if (!settingsFile) return;
|
|
179
180
|
const relsFile = zip.file("word/_rels/settings.xml.rels");
|
|
180
|
-
const filtered = withoutAttachedTemplate(await settingsFile.async("
|
|
181
|
+
const filtered = withoutAttachedTemplate(await settingsFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true })), await relsFile?.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true })));
|
|
181
182
|
if (filtered.settingsXml !== void 0) updates.set("word/settings.xml", filtered.settingsXml);
|
|
182
183
|
if (filtered.relsXml !== void 0) updates.set("word/_rels/settings.xml.rels", filtered.relsXml);
|
|
183
184
|
};
|
|
@@ -212,12 +213,12 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
|
|
|
212
213
|
for (const [path, file] of Object.entries(zip.files)) {
|
|
213
214
|
const lowerPath = path.toLowerCase();
|
|
214
215
|
if (file.dir || !lowerPath.startsWith("word/") || !lowerPath.endsWith(".xml")) continue;
|
|
215
|
-
sourceParts.set(path, await file.async("
|
|
216
|
+
sourceParts.set(path, await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true })));
|
|
216
217
|
}
|
|
217
218
|
for (const [path, xml] of normalizeImportedNumericIds(sourceParts)) if (xml !== sourceParts.get(path)) writeZipPart({
|
|
218
219
|
zip,
|
|
219
220
|
path,
|
|
220
|
-
data: xml
|
|
221
|
+
data: encodeUtf8(xml)
|
|
221
222
|
});
|
|
222
223
|
}
|
|
223
224
|
if (options.bodyAuthority === "canonical") await publishCanonicalImageResources({
|
|
@@ -229,7 +230,7 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
|
|
|
229
230
|
if (changedParaIds.size > 0 || structuralChange) {
|
|
230
231
|
const docXmlFile = zip.file("word/document.xml");
|
|
231
232
|
if (!docXmlFile) return null;
|
|
232
|
-
const originalDocXml = await docXmlFile.async("
|
|
233
|
+
const originalDocXml = await docXmlFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
233
234
|
const serializedDocXml = serializeDocument(doc, readRootNamespaceBindings(originalDocXml), {
|
|
234
235
|
xml: originalDocXml,
|
|
235
236
|
bodyAuthority: options.bodyAuthority,
|
|
@@ -239,7 +240,7 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
|
|
|
239
240
|
const originalBodyParaIds = structuralChange ? collectParaIds(originalDocXml) : void 0;
|
|
240
241
|
if (structuralChange && doc.package.numbering) {
|
|
241
242
|
const numberingFile = findZipEntryCaseInsensitive(zip, "word/numbering.xml");
|
|
242
|
-
const added = collectAddedNumberingDefs(numberingFile ? serializeNumberingXml(parseNumbering(await numberingFile.async("
|
|
243
|
+
const added = collectAddedNumberingDefs(numberingFile ? serializeNumberingXml(parseNumbering(await numberingFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }))).definitions) : "", serializeNumberingXml(doc.package.numbering));
|
|
243
244
|
if (added.abstractNums.size > 0 || added.nums.size > 0) return null;
|
|
244
245
|
}
|
|
245
246
|
const bodyChangedIds = /* @__PURE__ */ new Set();
|
|
@@ -262,28 +263,29 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
|
|
|
262
263
|
}
|
|
263
264
|
const sourceCommentsFile = findZipEntryCaseInsensitive(zip, "word/comments.xml");
|
|
264
265
|
const commentsPartPath = sourceCommentsFile?.name ?? "word/comments.xml";
|
|
265
|
-
|
|
266
|
-
|
|
266
|
+
const sourceCommentsXml = sourceCommentsFile ? await sourceCommentsFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true })) : void 0;
|
|
267
|
+
const commentsUnchanged = sourceCommentsXml !== void 0 && canReplayCommentsPart(doc, sourceCommentsXml);
|
|
268
|
+
if (!commentsUnchanged && (hasComments || ownsCommentsPart || sourceCommentsFile)) {
|
|
267
269
|
const commentsXml = serializeComments(commentPlan, sourceCommentsXml === void 0 ? void 0 : readRootNamespaceBindings(sourceCommentsXml));
|
|
268
270
|
if (commentsXml !== sourceCommentsXml) updates.set(commentsPartPath, commentsXml);
|
|
269
271
|
}
|
|
270
272
|
if (hasComments || ownsCommentsPart) {
|
|
271
273
|
const ctFile = zip.file("[Content_Types].xml");
|
|
272
274
|
if (ctFile) {
|
|
273
|
-
const ctXml = await ctFile.async("
|
|
275
|
+
const ctXml = await ctFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
274
276
|
if (!ctXml.toLowerCase().includes("/word/comments.xml")) updates.set("[Content_Types].xml", ctXml.replace("</Types>", `<Override PartName="/${commentsPartPath}" ContentType="${COMMENTS_CONTENT_TYPE}"/></Types>`));
|
|
275
277
|
}
|
|
276
278
|
const relsPath = "word/_rels/document.xml.rels";
|
|
277
279
|
const relsFile = zip.file(relsPath);
|
|
278
280
|
if (relsFile) {
|
|
279
|
-
const relsXml = await relsFile.async("
|
|
281
|
+
const relsXml = await relsFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
280
282
|
if (!relsXml.toLowerCase().includes("comments.xml")) {
|
|
281
283
|
const maxId = findMaxRId(relsXml);
|
|
282
284
|
updates.set(relsPath, relsXml.replace("</Relationships>", `<Relationship Id="rId${maxId + 1}" Type="${RELATIONSHIP_TYPES.comments}" Target="${commentsPartPath.slice(5)}"/></Relationships>`));
|
|
283
285
|
}
|
|
284
286
|
}
|
|
285
287
|
}
|
|
286
|
-
if (!await patchCommentsExtended(zip, commentPlan, updates)) return null;
|
|
288
|
+
if (!commentsUnchanged && !await patchCommentsExtended(zip, commentPlan, updates)) return null;
|
|
287
289
|
if (!await patchNumberingPart(zip, doc, updates)) return null;
|
|
288
290
|
const styles = await planAddedStyles(doc, zip);
|
|
289
291
|
switch (styles.type) {
|
|
@@ -306,7 +308,7 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
|
|
|
306
308
|
await queueSettingsUpdates(zip, updates);
|
|
307
309
|
const corePropsFile = zip.file("docProps/core.xml");
|
|
308
310
|
if (corePropsFile) {
|
|
309
|
-
const corePropsXml = await corePropsFile.async("
|
|
311
|
+
const corePropsXml = await corePropsFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
|
|
310
312
|
updates.set("docProps/core.xml", updateCoreProperties(corePropsXml, { updateModifiedDate: true }));
|
|
311
313
|
}
|
|
312
314
|
if ([...updates.keys()].some((path) => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { patchBreaksCommentRangeBalance } from "./commentRangeIntegrity.js";
|
|
2
2
|
import { resolveParagraphIdentities } from "./paraIdAttribute.js";
|
|
3
3
|
import { readRootNamespaceBindings } from "./serializer/partNamespaces.js";
|
|
4
|
+
import { scanStreamingXmlElements } from "./streamingXmlParser.js";
|
|
4
5
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
5
6
|
import { PARAGRAPH_SCAN_NAMES, hasCanonicalWordprocessingPrefixes, matchCloseTag, matchOpenTag, resolveSelectiveScanPrefixes, splicesAsCanonical } from "./wordprocessingPrefixes.js";
|
|
6
7
|
import { resolveNamespaceUri } from "./xmlNamespaceContext.js";
|
|
@@ -650,36 +651,32 @@ function extractElementByIdAttr(xml, openLiteral, closeTag, idAttr, id) {
|
|
|
650
651
|
const qualifiedName = (prefix, localName) => prefix.length === 0 ? localName : `${prefix}:${localName}`;
|
|
651
652
|
const collectNoteElementSyntax = (xml, elementName) => {
|
|
652
653
|
const byId = /* @__PURE__ */ new Map();
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
if (
|
|
654
|
+
let root;
|
|
655
|
+
scanStreamingXmlElements(xml, ({ element, parent, nameEnd }) => {
|
|
656
|
+
if (parent === void 0) {
|
|
657
|
+
root = element;
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (parent !== root || getLocalName(element.name) !== elementName || !WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(element) ?? "")) return;
|
|
656
661
|
const idAttribute = findAttributeByNamespaceUri(element, WORDPROCESSINGML_NAMESPACE_URIS, "id");
|
|
657
|
-
if (!element.name || !idAttribute)
|
|
662
|
+
if (!element.name || !idAttribute) return;
|
|
663
|
+
const spelling = idAttribute.value.trim();
|
|
664
|
+
if (!/^[+-]?\d+$/u.test(spelling) || !Number.isSafeInteger(Number(spelling))) return;
|
|
665
|
+
const id = String(Number(spelling));
|
|
666
|
+
const offsets = scanElementRange(xml, nameEnd - element.name.length - 1, `<${element.name}`, `</${element.name}>`);
|
|
667
|
+
if (!offsets) return;
|
|
658
668
|
const syntax = {
|
|
659
|
-
|
|
660
|
-
idAttributeName: idAttribute.name,
|
|
669
|
+
offsets,
|
|
661
670
|
elementPrefix: getNamespacePrefix(element.name) ?? "",
|
|
662
671
|
attributePrefix: getNamespacePrefix(idAttribute.name) ?? ""
|
|
663
672
|
};
|
|
664
|
-
const entries = byId.get(
|
|
673
|
+
const entries = byId.get(id);
|
|
665
674
|
if (entries) entries.push(syntax);
|
|
666
|
-
else byId.set(
|
|
667
|
-
}
|
|
675
|
+
else byId.set(id, [syntax]);
|
|
676
|
+
});
|
|
668
677
|
return byId;
|
|
669
678
|
};
|
|
670
|
-
const
|
|
671
|
-
openLiteral: `<${syntax.elementName}`,
|
|
672
|
-
closeTag: `</${syntax.elementName}>`,
|
|
673
|
-
idAttr: syntax.idAttributeName
|
|
674
|
-
});
|
|
675
|
-
const extractNoteElement = (xml, syntax, id) => {
|
|
676
|
-
const { openLiteral, closeTag, idAttr } = syntaxLiteral(syntax);
|
|
677
|
-
return extractElementByIdAttr(xml, openLiteral, closeTag, idAttr, id);
|
|
678
|
-
};
|
|
679
|
-
const findNoteElement = (xml, syntax, id) => {
|
|
680
|
-
const { openLiteral, closeTag, idAttr } = syntaxLiteral(syntax);
|
|
681
|
-
return findElementByIdAttr(xml, openLiteral, closeTag, idAttr, id);
|
|
682
|
-
};
|
|
679
|
+
const extractNoteElement = (xml, syntax) => xml.slice(syntax.offsets.start, syntax.offsets.end);
|
|
683
680
|
const rewriteWordprocessingPrefixes = (xml, { source, target, sourceXmlnsDeclarations }) => {
|
|
684
681
|
let rewritten = "";
|
|
685
682
|
let quote = null;
|
|
@@ -824,7 +821,7 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
|
|
|
824
821
|
const replacementSyntaxEntries = replacementElements.get(id);
|
|
825
822
|
if (originalSyntaxEntries === void 0) {
|
|
826
823
|
const replacementSyntax = currentSyntaxEntries.length === 1 && replacementSyntaxEntries?.length === 1 ? replacementSyntaxEntries[0] : void 0;
|
|
827
|
-
const replacementNote = replacementSyntax ? extractNoteElement(replacementXml, replacementSyntax
|
|
824
|
+
const replacementNote = replacementSyntax ? extractNoteElement(replacementXml, replacementSyntax) : null;
|
|
828
825
|
if (!replacementSyntax || !replacementNote) return {
|
|
829
826
|
type: "refused",
|
|
830
827
|
reason: "unroutable-paragraph"
|
|
@@ -836,24 +833,20 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
|
|
|
836
833
|
});
|
|
837
834
|
continue;
|
|
838
835
|
}
|
|
839
|
-
if (currentSyntaxEntries.length !== 1 || originalSyntaxEntries?.length
|
|
836
|
+
if (currentSyntaxEntries.length !== 1 || !originalSyntaxEntries?.length || replacementSyntaxEntries?.length !== 1) return {
|
|
840
837
|
type: "refused",
|
|
841
838
|
reason: "unroutable-paragraph"
|
|
842
839
|
};
|
|
843
840
|
const currentSyntax = currentSyntaxEntries[0];
|
|
844
|
-
const originalSyntax = originalSyntaxEntries
|
|
841
|
+
const originalSyntax = originalSyntaxEntries.at(0);
|
|
845
842
|
const replacementSyntax = replacementSyntaxEntries[0];
|
|
846
843
|
if (!currentSyntax || !originalSyntax || !replacementSyntax) return {
|
|
847
844
|
type: "refused",
|
|
848
845
|
reason: "unroutable-paragraph"
|
|
849
846
|
};
|
|
850
|
-
const currentNote = extractNoteElement(serializedXml, currentSyntax
|
|
851
|
-
const originalOffsets =
|
|
852
|
-
const replacementNote = extractNoteElement(replacementXml, replacementSyntax
|
|
853
|
-
if (!currentNote || !originalOffsets || !replacementNote) return {
|
|
854
|
-
type: "refused",
|
|
855
|
-
reason: "unroutable-paragraph"
|
|
856
|
-
};
|
|
847
|
+
const currentNote = extractNoteElement(serializedXml, currentSyntax);
|
|
848
|
+
const originalOffsets = originalSyntax.offsets;
|
|
849
|
+
const replacementNote = extractNoteElement(replacementXml, replacementSyntax);
|
|
857
850
|
const originalNote = originalXml.slice(originalOffsets.start, originalOffsets.end);
|
|
858
851
|
const currentParagraphs = paragraphRanges(currentNote, currentSyntax.elementPrefix);
|
|
859
852
|
const originalParagraphs = paragraphRanges(originalNote, originalSyntax.elementPrefix);
|
|
@@ -861,7 +854,7 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
|
|
|
861
854
|
const noteChangedParaIds = [...collectParaIds(currentNote).keys()].filter((paraId) => unroutedChangedParaIds.has(paraId));
|
|
862
855
|
const baselineSyntaxEntries = baselineElements.get(id);
|
|
863
856
|
const baselineSyntax = baselineSyntaxEntries?.length === 1 ? baselineSyntaxEntries[0] : void 0;
|
|
864
|
-
const baselineNote = baselineSyntax ? extractNoteElement(baselineXml, baselineSyntax
|
|
857
|
+
const baselineNote = baselineSyntax ? extractNoteElement(baselineXml, baselineSyntax) : null;
|
|
865
858
|
const reshaped = baselineSyntax !== void 0 && baselineNote !== null && baselineNote !== currentNote && noteShapeChanged({
|
|
866
859
|
note: baselineNote,
|
|
867
860
|
prefix: baselineSyntax.elementPrefix
|
|
@@ -910,26 +903,29 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
|
|
|
910
903
|
for (const [id, baselineSyntaxEntries] of baselineElements) {
|
|
911
904
|
if (currentElements.has(id) || baselineSyntaxEntries.length !== 1) continue;
|
|
912
905
|
const originalSyntaxEntries = originalElements.get(id);
|
|
913
|
-
|
|
914
|
-
const originalOffsets = originalSyntax && findNoteElement(originalXml, originalSyntax, id);
|
|
915
|
-
if (!originalOffsets) return {
|
|
906
|
+
if (!originalSyntaxEntries?.length) return {
|
|
916
907
|
type: "refused",
|
|
917
908
|
reason: "unroutable-paragraph"
|
|
918
909
|
};
|
|
919
|
-
const
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
910
|
+
for (const { offsets } of originalSyntaxEntries) {
|
|
911
|
+
const removal = {
|
|
912
|
+
...offsets,
|
|
913
|
+
newXml: ""
|
|
914
|
+
};
|
|
915
|
+
paragraphSplices.push(removal);
|
|
916
|
+
noteSplices.push(removal);
|
|
917
|
+
}
|
|
926
918
|
}
|
|
927
919
|
if (unroutedChangedParaIds.size > 0) return {
|
|
928
920
|
type: "refused",
|
|
929
921
|
reason: "unroutable-paragraph"
|
|
930
922
|
};
|
|
931
923
|
if (addedNotes.length > 0) {
|
|
932
|
-
const appended = appendNotesSplice(
|
|
924
|
+
const appended = appendNotesSplice({
|
|
925
|
+
originalElements,
|
|
926
|
+
addedNotes,
|
|
927
|
+
sourceXmlnsDeclarations: replacementXmlnsDeclarations
|
|
928
|
+
});
|
|
933
929
|
if (!appended) return {
|
|
934
930
|
type: "refused",
|
|
935
931
|
reason: "unroutable-paragraph"
|
|
@@ -951,15 +947,11 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
|
|
|
951
947
|
xml: wholeNotes
|
|
952
948
|
};
|
|
953
949
|
}
|
|
954
|
-
|
|
955
|
-
* Insert new notes after the part's last note, spelled with that note's
|
|
956
|
-
* prefixes. Null when the part holds no note to follow.
|
|
957
|
-
*/
|
|
958
|
-
const appendNotesSplice = (originalXml, originalElements, addedNotes, { sourceXmlnsDeclarations }) => {
|
|
950
|
+
const appendNotesSplice = ({ originalElements, addedNotes, sourceXmlnsDeclarations }) => {
|
|
959
951
|
let last = null;
|
|
960
|
-
for (const
|
|
961
|
-
const offsets =
|
|
962
|
-
if (
|
|
952
|
+
for (const entries of originalElements.values()) for (const syntax of entries) {
|
|
953
|
+
const offsets = syntax.offsets;
|
|
954
|
+
if (!last || offsets.end > last.end) last = {
|
|
963
955
|
end: offsets.end,
|
|
964
956
|
syntax
|
|
965
957
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { document_d_exports } from "../../types/document.js";
|
|
2
2
|
import { Comment } from "../../types/content.js";
|
|
3
3
|
//#region src/docx/serializer/commentSerializer.d.ts
|
|
4
|
+
/** An unrelated body edit must retain unmodelled duplicate comment definitions. */
|
|
5
|
+
declare const canReplayCommentsPart: (document: document_d_exports.Document, sourceXml: string) => boolean;
|
|
4
6
|
/** An owned empty part survives deletion of its final thread. */
|
|
5
7
|
declare const hasOwnedCommentsPart: (document: document_d_exports.Document) => boolean;
|
|
6
8
|
/**
|
|
@@ -55,4 +57,4 @@ declare function serializeComments({ order, byId }: CommentPartPlan, sourceBindi
|
|
|
55
57
|
*/
|
|
56
58
|
declare function serializeCommentsExtended(plan: CommentPartPlan): string | null;
|
|
57
59
|
//#endregion
|
|
58
|
-
export { CommentPartPlan, hasOwnedCommentsPart, planCommentParts, serializeComments, serializeCommentsExtended };
|
|
60
|
+
export { CommentPartPlan, canReplayCommentsPart, hasOwnedCommentsPart, planCommentParts, serializeComments, serializeCommentsExtended };
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { canonicalJson } from "../../utils/canonicalJson.js";
|
|
1
2
|
import { deterministicHexId } from "../../utils/hexId.js";
|
|
2
3
|
import { commentThreadParaId } from "../commentThreadKey.js";
|
|
3
4
|
import { serializeWithPreservedChildren } from "../containerChildren.js";
|
|
5
|
+
import { getDocumentSourceBaseline } from "../headerFooterVerbatim.js";
|
|
4
6
|
import { serializeParagraph } from "./paragraphSerializer.js";
|
|
5
7
|
import { serializePartElement } from "./partNamespaces.js";
|
|
6
8
|
import { serializeTextFormatting } from "./textFormattingSerializer.js";
|
|
7
|
-
import {
|
|
9
|
+
import { Result } from "better-result";
|
|
10
|
+
import { assertValidOoxmlNumericIds, escapeXmlAttribute } from "@stll/docx-core";
|
|
8
11
|
import { COMMENT_PART_RELATIONSHIPS } from "@stll/docx-core/model";
|
|
9
12
|
//#region src/docx/serializer/commentSerializer.ts
|
|
10
13
|
/**
|
|
@@ -12,6 +15,12 @@ import { COMMENT_PART_RELATIONSHIPS } from "@stll/docx-core/model";
|
|
|
12
15
|
*
|
|
13
16
|
* Serializes Comment[] to OOXML comments.xml format.
|
|
14
17
|
*/
|
|
18
|
+
/** An unrelated body edit must retain unmodelled duplicate comment definitions. */
|
|
19
|
+
const canReplayCommentsPart = (document, sourceXml) => {
|
|
20
|
+
if (Result.try(() => assertValidOoxmlNumericIds(sourceXml, "word/comments.xml")).isErr()) return false;
|
|
21
|
+
const baseline = getDocumentSourceBaseline(document);
|
|
22
|
+
return baseline.type === "captured" && canonicalJson(document.package.document.comments ?? []) === canonicalJson(baseline.body.comments ?? []);
|
|
23
|
+
};
|
|
15
24
|
/** An owned empty part survives deletion of its final thread. */
|
|
16
25
|
const hasOwnedCommentsPart = (document) => [...document.package.relationships?.values() ?? []].some(({ type }) => type === COMMENT_PART_RELATIONSHIPS.comments.type);
|
|
17
26
|
const DEFAULT_ANNOTATION_REFERENCE_PROPERTIES = "<w:rPr><w:rStyle w:val=\"CommentReference\"/></w:rPr>";
|
|
@@ -200,4 +209,4 @@ function serializeCommentsExtended(plan) {
|
|
|
200
209
|
});
|
|
201
210
|
}
|
|
202
211
|
//#endregion
|
|
203
|
-
export { hasOwnedCommentsPart, planCommentParts, serializeComments, serializeCommentsExtended };
|
|
212
|
+
export { canReplayCommentsPart, hasOwnedCommentsPart, planCommentParts, serializeComments, serializeCommentsExtended };
|
|
@@ -9,16 +9,17 @@ import { canReplayEditableImageRawXml } from "../imageRawXml.js";
|
|
|
9
9
|
import { serializeNonVisualDrawingNames } from "../nonVisualDrawingProps.js";
|
|
10
10
|
import { serializeBoundaryJoins } from "../reviewResolutionProvenance.js";
|
|
11
11
|
import { runHoldsPayload } from "../runPayload.js";
|
|
12
|
+
import { serializeEmptyRunProperties, serializePreviousRunProperties } from "../runPropertyPresence.js";
|
|
12
13
|
import { replayableShapeAlternateContent } from "../shapeAlternateContent.js";
|
|
13
14
|
import { requiredWrapPolygon, serializeWrapPolygon } from "../wrapPolygon.js";
|
|
14
15
|
import { serializeParagraph } from "./paragraphSerializer.js";
|
|
15
16
|
import { serializeTable } from "./tableSerializer.js";
|
|
16
17
|
import { serializeTextFormatting } from "./textFormattingSerializer.js";
|
|
17
|
-
import {
|
|
18
|
+
import { serializeTrackedChangeAttributes } from "./trackedChangeAttributes.js";
|
|
18
19
|
import { intAttr } from "./xmlUtils.js";
|
|
19
20
|
import { panic } from "better-result";
|
|
20
21
|
import { escapeXmlAttribute, escapeXmlText, isValidOoxmlNumericId, requiresXmlSpacePreserve } from "@stll/docx-core";
|
|
21
|
-
import { SCHEME_COLOR_VALUE_BY_THEME_COLOR, knownThemeColor, presetLineDashToken } from "@stll/docx-core/model";
|
|
22
|
+
import { SCHEME_COLOR_VALUE_BY_THEME_COLOR, getSingularRunPropertyChange, knownThemeColor, presetLineDashToken } from "@stll/docx-core/model";
|
|
22
23
|
//#region src/docx/serializer/runSerializer.ts
|
|
23
24
|
/**
|
|
24
25
|
* Run Serializer - Serialize runs to OOXML XML
|
|
@@ -52,7 +53,7 @@ function getUniqueId(id) {
|
|
|
52
53
|
}
|
|
53
54
|
function serializeRunPropertyChange(change) {
|
|
54
55
|
const previousRPrXml = serializeTextFormatting(change.previousFormatting) || "<w:rPr/>";
|
|
55
|
-
return `<w:rPrChange ${serializeTrackedChangeAttributes(change.info)}${serializeBoundaryJoins(change.boundaryJoins)}>${previousRPrXml}</w:rPrChange>`;
|
|
56
|
+
return `<w:rPrChange ${serializeTrackedChangeAttributes(change.info)}${serializeBoundaryJoins(change.boundaryJoins)}${serializePreviousRunProperties(change.previousFormatting)}>${previousRPrXml}</w:rPrChange>`;
|
|
56
57
|
}
|
|
57
58
|
function serializeRunProperties(formatting, propertyChanges) {
|
|
58
59
|
const propertyChange = getSingularRunPropertyChange(propertyChanges);
|
|
@@ -567,7 +568,7 @@ function serializeRun(run) {
|
|
|
567
568
|
const contentXml = serializeRunContent(content);
|
|
568
569
|
if (contentXml) parts.push(contentXml);
|
|
569
570
|
}
|
|
570
|
-
const attrs = serializePreservedAttributes(
|
|
571
|
+
const attrs = serializePreservedAttributes(serializeEmptyRunProperties(run), run.preservedAttributes);
|
|
571
572
|
return `<w:r${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""}>${parts.join("")}</w:r>`;
|
|
572
573
|
}
|
|
573
574
|
/**
|
|
@@ -5,6 +5,7 @@ import { withPreservedChildren } from "../containerChildren.js";
|
|
|
5
5
|
import { TRANSITIONAL_NAME_BY_STRICT_NAME } from "../strictNames.gen.js";
|
|
6
6
|
import { TABLE_LOOK_FLAGS } from "../tableLook.js";
|
|
7
7
|
import { parseTableCellProperties, parseTableGrid, parseTableProperties, parseTablePropertyExceptions, parseTableRowProperties } from "../tableParser.js";
|
|
8
|
+
import { serializeEmptyRowProperties } from "../tableRowPropertyPresence.js";
|
|
8
9
|
import { OOXML_NAMESPACE_SCOPE, parseXml } from "../xmlParser.js";
|
|
9
10
|
import { serializeBlockCustomXml } from "./blockCustomXmlSerializer.js";
|
|
10
11
|
import { serializeBlockSdt } from "./blockSdtSerializer.js";
|
|
@@ -555,7 +556,7 @@ function serializeTableRow(row, serializeParagraph) {
|
|
|
555
556
|
carriers: carriersFor(cell),
|
|
556
557
|
xml: serializeTableCell(cell, serializeParagraph)
|
|
557
558
|
})), positionedChildren(row.preserved, row.bookmarks), preservedChild), 0));
|
|
558
|
-
const attrs = serializePreservedAttributes(
|
|
559
|
+
const attrs = serializePreservedAttributes(serializeEmptyRowProperties(row), row.preservedAttributes);
|
|
559
560
|
return `<w:tr${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""}>${parts.join("")}</w:tr>`;
|
|
560
561
|
}
|
|
561
562
|
/**
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { document_d_exports } from "../../types/document.js";
|
|
2
2
|
//#region src/docx/serializer/trackedChangeAttributes.d.ts
|
|
3
3
|
type SerializableTrackedChangeInfo = document_d_exports.TrackedChangeInfo | document_d_exports.PropertyChangeInfo;
|
|
4
|
-
/** Enforces the singular `w:rPrChange` child in a run-property container. */
|
|
5
|
-
declare const getSingularRunPropertyChange: (propertyChanges: readonly document_d_exports.RunPropertyChange[] | undefined) => document_d_exports.RunPropertyChange | undefined;
|
|
6
4
|
/** Normalized, unescaped attributes in schema order for an XML element or string serializer. */
|
|
7
5
|
declare const trackedChangeAttributeEntries: (info: SerializableTrackedChangeInfo) => readonly (readonly [string, string])[];
|
|
8
6
|
declare const trackedChangeAttributeRecord: (info: SerializableTrackedChangeInfo) => Record<string, string>;
|
|
9
7
|
declare const serializeTrackedChangeAttributes: (info: SerializableTrackedChangeInfo) => string;
|
|
10
8
|
//#endregion
|
|
11
|
-
export {
|
|
9
|
+
export { serializeTrackedChangeAttributes, trackedChangeAttributeEntries, trackedChangeAttributeRecord };
|
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
import { DATE_UTC_ATTRIBUTE } from "../trackedChangeInfo.js";
|
|
2
|
-
import { panic } from "better-result";
|
|
3
2
|
import { assertValidOoxmlNumericId, escapeXmlAttribute } from "@stll/docx-core";
|
|
4
3
|
import { normalizeRevisionId } from "@stll/docx-core/model";
|
|
5
4
|
//#region src/docx/serializer/trackedChangeAttributes.ts
|
|
6
|
-
/** Enforces the singular `w:rPrChange` child in a run-property container. */
|
|
7
|
-
const getSingularRunPropertyChange = (propertyChanges) => {
|
|
8
|
-
const propertyChangeCount = propertyChanges?.length ?? 0;
|
|
9
|
-
if (propertyChangeCount > 1) panic("A run-property container cannot serialize more than one w:rPrChange", {
|
|
10
|
-
elementName: "w:rPrChange",
|
|
11
|
-
propertyChangeCount
|
|
12
|
-
});
|
|
13
|
-
return propertyChanges?.at(0);
|
|
14
|
-
};
|
|
15
5
|
/** Normalized, unescaped attributes in schema order for an XML element or string serializer. */
|
|
16
6
|
const trackedChangeAttributeEntries = (info) => {
|
|
17
7
|
assertValidOoxmlNumericId({
|
|
@@ -34,4 +24,4 @@ const trackedChangeAttributeEntries = (info) => {
|
|
|
34
24
|
const trackedChangeAttributeRecord = (info) => Object.fromEntries(trackedChangeAttributeEntries(info));
|
|
35
25
|
const serializeTrackedChangeAttributes = (info) => trackedChangeAttributeEntries(info).map(([name, value]) => `${name}="${escapeXmlAttribute(value)}"`).join(" ");
|
|
36
26
|
//#endregion
|
|
37
|
-
export {
|
|
27
|
+
export { serializeTrackedChangeAttributes, trackedChangeAttributeEntries, trackedChangeAttributeRecord };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { encodeUtf8 } from "../../utils/textEncoding.js";
|
|
1
2
|
import { loadDocxArchive } from "./boundedArchive.js";
|
|
2
3
|
import { FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE, InvalidFolioDocxXmlPatchProposalError, evaluateDocxXmlPatchProposal, parseFolioDocxXmlPatchProposal } from "./evaluateDocxXmlPatchProposal.js";
|
|
3
4
|
import { FOLIO_DOCX_CONFORMANCE_PROFILE, validateDocxConformance } from "./validateDocxConformance.js";
|
|
@@ -96,9 +97,8 @@ const applyReplacements = async ({ bytes, proposal }) => {
|
|
|
96
97
|
writeZipPart({
|
|
97
98
|
zip,
|
|
98
99
|
path: replacement.path,
|
|
99
|
-
data:
|
|
100
|
+
data: encodeUtf8(replacement.replacementXml),
|
|
100
101
|
options: {
|
|
101
|
-
binary: true,
|
|
102
102
|
date: current.date,
|
|
103
103
|
comment: current.comment,
|
|
104
104
|
unixPermissions: current.unixPermissions,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeText } from "../../utils/textEncoding.js";
|
|
1
2
|
import { compressionRatioLimitFor, countCentralDirectoryRecords, createInflationBudget, exceedsCompressionRatio, getZipEntrySizes, inflateEntryWithinLimits } from "../archiveInflation.js";
|
|
2
3
|
import { FOLIO_XML_RESOURCE_LIMITS, assertXmlResourceLimits, createXmlPackageBudget } from "../xmlResourceLimits.js";
|
|
3
4
|
import { TaggedError } from "better-result";
|
|
@@ -170,7 +171,7 @@ const loadDocxArchive = async (bytes, options = {}) => {
|
|
|
170
171
|
async readEntryString(path) {
|
|
171
172
|
const content = await readEntry(path);
|
|
172
173
|
if (content === null) return null;
|
|
173
|
-
const xml =
|
|
174
|
+
const xml = decodeText(content, { ignoreBOM: true });
|
|
174
175
|
const lower = path.toLowerCase();
|
|
175
176
|
if (!lower.endsWith(".xml") && !lower.endsWith(".rels")) return xml;
|
|
176
177
|
const charged = countedParts.has(path);
|