@stll/folio-core 0.35.1 → 0.37.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 +59 -1
- package/dist/ai-edits/apply.js +878 -377
- package/dist/ai-edits/block-identity.d.ts +4 -1
- package/dist/ai-edits/block-identity.js +4 -1
- package/dist/ai-edits/blockRange.js +14 -14
- package/dist/ai-edits/clean-text.d.ts +40 -1
- package/dist/ai-edits/clean-text.js +52 -2
- package/dist/ai-edits/headless.d.ts +3 -1
- package/dist/ai-edits/headless.js +141 -73
- package/dist/ai-edits/index.d.ts +3 -3
- package/dist/ai-edits/index.js +2 -2
- package/dist/ai-edits/read.js +38 -20
- package/dist/ai-edits/snapshot.d.ts +14 -6
- package/dist/ai-edits/snapshot.js +105 -20
- package/dist/ai-edits/table-cell-mutations.js +1 -1
- package/dist/ai-edits/table-row-column-mutations.js +7 -0
- package/dist/ai-edits/table-template.d.ts +11 -1
- package/dist/ai-edits/table-template.js +49 -17
- package/dist/ai-edits/types.d.ts +62 -61
- package/dist/ai-edits/word-diff.d.ts +18 -3
- package/dist/ai-edits/word-diff.js +556 -67
- package/dist/ai-suggestions/conflict.js +1 -4
- package/dist/ai-suggestions/text-positions.d.ts +8 -0
- package/dist/ai-suggestions/text-positions.js +27 -5
- package/dist/compare/__fixtures__/body-sequence.js +3 -4
- package/dist/compare/column-alignment.d.ts +13 -13
- package/dist/compare/column-alignment.js +78 -39
- package/dist/compare/compare.d.ts +4 -3
- package/dist/compare/compare.js +48 -21
- package/dist/compare/content-alignment.d.ts +95 -0
- package/dist/compare/content-alignment.js +1450 -0
- package/dist/compare/content-types.d.ts +100 -0
- package/dist/compare/content-types.js +0 -0
- package/dist/compare/content.d.ts +209 -0
- package/dist/compare/content.js +885 -0
- package/dist/compare/formatting.d.ts +5 -5
- package/dist/compare/formatting.js +6 -4
- package/dist/compare/plan.d.ts +6 -1
- package/dist/compare/plan.js +171 -566
- package/dist/compare/scenario.d.ts +2 -2
- package/dist/compare/types.d.ts +3 -3
- package/dist/compare/verification.d.ts +8 -9
- package/dist/compare/verification.js +72 -50
- package/dist/compat/eigenpal.d.ts +5 -3
- package/dist/compat/eigenpal.js +3 -2
- package/dist/controller/headerFooterEditorManager.js +18 -10
- package/dist/controller/hiddenEditorApi.js +1 -1
- package/dist/controller/hiddenEditorManager.d.ts +7 -2
- package/dist/controller/hiddenEditorManager.js +15 -6
- package/dist/controller/layoutPipeline.js +5 -3
- package/dist/controller/noteEditorManager.js +12 -7
- package/dist/display-list/build/headerFooterPrimitives.js +1 -0
- package/dist/display-list/build/storyPrimitives.d.ts +5 -2
- package/dist/display-list/build/storyPrimitives.js +60 -3
- package/dist/display-list/build/tablePrimitives.js +30 -0
- package/dist/display-list/primitives.d.ts +1 -1
- package/dist/document-operations.d.ts +8 -4
- package/dist/document-operations.js +104 -12
- package/dist/docx/blockContentParser.js +6 -27
- package/dist/docx/commentParser.js +2 -4
- package/dist/docx/commentRangeIntegrity.js +2 -4
- package/dist/docx/documentClone.d.ts +2 -0
- package/dist/docx/documentClone.js +2 -0
- package/dist/docx/ensureParaIds.js +9 -4
- package/dist/docx/headerFooterParser.js +2 -4
- package/dist/docx/normalizeBaseDirection.js +5 -7
- package/dist/docx/numberingParser.d.ts +3 -1
- package/dist/docx/numberingParser.js +4 -1
- package/dist/docx/paragraphParser.js +34 -35
- package/dist/docx/paragraphPropertySource.d.ts +49 -0
- package/dist/docx/paragraphPropertySource.js +113 -0
- package/dist/docx/paragraphTraversal.js +30 -34
- package/dist/docx/rezip.d.ts +0 -8
- package/dist/docx/rezip.js +41 -16
- package/dist/docx/runConsolidator.js +2 -4
- package/dist/docx/runParser.js +6 -17
- package/dist/docx/sectionParser.js +1 -15
- package/dist/docx/serializer/paragraphSerializer.js +289 -30
- package/dist/docx/serializer/runSerializer.js +6 -12
- package/dist/docx/serializer/sectionPropertiesSerializer.js +2 -10
- package/dist/docx/serializer/tableSerializer.js +4 -23
- package/dist/docx/serializer/trackedChangeAttributes.d.ts +11 -0
- package/dist/docx/serializer/trackedChangeAttributes.js +31 -0
- package/dist/docx/server/build.d.ts +1 -1
- package/dist/docx/server/createBilingualDocument.js +8 -10
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +22 -13
- package/dist/docx/server/validateDocxConformance.js +17 -13
- package/dist/docx/tableParser.js +1 -37
- package/dist/docx/trackedChangeInfo.d.ts +11 -0
- package/dist/docx/trackedChangeInfo.js +38 -0
- package/dist/docx/verbatimCapture.d.ts +20 -9
- package/dist/docx/verbatimCapture.js +208 -8
- package/dist/docx/xmlParser.d.ts +3 -1
- package/dist/docx/xmlParser.js +6 -1
- package/dist/docx/xmlSafety.d.ts +1 -1
- package/dist/docx/xmlSafety.js +157 -0
- package/dist/headless-layout.js +5 -4
- package/dist/index.d.ts +5 -3
- package/dist/index.js +3 -2
- package/dist/internal/headlessRevisionResolution.d.ts +23 -0
- package/dist/internal/headlessRevisionResolution.js +155 -0
- package/dist/internal/headlessRevisionResolutionGuard.d.ts +12 -0
- package/dist/internal/headlessRevisionResolutionGuard.js +28 -0
- package/dist/internal/pageBreakRunDescendantIndex.d.ts +9 -0
- package/dist/internal/pageBreakRunDescendantIndex.js +27 -0
- package/dist/internal/pageBreakRunPartition.d.ts +26 -0
- package/dist/internal/pageBreakRunPartition.js +35 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.d.ts +15 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +126 -0
- package/dist/internal/sectionEndpointResolution.d.ts +32 -0
- package/dist/internal/sectionEndpointResolution.js +38 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +16 -4
- package/dist/layout-bridge/convert/headerFooterLayout.js +52 -9
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -0
- package/dist/layout-bridge/convert/toFlowBlocks.js +391 -82
- package/dist/layout-bridge/dom/clickToPositionDom.d.ts +8 -1
- package/dist/layout-bridge/dom/clickToPositionDom.js +114 -40
- package/dist/layout-bridge/dom/textStreamDom.d.ts +24 -0
- package/dist/layout-bridge/dom/textStreamDom.js +75 -0
- package/dist/layout-bridge/headerFooterLayout.js +16 -30
- package/dist/layout-engine/index.js +12 -3
- package/dist/layout-engine/measure/tableFragmentBorderGeometry.d.ts +17 -0
- package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +24 -0
- package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
- package/dist/layout-engine/types.d.ts +7 -0
- package/dist/layout-painter/imageLayout.d.ts +1 -1
- package/dist/layout-painter/renderTable.js +20 -0
- package/dist/markdown/renderBlock.js +7 -10
- package/dist/model.d.ts +1 -1
- package/dist/paged-layout/rangeProjection.js +6 -9
- package/dist/paged-layout/transactionDirtyRange.d.ts +3 -1
- package/dist/paged-layout/transactionDirtyRange.js +23 -1
- package/dist/prosemirror/attrs/index.d.ts +7 -3
- package/dist/prosemirror/attrs/index.js +128 -7
- package/dist/prosemirror/bookmarkBoundaryIntegrity.d.ts +9 -0
- package/dist/prosemirror/bookmarkBoundaryIntegrity.js +22 -0
- package/dist/prosemirror/commands/comments.d.ts +10 -1
- package/dist/prosemirror/commands/comments.js +551 -87
- package/dist/prosemirror/commands/formatPainter.d.ts +45 -10
- package/dist/prosemirror/commands/formatPainter.js +319 -63
- package/dist/prosemirror/commands/index.d.ts +2 -2
- package/dist/prosemirror/commands/pageBreak.d.ts +5 -2
- package/dist/prosemirror/commands/pageBreak.js +18 -36
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +27 -3
- package/dist/prosemirror/commands/propertyChangeScope.js +85 -14
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +8 -5
- package/dist/prosemirror/conversion/fromProseDoc.js +935 -223
- package/dist/prosemirror/conversion/runShadingMark.js +2 -2
- package/dist/prosemirror/conversion/toProseDoc.d.ts +26 -1
- package/dist/prosemirror/conversion/toProseDoc.js +439 -291
- package/dist/prosemirror/extensions/StarterKit.js +4 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +54 -6
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +7 -1
- package/dist/prosemirror/extensions/features/ListExtension.js +95 -123
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +19 -6
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +27 -1
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +134 -30
- package/dist/prosemirror/extensions/features/pasteCleanup.js +9 -15
- package/dist/prosemirror/extensions/marks/BoldExtension.js +4 -3
- package/dist/prosemirror/extensions/marks/CharacterStyleExtension.d.ts +4 -7
- package/dist/prosemirror/extensions/marks/CharacterStyleExtension.js +5 -11
- package/dist/prosemirror/extensions/marks/FontSizeExtension.js +3 -3
- package/dist/prosemirror/extensions/marks/ItalicExtension.js +4 -3
- package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.d.ts +13 -0
- package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.js +39 -0
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts +7 -1
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +67 -21
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +8 -1
- package/dist/prosemirror/extensions/marks/markUtils.js +88 -2
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +44 -3
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
- package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +15 -4
- package/dist/prosemirror/extensions/nodes/PageBreakExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/nodes/PageBreakExtension.js +6 -0
- package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.js +36 -0
- package/dist/prosemirror/findReplaceSelection.js +8 -3
- package/dist/prosemirror/index.d.ts +3 -3
- package/dist/prosemirror/listMarker.d.ts +26 -1
- package/dist/prosemirror/listMarker.js +108 -14
- package/dist/prosemirror/pageBreakRunProjection.d.ts +28 -0
- package/dist/prosemirror/pageBreakRunProjection.js +47 -0
- package/dist/prosemirror/paraText.js +6 -0
- package/dist/prosemirror/paragraphAlignment.d.ts +12 -0
- package/dist/prosemirror/paragraphAlignment.js +13 -0
- package/dist/prosemirror/paragraphSpacing.d.ts +29 -0
- package/dist/prosemirror/paragraphSpacing.js +125 -0
- package/dist/prosemirror/plugins/documentNumbering.d.ts +8 -0
- package/dist/prosemirror/plugins/documentNumbering.js +18 -0
- package/dist/prosemirror/plugins/pmTextScan.d.ts +3 -2
- package/dist/prosemirror/plugins/pmTextScan.js +14 -4
- package/dist/prosemirror/plugins/suggestionMode.js +9 -11
- package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +16 -0
- package/dist/prosemirror/rebaseParagraphRunFormatting.js +61 -0
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +64 -0
- package/dist/prosemirror/runFormattingInlineCarriers.js +121 -0
- package/dist/prosemirror/runFormattingProvenance.d.ts +12 -0
- package/dist/prosemirror/runFormattingProvenance.js +48 -0
- package/dist/prosemirror/runFormattingReconciliation.d.ts +25 -0
- package/dist/prosemirror/runFormattingReconciliation.js +54 -0
- package/dist/prosemirror/runPropertyChangeResolution.d.ts +18 -0
- package/dist/prosemirror/runPropertyChangeResolution.js +18 -0
- package/dist/prosemirror/runStyleFormatting.d.ts +50 -0
- package/dist/prosemirror/runStyleFormatting.js +105 -0
- package/dist/prosemirror/schema/index.d.ts +3 -3
- package/dist/prosemirror/schema/marks.d.ts +84 -9
- package/dist/prosemirror/schema/marks.js +73 -1
- package/dist/prosemirror/schema/nodes.d.ts +30 -6
- package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +14 -2
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +45 -23
- package/dist/prosemirror/styles/styleResolver.d.ts +10 -13
- package/dist/prosemirror/styles/styleResolver.js +3 -3
- package/dist/prosemirror/styles/styleToggleCascade.d.ts +8 -1
- package/dist/prosemirror/styles/styleToggleCascade.js +24 -1
- package/dist/prosemirror/tableGridMutation.d.ts +20 -0
- package/dist/prosemirror/tableGridMutation.js +30 -0
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +31 -0
- package/dist/prosemirror/trackedRunInlineAtoms.js +39 -0
- package/dist/prosemirror/utils/visualLineNavigation.js +27 -31
- package/dist/prosemirror/validation.js +23 -4
- package/dist/redline.js +4 -1
- package/dist/render-dom/HeaderFooterSelectionOverlay.js +20 -20
- package/dist/render-dom/RenderedDomContext.js +17 -34
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/style-engine/styleEngine.d.ts +1 -1
- package/dist/utils/clipboard.js +1 -1
- package/dist/utils/findReplace.js +38 -21
- package/dist/utils/mergeDocumentContent.js +3 -3
- package/dist/utils/replaceText.js +2 -1
- package/dist/version-comparison.d.ts +38 -44
- package/dist/version-comparison.js +270 -399
- package/dist/watermark/index.js +2 -4
- package/package.json +8 -2
|
@@ -392,10 +392,15 @@ const ensureParaIdsInternal = async (docx, options) => {
|
|
|
392
392
|
};
|
|
393
393
|
const zip = await JSZip.loadAsync(docx);
|
|
394
394
|
if (hasDigitalSignatureParts(zip) && options.allowSignedPackageMutation !== true) throw createEnsureParaIdsError("Refusing to normalize a digitally signed package because rewriting OOXML invalidates its signatures. Warn the user and pass allowSignedPackageMutation only if invalidation is acceptable.");
|
|
395
|
-
for (const [partPath, content] of updates)
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
395
|
+
for (const [partPath, content] of updates) {
|
|
396
|
+
const sourceEntry = zip.file(partPath);
|
|
397
|
+
if (sourceEntry === null) throw createEnsureParaIdsError(`Package part disappeared during normalization: ${partPath}`);
|
|
398
|
+
zip.file(partPath, content, {
|
|
399
|
+
compression: "DEFLATE",
|
|
400
|
+
compressionOptions: { level: 6 },
|
|
401
|
+
date: sourceEntry.date
|
|
402
|
+
});
|
|
403
|
+
}
|
|
399
404
|
return {
|
|
400
405
|
docx: await zip.generateAsync({
|
|
401
406
|
type: "uint8array",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parseBlockContent } from "./blockContentParser.js";
|
|
2
2
|
import { parseFooterReference, parseFooterReferences, parseHeaderReference, parseHeaderReferences } from "./headerFooterRefParser.js";
|
|
3
3
|
import { assignHeaderFooterVerbatimXml } from "./headerFooterVerbatim.js";
|
|
4
|
+
import { cloneParagraphWithPropertySource } from "./paragraphPropertySource.js";
|
|
4
5
|
import { parseWatermark } from "./watermarkParser.js";
|
|
5
6
|
import { collectXmlnsDeclarations, parseXml } from "./xmlParser.js";
|
|
6
7
|
//#region src/docx/headerFooterParser.ts
|
|
@@ -37,10 +38,7 @@ function parseHeader(headerXml, hdrFtrType = "default", styles = null, theme = n
|
|
|
37
38
|
});
|
|
38
39
|
if (watermarkResult) {
|
|
39
40
|
const host = result.content.at(watermarkResult.blockIndex);
|
|
40
|
-
if (host?.type === "paragraph") result.content[watermarkResult.blockIndex] = {
|
|
41
|
-
...host,
|
|
42
|
-
content: []
|
|
43
|
-
};
|
|
41
|
+
if (host?.type === "paragraph") result.content[watermarkResult.blockIndex] = cloneParagraphWithPropertySource(host, { content: [] });
|
|
44
42
|
}
|
|
45
43
|
assignHeaderFooterVerbatimXml(result, headerXml);
|
|
46
44
|
return result;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createStyleResolver } from "../prosemirror/styles/styleResolver.js";
|
|
2
2
|
import { detectBaseDirection } from "../utils/baseDirection.js";
|
|
3
|
+
import { cloneParagraphWithoutPropertySource } from "./paragraphPropertySource.js";
|
|
3
4
|
//#region src/docx/normalizeBaseDirection.ts
|
|
4
5
|
const runText = (run) => {
|
|
5
6
|
let text = "";
|
|
@@ -32,13 +33,10 @@ const paragraphText = (paragraph) => paragraphContentText(paragraph.content);
|
|
|
32
33
|
const normalizeParagraph = (paragraph, styles) => {
|
|
33
34
|
if ((paragraph.formatting?.bidi ?? styles.resolveParagraphStyle(paragraph.formatting?.styleId).paragraphFormatting?.bidi) != null) return paragraph;
|
|
34
35
|
if (detectBaseDirection(paragraphText(paragraph)) !== "rtl") return paragraph;
|
|
35
|
-
return {
|
|
36
|
-
...paragraph,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
bidi: true
|
|
40
|
-
}
|
|
41
|
-
};
|
|
36
|
+
return cloneParagraphWithoutPropertySource(paragraph, { formatting: {
|
|
37
|
+
...paragraph.formatting,
|
|
38
|
+
bidi: true
|
|
39
|
+
} });
|
|
42
40
|
};
|
|
43
41
|
const normalizeTable = (table, styles) => ({
|
|
44
42
|
...table,
|
|
@@ -80,5 +80,7 @@ declare function getBulletCharacter(level: document_d_exports.ListLevel): string
|
|
|
80
80
|
* Check if a list level is a bullet (not numbered)
|
|
81
81
|
*/
|
|
82
82
|
declare function isBulletLevel(level: document_d_exports.ListLevel): boolean;
|
|
83
|
+
/** Whether a numbering level reserves horizontal space for a visible marker. */
|
|
84
|
+
declare const numberingLevelHasMarkerSlot: (level: Pick<document_d_exports.ListLevel, "numFmt">) => boolean;
|
|
83
85
|
//#endregion
|
|
84
|
-
export { NumberingMap, computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerFormattingFromLevel, numPrEqual, padDecimal, parseNumbering, renderListMarker };
|
|
86
|
+
export { NumberingMap, computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerFormattingFromLevel, numPrEqual, numberingLevelHasMarkerSlot, padDecimal, parseNumbering, renderListMarker };
|
|
@@ -542,6 +542,7 @@ function computeListRendering(numPr, numbering) {
|
|
|
542
542
|
level: ilvl,
|
|
543
543
|
numId,
|
|
544
544
|
marker: level.lvlText,
|
|
545
|
+
markerTemplate: level.lvlText,
|
|
545
546
|
isBullet: level.numFmt === "bullet",
|
|
546
547
|
numFmt: level.isLgl ? "decimal" : level.numFmt,
|
|
547
548
|
levelNumFmts,
|
|
@@ -609,5 +610,7 @@ function getBulletCharacter(level) {
|
|
|
609
610
|
function isBulletLevel(level) {
|
|
610
611
|
return level.numFmt === "bullet" || level.numFmt === "none";
|
|
611
612
|
}
|
|
613
|
+
/** Whether a numbering level reserves horizontal space for a visible marker. */
|
|
614
|
+
const numberingLevelHasMarkerSlot = (level) => level.numFmt !== "none";
|
|
612
615
|
//#endregion
|
|
613
|
-
export { computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerFormattingFromLevel, numPrEqual, padDecimal, parseNumbering, renderListMarker };
|
|
616
|
+
export { computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerFormattingFromLevel, numPrEqual, numberingLevelHasMarkerSlot, padDecimal, parseNumbering, renderListMarker };
|
|
@@ -1,19 +1,22 @@
|
|
|
1
|
+
import { canonicalJson } from "../utils/canonicalJson.js";
|
|
1
2
|
import { isValidHexColor } from "../utils/colorResolver.js";
|
|
2
3
|
import { isValidHexId } from "../utils/hexId.js";
|
|
3
4
|
import { parseBookmarkEnd as parseBookmarkEnd$1, parseBookmarkStart as parseBookmarkStart$1 } from "./bookmarkParser.js";
|
|
4
5
|
import { parseFieldType } from "./fieldParser.js";
|
|
5
6
|
import { parseHyperlink as parseHyperlink$1, parseHyperlinkChild } from "./hyperlinkParser.js";
|
|
6
|
-
import { markerFormattingFromLevel } from "./numberingParser.js";
|
|
7
|
+
import { markerFormattingFromLevel, numberingLevelHasMarkerSlot } from "./numberingParser.js";
|
|
7
8
|
import { paraIdInRange } from "./paraIdRangeNormalization.js";
|
|
9
|
+
import { assignParagraphPropertySource } from "./paragraphPropertySource.js";
|
|
8
10
|
import { BorderStyleSchema, FrameWrapSchema, FrameXAlignSchema, FrameYAlignSchema, LineSpacingRuleSchema, ParagraphAlignmentSchema, ShadingPatternSchema, TabLeaderSchema, TabStopAlignmentSchema, ThemeColorSlotSchema, narrowEnum } from "./parserEnums.js";
|
|
9
11
|
import { consolidateParagraphContent } from "./runConsolidator.js";
|
|
10
12
|
import { parseRun, parseRunProperties } from "./runParser.js";
|
|
11
13
|
import { parseSdtProperties } from "./sdtProperties.js";
|
|
12
14
|
import { parseSectionProperties } from "./sectionParser.js";
|
|
15
|
+
import { parsePropertyChangeInfo, parseTrackedChangeInfo } from "./trackedChangeInfo.js";
|
|
13
16
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
14
|
-
import { WORDPROCESSINGML_NAMESPACE_URIS, findChild, findChildByNamespaceUri, findChildren, getAttribute, getChildElements, getLocalName, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseNumberingLevelAttribute, parseNumericAttribute } from "./xmlParser.js";
|
|
17
|
+
import { WORDPROCESSINGML_NAMESPACE_URIS, cloneElement, findChild, findChildByNamespaceUri, findChildren, findChildrenByNamespaceUri, getAttribute, getChildElements, getLocalName, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseNumberingLevelAttribute, parseNumericAttribute } from "./xmlParser.js";
|
|
15
18
|
import { panic } from "better-result";
|
|
16
|
-
import { PARAGRAPH_MARK_CHANGE_KINDS
|
|
19
|
+
import { PARAGRAPH_MARK_CHANGE_KINDS } from "@stll/docx-core/model";
|
|
17
20
|
//#region src/docx/paragraphParser.ts
|
|
18
21
|
/**
|
|
19
22
|
* Extract plain text from a math element (recursive text content extraction)
|
|
@@ -373,6 +376,21 @@ function parseParagraphProperties(pPr, theme, styles) {
|
|
|
373
376
|
}
|
|
374
377
|
return Object.keys(formatting).length > 0 ? formatting : void 0;
|
|
375
378
|
}
|
|
379
|
+
/**
|
|
380
|
+
* Capture the authored paragraph properties separately from structural and
|
|
381
|
+
* revision children, which have their own model fields and lifecycles.
|
|
382
|
+
*/
|
|
383
|
+
const captureParagraphPropertySource = (pPr) => captureVerbatimXml(cloneElement(pPr, { elements: (pPr.elements ?? []).flatMap((child) => {
|
|
384
|
+
if (child.type !== "element") return [child];
|
|
385
|
+
const localName = getLocalName(child.name);
|
|
386
|
+
if (WORDPROCESSINGML_NAMESPACE_URIS.has(child.namespaceUri ?? "") && (localName === "sectPr" || localName === "pPrChange")) return [];
|
|
387
|
+
if (localName !== "rPr" || !WORDPROCESSINGML_NAMESPACE_URIS.has(child.namespaceUri ?? "")) return [child];
|
|
388
|
+
return [cloneElement(child, { elements: (child.elements ?? []).filter((runProperty) => {
|
|
389
|
+
if (runProperty.type !== "element") return true;
|
|
390
|
+
const runPropertyName = getLocalName(runProperty.name);
|
|
391
|
+
return !WORDPROCESSINGML_NAMESPACE_URIS.has(runProperty.namespaceUri ?? "") || !PARAGRAPH_MARK_CHANGE_KINDS.some((kind) => kind === runPropertyName);
|
|
392
|
+
}) })];
|
|
393
|
+
}) }));
|
|
376
394
|
const RENDERED_BREAK_INLINE_WRAPPERS = /* @__PURE__ */ new Set([
|
|
377
395
|
"hyperlink",
|
|
378
396
|
"smartTag",
|
|
@@ -477,39 +495,15 @@ function normalizeDeletionContentElement(node) {
|
|
|
477
495
|
let mappedName = node.name;
|
|
478
496
|
if (localName === "delText") mappedName = replaceLocalName(node.name, "t");
|
|
479
497
|
else if (localName === "delInstrText") mappedName = replaceLocalName(node.name, "instrText");
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
}
|
|
485
|
-
function parseTrackedChangeInfo(node) {
|
|
486
|
-
const rawId = getAttribute(node, "w", "id");
|
|
487
|
-
const parsedId = rawId ? Number.parseInt(rawId, 10) : 0;
|
|
488
|
-
const rawAuthor = getAttribute(node, "w", "author");
|
|
489
|
-
const rawDate = getAttribute(node, "w", "date");
|
|
490
|
-
const author = rawAuthor?.trim() ?? "";
|
|
491
|
-
const date = rawDate?.trim() ?? "";
|
|
492
|
-
const initials = (getAttribute(node, "w", "initials") ?? "").trim();
|
|
493
|
-
const info = {
|
|
494
|
-
id: normalizeRevisionId(parsedId),
|
|
495
|
-
author: author.length > 0 ? author : "Unknown"
|
|
496
|
-
};
|
|
497
|
-
if (date.length > 0) info.date = date;
|
|
498
|
-
if (initials.length > 0) info.initials = initials;
|
|
499
|
-
return info;
|
|
500
|
-
}
|
|
501
|
-
function parsePropertyChangeInfo(node) {
|
|
502
|
-
const base = parseTrackedChangeInfo(node);
|
|
503
|
-
const rsid = (getAttribute(node, "w", "rsid") ?? "").trim();
|
|
504
|
-
return rsid.length > 0 ? {
|
|
505
|
-
...base,
|
|
506
|
-
rsid
|
|
507
|
-
} : base;
|
|
498
|
+
return cloneElement(node, {
|
|
499
|
+
...mappedName !== void 0 ? { name: mappedName } : {},
|
|
500
|
+
...node.elements ? { elements: node.elements.map(normalizeDeletionContentElement) } : {}
|
|
501
|
+
});
|
|
508
502
|
}
|
|
509
503
|
function parseParagraphPropertyChanges(pPr, theme, styles, currentFormatting) {
|
|
510
504
|
if (!pPr) return;
|
|
511
|
-
const changes =
|
|
512
|
-
const previousFormatting = parseParagraphProperties(
|
|
505
|
+
const changes = findChildrenByNamespaceUri(pPr, WORDPROCESSINGML_NAMESPACE_URIS, "pPrChange").map((changeElement) => {
|
|
506
|
+
const previousFormatting = parseParagraphProperties(findChildByNamespaceUri(changeElement, WORDPROCESSINGML_NAMESPACE_URIS, "pPr"), theme, styles ?? void 0);
|
|
513
507
|
const change = {
|
|
514
508
|
type: "paragraphPropertyChange",
|
|
515
509
|
info: parsePropertyChangeInfo(changeElement)
|
|
@@ -528,7 +522,7 @@ function parseParagraphPropertyChanges(pPr, theme, styles, currentFormatting) {
|
|
|
528
522
|
*/
|
|
529
523
|
function parseParagraphMarkChange(pPr) {
|
|
530
524
|
if (!pPr) return;
|
|
531
|
-
const rPr =
|
|
525
|
+
const rPr = findChildByNamespaceUri(pPr, WORDPROCESSINGML_NAMESPACE_URIS, "rPr");
|
|
532
526
|
if (!rPr) return;
|
|
533
527
|
for (const kind of PARAGRAPH_MARK_CHANGE_KINDS) {
|
|
534
528
|
const element = findChildByNamespaceUri(rPr, WORDPROCESSINGML_NAMESPACE_URIS, kind);
|
|
@@ -1128,6 +1122,7 @@ function parseParagraph(node, styles, theme, numbering, rels = null, media = nul
|
|
|
1128
1122
|
level: ilvl,
|
|
1129
1123
|
numId,
|
|
1130
1124
|
marker: level.lvlText,
|
|
1125
|
+
markerTemplate: level.lvlText,
|
|
1131
1126
|
isBullet: level.numFmt === "bullet",
|
|
1132
1127
|
levelNumFmts,
|
|
1133
1128
|
levelStarts
|
|
@@ -1192,7 +1187,7 @@ function parseParagraph(node, styles, theme, numbering, rels = null, media = nul
|
|
|
1192
1187
|
const directHanging = directInd ? parseNumericAttribute(directInd, "w", "hanging") : void 0;
|
|
1193
1188
|
const hasDirectFirstLineOrHanging = directFirstLine !== void 0 && directFirstLine !== 0 || directHanging !== void 0 && directHanging !== 0;
|
|
1194
1189
|
if (!hasDirectLeft && !chainInd.left && level.pPr.indentLeft !== void 0) paragraph.formatting.indentLeft = level.pPr.indentLeft;
|
|
1195
|
-
if (!hasDirectFirstLineOrHanging && !chainInd.firstLine) {
|
|
1190
|
+
if (!hasDirectFirstLineOrHanging && !chainInd.firstLine && numberingLevelHasMarkerSlot(level)) {
|
|
1196
1191
|
if (level.pPr.indentFirstLine !== void 0) paragraph.formatting.indentFirstLine = level.pPr.indentFirstLine;
|
|
1197
1192
|
if (level.pPr.hangingIndent !== void 0) paragraph.formatting.hangingIndent = level.pPr.hangingIndent;
|
|
1198
1193
|
}
|
|
@@ -1200,6 +1195,10 @@ function parseParagraph(node, styles, theme, numbering, rels = null, media = nul
|
|
|
1200
1195
|
}
|
|
1201
1196
|
}
|
|
1202
1197
|
}
|
|
1198
|
+
if (pPr) assignParagraphPropertySource(paragraph, {
|
|
1199
|
+
xml: captureParagraphPropertySource(pPr),
|
|
1200
|
+
formattingJson: canonicalJson(paragraph.formatting ?? {})
|
|
1201
|
+
});
|
|
1203
1202
|
return paragraph;
|
|
1204
1203
|
}
|
|
1205
1204
|
/**
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { Transaction } from "prosemirror-state";
|
|
3
|
+
import { Fragment, Mark, Node } from "prosemirror-model";
|
|
4
|
+
//#region src/docx/paragraphPropertySource.d.ts
|
|
5
|
+
type ParagraphPropertySource = {
|
|
6
|
+
xml: string;
|
|
7
|
+
formattingJson: string;
|
|
8
|
+
};
|
|
9
|
+
declare const assignParagraphPropertySource: (paragraph: document_d_exports.Paragraph, source: ParagraphPropertySource) => void;
|
|
10
|
+
declare const getParagraphPropertySource: (paragraph: document_d_exports.Paragraph) => ParagraphPropertySource | undefined;
|
|
11
|
+
declare const copyParagraphPropertySource: (target: document_d_exports.Paragraph, source: document_d_exports.Paragraph) => void;
|
|
12
|
+
type ParagraphCloneOverrides = Omit<Partial<document_d_exports.Paragraph>, "type">;
|
|
13
|
+
/** Clone a paragraph while deliberately retaining its parsed `w:pPr` owner. */
|
|
14
|
+
declare const cloneParagraphWithPropertySource: (paragraph: document_d_exports.Paragraph, overrides: ParagraphCloneOverrides) => document_d_exports.Paragraph;
|
|
15
|
+
/** Clone a paragraph whose formatting provenance is deliberately no longer applicable. */
|
|
16
|
+
declare const cloneParagraphWithoutPropertySource: (paragraph: document_d_exports.Paragraph, overrides: ParagraphCloneOverrides) => document_d_exports.Paragraph;
|
|
17
|
+
/**
|
|
18
|
+
* Deep-clone a document and transfer each private paragraph capture across the
|
|
19
|
+
* exact graph clone. `structuredClone` preserves graph topology, so any count
|
|
20
|
+
* mismatch is an internal invariant failure rather than a position heuristic.
|
|
21
|
+
*/
|
|
22
|
+
declare const cloneDocumentWithParagraphPropertySources: (document: document_d_exports.Document) => document_d_exports.Document;
|
|
23
|
+
declare const linkProseParagraphPropertySource: (proseParagraph: Node, sourceParagraph: document_d_exports.Paragraph) => void;
|
|
24
|
+
type RecreateProseNodeOptions = {
|
|
25
|
+
attrs?: Node["attrs"];
|
|
26
|
+
content?: Fragment | Node | readonly Node[] | null;
|
|
27
|
+
marks?: readonly Mark[];
|
|
28
|
+
};
|
|
29
|
+
/** Rebuild a PM node and retain paragraph provenance when the node is one. */
|
|
30
|
+
declare const recreateProseNodeWithParagraphPropertySource: (source: Node, options?: RecreateProseNodeOptions) => Node;
|
|
31
|
+
type SetProseParagraphMarkupOptions = {
|
|
32
|
+
attrs: Node["attrs"];
|
|
33
|
+
ownership: "preserve" | "transfer-allocated-id";
|
|
34
|
+
pos: number;
|
|
35
|
+
transaction: Transaction;
|
|
36
|
+
};
|
|
37
|
+
/** Replace paragraph markup without losing its private parser-owner link. */
|
|
38
|
+
declare const setProseParagraphMarkupWithPropertySource: ({ attrs, ownership, pos, transaction }: SetProseParagraphMarkupOptions) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Carry the parser-owned source identity across load-time paraId allocation.
|
|
41
|
+
* The generated id is safe to use later because it was assigned while the
|
|
42
|
+
* ProseMirror node still had an unambiguous source paragraph owner.
|
|
43
|
+
*/
|
|
44
|
+
declare const transferProseParagraphPropertySource: (target: Node, source: Node, paraId: string) => void;
|
|
45
|
+
declare const linkParagraphPropertySourceCandidate: (target: document_d_exports.Paragraph, source: Node) => void;
|
|
46
|
+
declare const getParagraphPropertySourceCandidate: (paragraph: document_d_exports.Paragraph) => document_d_exports.Paragraph | undefined;
|
|
47
|
+
declare const getParagraphPropertySourceTransferId: (paragraph: document_d_exports.Paragraph) => string | undefined;
|
|
48
|
+
//#endregion
|
|
49
|
+
export { assignParagraphPropertySource, cloneDocumentWithParagraphPropertySources, cloneParagraphWithPropertySource, cloneParagraphWithoutPropertySource, copyParagraphPropertySource, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceTransferId, linkParagraphPropertySourceCandidate, linkProseParagraphPropertySource, recreateProseNodeWithParagraphPropertySource, setProseParagraphMarkupWithPropertySource, transferProseParagraphPropertySource };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { visitDocxParagraphs } from "./paragraphTraversal.js";
|
|
2
|
+
import { panic } from "better-result";
|
|
3
|
+
//#region src/docx/paragraphPropertySource.ts
|
|
4
|
+
const paragraphPropertySources = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
const paragraphPropertySourceOwners = /* @__PURE__ */ new WeakMap();
|
|
6
|
+
const proseParagraphSourceOwners = /* @__PURE__ */ new WeakMap();
|
|
7
|
+
const paragraphPropertySourceCandidates = /* @__PURE__ */ new WeakMap();
|
|
8
|
+
const paragraphPropertySourceTransferIds = /* @__PURE__ */ new WeakMap();
|
|
9
|
+
const assignParagraphPropertySource = (paragraph, source) => {
|
|
10
|
+
paragraphPropertySources.set(paragraph, source);
|
|
11
|
+
paragraphPropertySourceOwners.set(paragraph, paragraph);
|
|
12
|
+
};
|
|
13
|
+
const getParagraphPropertySource = (paragraph) => paragraphPropertySources.get(paragraph);
|
|
14
|
+
const copyParagraphPropertySource = (target, source) => {
|
|
15
|
+
const propertySource = paragraphPropertySources.get(source);
|
|
16
|
+
if (propertySource) {
|
|
17
|
+
paragraphPropertySources.set(target, { ...propertySource });
|
|
18
|
+
paragraphPropertySourceOwners.set(target, paragraphPropertySourceOwners.get(source) ?? source);
|
|
19
|
+
}
|
|
20
|
+
const transferId = paragraphPropertySourceTransferIds.get(source);
|
|
21
|
+
if (transferId) paragraphPropertySourceTransferIds.set(target, transferId);
|
|
22
|
+
const candidate = paragraphPropertySourceCandidates.get(source);
|
|
23
|
+
if (candidate) paragraphPropertySourceCandidates.set(target, candidate);
|
|
24
|
+
};
|
|
25
|
+
/** Clone a paragraph while deliberately retaining its parsed `w:pPr` owner. */
|
|
26
|
+
const cloneParagraphWithPropertySource = (paragraph, overrides) => {
|
|
27
|
+
const cloned = {
|
|
28
|
+
...paragraph,
|
|
29
|
+
...overrides
|
|
30
|
+
};
|
|
31
|
+
copyParagraphPropertySource(cloned, paragraph);
|
|
32
|
+
return cloned;
|
|
33
|
+
};
|
|
34
|
+
/** Clone a paragraph whose formatting provenance is deliberately no longer applicable. */
|
|
35
|
+
const cloneParagraphWithoutPropertySource = (paragraph, overrides) => ({
|
|
36
|
+
...paragraph,
|
|
37
|
+
...overrides
|
|
38
|
+
});
|
|
39
|
+
const paragraphsIn = (document) => {
|
|
40
|
+
const paragraphs = [];
|
|
41
|
+
visitDocxParagraphs({
|
|
42
|
+
documentBody: document.package.document,
|
|
43
|
+
headers: document.package.headers,
|
|
44
|
+
footers: document.package.footers,
|
|
45
|
+
footnotes: document.package.footnotes,
|
|
46
|
+
endnotes: document.package.endnotes
|
|
47
|
+
}, (paragraph) => paragraphs.push(paragraph));
|
|
48
|
+
return paragraphs;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Deep-clone a document and transfer each private paragraph capture across the
|
|
52
|
+
* exact graph clone. `structuredClone` preserves graph topology, so any count
|
|
53
|
+
* mismatch is an internal invariant failure rather than a position heuristic.
|
|
54
|
+
*/
|
|
55
|
+
const cloneDocumentWithParagraphPropertySources = (document) => {
|
|
56
|
+
const cloned = structuredClone(document);
|
|
57
|
+
const sources = paragraphsIn(document);
|
|
58
|
+
const targets = paragraphsIn(cloned);
|
|
59
|
+
if (sources.length !== targets.length) panic("The cloned document changed paragraph graph ownership.");
|
|
60
|
+
for (const [index, source] of sources.entries()) {
|
|
61
|
+
const target = targets.at(index);
|
|
62
|
+
if (!target) panic("The cloned document lost a paragraph owner.");
|
|
63
|
+
copyParagraphPropertySource(target, source);
|
|
64
|
+
}
|
|
65
|
+
return cloned;
|
|
66
|
+
};
|
|
67
|
+
const linkProseParagraphPropertySource = (proseParagraph, sourceParagraph) => {
|
|
68
|
+
if (paragraphPropertySources.has(sourceParagraph)) proseParagraphSourceOwners.set(proseParagraph, paragraphPropertySourceOwners.get(sourceParagraph) ?? sourceParagraph);
|
|
69
|
+
};
|
|
70
|
+
/** Carry a parser-linked paragraph owner across an immutable PM node rebuild. */
|
|
71
|
+
const copyProseParagraphPropertySource = (target, source) => {
|
|
72
|
+
if (target.type.name !== "paragraph" || source.type.name !== "paragraph") return;
|
|
73
|
+
const sourceOwner = proseParagraphSourceOwners.get(source);
|
|
74
|
+
if (sourceOwner) proseParagraphSourceOwners.set(target, sourceOwner);
|
|
75
|
+
};
|
|
76
|
+
/** Rebuild a PM node and retain paragraph provenance when the node is one. */
|
|
77
|
+
const recreateProseNodeWithParagraphPropertySource = (source, options = {}) => {
|
|
78
|
+
const target = source.type.create(options.attrs ?? source.attrs, options.content === void 0 ? source.content : options.content, options.marks ?? source.marks);
|
|
79
|
+
copyProseParagraphPropertySource(target, source);
|
|
80
|
+
return target;
|
|
81
|
+
};
|
|
82
|
+
/** Replace paragraph markup without losing its private parser-owner link. */
|
|
83
|
+
const setProseParagraphMarkupWithPropertySource = ({ attrs, ownership, pos, transaction }) => {
|
|
84
|
+
const source = transaction.doc.nodeAt(pos);
|
|
85
|
+
transaction.setNodeMarkup(pos, void 0, attrs);
|
|
86
|
+
const target = transaction.doc.nodeAt(pos);
|
|
87
|
+
if (!source || !target) return;
|
|
88
|
+
if (ownership === "preserve") {
|
|
89
|
+
copyProseParagraphPropertySource(target, source);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const paraId = target.attrs["paraId"];
|
|
93
|
+
if (typeof paraId === "string") transferProseParagraphPropertySource(target, source, paraId);
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Carry the parser-owned source identity across load-time paraId allocation.
|
|
97
|
+
* The generated id is safe to use later because it was assigned while the
|
|
98
|
+
* ProseMirror node still had an unambiguous source paragraph owner.
|
|
99
|
+
*/
|
|
100
|
+
const transferProseParagraphPropertySource = (target, source, paraId) => {
|
|
101
|
+
const sourceOwner = proseParagraphSourceOwners.get(source);
|
|
102
|
+
if (!sourceOwner) return;
|
|
103
|
+
proseParagraphSourceOwners.set(target, sourceOwner);
|
|
104
|
+
paragraphPropertySourceTransferIds.set(sourceOwner, paraId);
|
|
105
|
+
};
|
|
106
|
+
const linkParagraphPropertySourceCandidate = (target, source) => {
|
|
107
|
+
const sourceOwner = proseParagraphSourceOwners.get(source);
|
|
108
|
+
if (sourceOwner) paragraphPropertySourceCandidates.set(target, sourceOwner);
|
|
109
|
+
};
|
|
110
|
+
const getParagraphPropertySourceCandidate = (paragraph) => paragraphPropertySourceCandidates.get(paragraph);
|
|
111
|
+
const getParagraphPropertySourceTransferId = (paragraph) => paragraphPropertySourceTransferIds.get(paragraph);
|
|
112
|
+
//#endregion
|
|
113
|
+
export { assignParagraphPropertySource, cloneDocumentWithParagraphPropertySources, cloneParagraphWithPropertySource, cloneParagraphWithoutPropertySource, copyParagraphPropertySource, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceTransferId, linkParagraphPropertySourceCandidate, linkProseParagraphPropertySource, recreateProseNodeWithParagraphPropertySource, setProseParagraphMarkupWithPropertySource, transferProseParagraphPropertySource };
|
|
@@ -1,41 +1,37 @@
|
|
|
1
1
|
//#region src/docx/paragraphTraversal.ts
|
|
2
2
|
/** Visit every run directly owned by a paragraph's inline-content tree. */
|
|
3
3
|
const visitParagraphRuns = (paragraph, visit) => {
|
|
4
|
-
const visitRun = (run) => {
|
|
5
|
-
visit(run);
|
|
6
|
-
};
|
|
7
|
-
const visitHyperlink = (hyperlink) => {
|
|
8
|
-
for (const child of hyperlink.children) if (child.type === "run") visitRun(child);
|
|
9
|
-
};
|
|
10
|
-
const visitInlineContent = (content) => {
|
|
11
|
-
for (const child of content) {
|
|
12
|
-
if (child.type === "run") {
|
|
13
|
-
visitRun(child);
|
|
14
|
-
continue;
|
|
15
|
-
}
|
|
16
|
-
if (child.type === "hyperlink") visitHyperlink(child);
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
4
|
const visitParagraphContent = (content) => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
5
|
+
switch (content.type) {
|
|
6
|
+
case "run":
|
|
7
|
+
visit(content);
|
|
8
|
+
return;
|
|
9
|
+
case "hyperlink":
|
|
10
|
+
for (const child of content.children) visitParagraphContent(child);
|
|
11
|
+
return;
|
|
12
|
+
case "simpleField":
|
|
13
|
+
case "insertion":
|
|
14
|
+
case "deletion":
|
|
15
|
+
case "moveFrom":
|
|
16
|
+
case "moveTo":
|
|
17
|
+
case "inlineSdt":
|
|
18
|
+
for (const child of content.content) visitParagraphContent(child);
|
|
19
|
+
return;
|
|
20
|
+
case "complexField":
|
|
21
|
+
for (const run of content.fieldCode) visit(run);
|
|
22
|
+
for (const run of content.fieldResult) visit(run);
|
|
23
|
+
return;
|
|
24
|
+
case "bookmarkStart":
|
|
25
|
+
case "bookmarkEnd":
|
|
26
|
+
case "commentRangeStart":
|
|
27
|
+
case "commentRangeEnd":
|
|
28
|
+
case "commentReference":
|
|
29
|
+
case "moveFromRangeStart":
|
|
30
|
+
case "moveFromRangeEnd":
|
|
31
|
+
case "moveToRangeStart":
|
|
32
|
+
case "moveToRangeEnd":
|
|
33
|
+
case "mathEquation": return;
|
|
34
|
+
default: return content;
|
|
39
35
|
}
|
|
40
36
|
};
|
|
41
37
|
for (const content of paragraph.content) visitParagraphContent(content);
|
package/dist/docx/rezip.d.ts
CHANGED
|
@@ -28,14 +28,6 @@ type RepackOptions = {
|
|
|
28
28
|
/** Changed note paragraphs that must be serialized even without source paraIds. */
|
|
29
29
|
changedNoteParaIds?: ReadonlySet<string>;
|
|
30
30
|
};
|
|
31
|
-
/**
|
|
32
|
-
* Repack a Document into a valid DOCX file
|
|
33
|
-
*
|
|
34
|
-
* @param doc - Document with modified content
|
|
35
|
-
* @param options - Optional repack options
|
|
36
|
-
* @returns Promise resolving to DOCX as ArrayBuffer
|
|
37
|
-
* @throws {Error} if document has no original buffer for round-trip
|
|
38
|
-
*/
|
|
39
31
|
declare function repackDocx(doc: document_d_exports.Document, options?: RepackOptions): Promise<ArrayBuffer>;
|
|
40
32
|
/**
|
|
41
33
|
* Repack a Document using raw content for more control
|
package/dist/docx/rezip.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { consumeTrackedSectionEndpointRemoval } from "../internal/sectionEndpointResolution.js";
|
|
1
2
|
import { isAllowedExternalWatermarkImageUrl } from "../watermark/index.js";
|
|
2
3
|
import { normalizeAppVersionInExtendedProperties } from "./appVersionNormalization.js";
|
|
3
4
|
import { withoutOrphanCommentRanges } from "./commentRangeIntegrity.js";
|
|
@@ -115,11 +116,34 @@ const extractHeaderFooterReferences = (xml) => {
|
|
|
115
116
|
const hasParsedHeaderFooterPart = (doc, ref) => {
|
|
116
117
|
return (ref.element === "headerReference" ? doc.package.headers : doc.package.footers)?.has(ref.rId) ?? false;
|
|
117
118
|
};
|
|
118
|
-
|
|
119
|
+
const headerFooterReferenceKey = ({ element, type, rId }) => `${element}:${type}:${rId}`;
|
|
120
|
+
const incrementReferenceCount = (counts, key) => {
|
|
121
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
122
|
+
};
|
|
123
|
+
const consumeReferenceCount = (counts, key) => {
|
|
124
|
+
const count = counts.get(key) ?? 0;
|
|
125
|
+
if (count === 0) return false;
|
|
126
|
+
counts.set(key, count - 1);
|
|
127
|
+
return true;
|
|
128
|
+
};
|
|
129
|
+
function assertDocumentPackageFidelity(originalDocumentXml, serializedDocumentXml, doc, sectionEndpointRemoval) {
|
|
119
130
|
const originalSectionCount = countDocumentSections(originalDocumentXml);
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
131
|
+
const serializedSectionCount = countDocumentSections(serializedDocumentXml);
|
|
132
|
+
const trackedRemovedSectionCount = sectionEndpointRemoval ? sectionEndpointRemoval.sourceParagraphEndpointCount - sectionEndpointRemoval.expectedParagraphEndpointCount : 0;
|
|
133
|
+
const exactTrackedSectionRemoval = trackedRemovedSectionCount > 0 && originalSectionCount - trackedRemovedSectionCount === serializedSectionCount && serializedSectionCount < originalSectionCount;
|
|
134
|
+
if (serializedSectionCount < originalSectionCount && !exactTrackedSectionRemoval) throw new DocxPackageFidelityError("Full DOCX repack would drop section properties. Use selective patching instead.");
|
|
135
|
+
const serializedReferenceCounts = /* @__PURE__ */ new Map();
|
|
136
|
+
for (const reference of extractHeaderFooterReferences(serializedDocumentXml)) incrementReferenceCount(serializedReferenceCounts, headerFooterReferenceKey(reference));
|
|
137
|
+
const removedReferenceCounts = /* @__PURE__ */ new Map();
|
|
138
|
+
if (exactTrackedSectionRemoval && sectionEndpointRemoval) for (const { part, type, relationshipId } of sectionEndpointRemoval.removedReferences) incrementReferenceCount(removedReferenceCounts, `${part}Reference:${type}:${relationshipId}`);
|
|
139
|
+
const missingRefs = [];
|
|
140
|
+
for (const reference of extractHeaderFooterReferences(originalDocumentXml)) {
|
|
141
|
+
if (!hasParsedHeaderFooterPart(doc, reference)) continue;
|
|
142
|
+
const key = headerFooterReferenceKey(reference);
|
|
143
|
+
if (consumeReferenceCount(serializedReferenceCounts, key) || consumeReferenceCount(removedReferenceCounts, key)) continue;
|
|
144
|
+
missingRefs.push(reference);
|
|
145
|
+
}
|
|
146
|
+
if (missingRefs.length > 0) throw new DocxPackageFidelityError("Full DOCX repack would drop header/footer references. Use selective patching instead.");
|
|
123
147
|
}
|
|
124
148
|
async function serializeCommentsToZip(doc, zip, compressionLevel) {
|
|
125
149
|
const comments = doc.package.document.comments ?? [];
|
|
@@ -488,7 +512,7 @@ const cloneDocxZip = (source) => {
|
|
|
488
512
|
clone.files = { ...source.files };
|
|
489
513
|
return clone;
|
|
490
514
|
};
|
|
491
|
-
const finishRepack = async ({ document, originalZip, outputZip, originalDocumentXml, originalCorePropertiesXml, compressionLevel, updateModifiedDate, modifiedBy, changedNoteParaIds }) => {
|
|
515
|
+
const finishRepack = async ({ document, originalZip, outputZip, originalDocumentXml, originalCorePropertiesXml, compressionLevel, updateModifiedDate, modifiedBy, changedNoteParaIds, sectionEndpointRemoval }) => {
|
|
492
516
|
await materializeNewHeaderFooterParts(document, outputZip, compressionLevel);
|
|
493
517
|
const parts = collectDocxParts(document, outputZip);
|
|
494
518
|
await processNewImages(parts, outputZip, compressionLevel);
|
|
@@ -496,7 +520,7 @@ const finishRepack = async ({ document, originalZip, outputZip, originalDocument
|
|
|
496
520
|
assertValidFolioDocumentModel(document, "Cannot repack invalid DOCX document model");
|
|
497
521
|
applyReplyThreadMarkers(document);
|
|
498
522
|
const documentXml = serializeDocument(document, originalDocumentXml === void 0 ? void 0 : readRootNamespaceBindings(originalDocumentXml));
|
|
499
|
-
if (originalDocumentXml) assertDocumentPackageFidelity(originalDocumentXml, documentXml, document);
|
|
523
|
+
if (originalDocumentXml) assertDocumentPackageFidelity(originalDocumentXml, documentXml, document, sectionEndpointRemoval);
|
|
500
524
|
outputZip.file("word/document.xml", documentXml, {
|
|
501
525
|
compression: "DEFLATE",
|
|
502
526
|
compressionOptions: { level: compressionLevel }
|
|
@@ -526,15 +550,7 @@ const finishRepack = async ({ document, originalZip, outputZip, originalDocument
|
|
|
526
550
|
}
|
|
527
551
|
return generateDocxZip(outputZip, compressionLevel);
|
|
528
552
|
};
|
|
529
|
-
|
|
530
|
-
* Repack a Document into a valid DOCX file
|
|
531
|
-
*
|
|
532
|
-
* @param doc - Document with modified content
|
|
533
|
-
* @param options - Optional repack options
|
|
534
|
-
* @returns Promise resolving to DOCX as ArrayBuffer
|
|
535
|
-
* @throws {Error} if document has no original buffer for round-trip
|
|
536
|
-
*/
|
|
537
|
-
async function repackDocx(doc, options = {}) {
|
|
553
|
+
async function repackDocxWithSectionEndpointRemoval({ document: doc, options, sectionEndpointRemoval }) {
|
|
538
554
|
if (!doc.originalBuffer) panic("Cannot repack document: no original buffer for round-trip. Use createDocx() for new documents.");
|
|
539
555
|
const { compressionLevel = 6, updateModifiedDate = true, modifiedBy, changedNoteParaIds } = options;
|
|
540
556
|
const exportDocument = withoutOrphanCommentRanges(doc);
|
|
@@ -551,7 +567,16 @@ async function repackDocx(doc, options = {}) {
|
|
|
551
567
|
compressionLevel,
|
|
552
568
|
updateModifiedDate,
|
|
553
569
|
...modifiedBy !== void 0 ? { modifiedBy } : {},
|
|
554
|
-
...changedNoteParaIds !== void 0 ? { changedNoteParaIds } : {}
|
|
570
|
+
...changedNoteParaIds !== void 0 ? { changedNoteParaIds } : {},
|
|
571
|
+
...sectionEndpointRemoval !== void 0 ? { sectionEndpointRemoval } : {}
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
function repackDocx(doc, options = {}) {
|
|
575
|
+
const sectionEndpointRemoval = consumeTrackedSectionEndpointRemoval(doc);
|
|
576
|
+
return repackDocxWithSectionEndpointRemoval({
|
|
577
|
+
document: doc,
|
|
578
|
+
options,
|
|
579
|
+
...sectionEndpointRemoval ? { sectionEndpointRemoval } : {}
|
|
555
580
|
});
|
|
556
581
|
}
|
|
557
582
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { cloneParagraphWithPropertySource } from "./paragraphPropertySource.js";
|
|
1
2
|
//#region src/docx/runConsolidator.ts
|
|
2
3
|
/**
|
|
3
4
|
* Check if two TextFormatting objects are equivalent
|
|
@@ -193,10 +194,7 @@ function consolidateParagraphContent(content) {
|
|
|
193
194
|
*/
|
|
194
195
|
function consolidateParagraph(paragraph) {
|
|
195
196
|
if (paragraph.content.length === 0) return paragraph;
|
|
196
|
-
return {
|
|
197
|
-
...paragraph,
|
|
198
|
-
content: consolidateParagraphContent(paragraph.content)
|
|
199
|
-
};
|
|
197
|
+
return cloneParagraphWithPropertySource(paragraph, { content: consolidateParagraphContent(paragraph.content) });
|
|
200
198
|
}
|
|
201
199
|
/**
|
|
202
200
|
* Get the number of runs in a paragraph (for debugging/metrics)
|
package/dist/docx/runParser.js
CHANGED
|
@@ -7,10 +7,11 @@ import { parseShapeFromDrawing, shouldPreserveRawShapeDrawing } from "./shapePar
|
|
|
7
7
|
import { isTextBoxDrawing } from "./textBoxParser.js";
|
|
8
8
|
import { requiresXmlSpacePreserve } from "./textWhitespace.js";
|
|
9
9
|
import { resolveThemeFontRef } from "./themeParser.js";
|
|
10
|
+
import { parsePropertyChangeInfo } from "./trackedChangeInfo.js";
|
|
10
11
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
11
12
|
import { parseVmlImageContent } from "./vmlImageParser.js";
|
|
12
13
|
import { cloneWithXmlnsDeclarations, findAllDeep, findChild, findChildren, getAttribute, getChildElements, getLocalName, getTextContent, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute } from "./xmlParser.js";
|
|
13
|
-
import { DRAWING_RAW_XML_MODES
|
|
14
|
+
import { DRAWING_RAW_XML_MODES } from "@stll/docx-core/model";
|
|
14
15
|
//#region src/docx/runParser.ts
|
|
15
16
|
/**
|
|
16
17
|
* Sanity cap on `w:lang` `@w:val`/`@w:eastAsia`/`@w:bidi` tag length. BCP-47
|
|
@@ -40,9 +41,11 @@ function parseShadingProperties(shd) {
|
|
|
40
41
|
if (!shd) return;
|
|
41
42
|
const props = {};
|
|
42
43
|
const color = getAttribute(shd, "w", "color");
|
|
43
|
-
if (color
|
|
44
|
+
if (color === "auto") props.color = { auto: true };
|
|
45
|
+
else if (color && isValidHexColor(color)) props.color = { rgb: color };
|
|
44
46
|
const fill = getAttribute(shd, "w", "fill");
|
|
45
|
-
if (fill
|
|
47
|
+
if (fill === "auto") props.fill = { auto: true };
|
|
48
|
+
else if (fill && isValidHexColor(fill)) props.fill = { rgb: fill };
|
|
46
49
|
const validatedThemeFill = narrowEnum(getAttribute(shd, "w", "themeFill"), ThemeColorSlotSchema);
|
|
47
50
|
if (validatedThemeFill) {
|
|
48
51
|
if (!props.fill) props.fill = {};
|
|
@@ -341,20 +344,6 @@ function parseRunProperties(rPr, theme, _styles) {
|
|
|
341
344
|
}
|
|
342
345
|
return Object.keys(formatting).length > 0 ? formatting : void 0;
|
|
343
346
|
}
|
|
344
|
-
function parsePropertyChangeInfo(changeElement) {
|
|
345
|
-
const rawId = getAttribute(changeElement, "w", "id");
|
|
346
|
-
const parsedId = rawId ? Number.parseInt(rawId, 10) : 0;
|
|
347
|
-
const author = (getAttribute(changeElement, "w", "author") ?? "").trim();
|
|
348
|
-
const date = (getAttribute(changeElement, "w", "date") ?? "").trim();
|
|
349
|
-
const rsid = (getAttribute(changeElement, "w", "rsid") ?? "").trim();
|
|
350
|
-
const info = {
|
|
351
|
-
id: normalizeRevisionId(parsedId),
|
|
352
|
-
author: author.length > 0 ? author : "Unknown"
|
|
353
|
-
};
|
|
354
|
-
if (date.length > 0) info.date = date;
|
|
355
|
-
if (rsid.length > 0) info.rsid = rsid;
|
|
356
|
-
return info;
|
|
357
|
-
}
|
|
358
347
|
function parseRunPropertyChanges(rPr, theme, styles, currentFormatting) {
|
|
359
348
|
if (!rPr) return;
|
|
360
349
|
const changes = findChildren(rPr, "w", "rPrChange").map((changeElement) => {
|