@stll/folio-core 0.54.2 → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/apply.js +185 -33
- package/dist/ai-edits/headless.js +3 -2
- package/dist/ai-edits/types.d.ts +7 -1
- package/dist/compare/compare.js +1 -0
- package/dist/compare/content-types.d.ts +3 -15
- package/dist/compare/reproducible-package.d.ts +0 -17
- package/dist/compare/reproducible-package.js +12 -18
- package/dist/controller/canonicalClipboard.d.ts +23 -0
- package/dist/controller/canonicalClipboard.js +476 -0
- package/dist/controller/canonicalClipboardResources.d.ts +21 -0
- package/dist/controller/canonicalClipboardResources.js +382 -0
- package/dist/controller/canonicalComposition.d.ts +53 -0
- package/dist/controller/canonicalComposition.js +296 -0
- package/dist/controller/canonicalInlineProjection.d.ts +113 -0
- package/dist/controller/canonicalInlineProjection.js +393 -0
- package/dist/controller/canonicalInput.d.ts +54 -0
- package/dist/controller/canonicalInput.js +377 -0
- package/dist/controller/canonicalInputTimer.d.ts +7 -0
- package/dist/controller/canonicalInputTimer.js +11 -0
- package/dist/controller/canonicalOperations.d.ts +22 -0
- package/dist/controller/canonicalOperations.js +85 -0
- package/dist/controller/canonicalPublicOperations.d.ts +48 -0
- package/dist/controller/canonicalPublicOperations.js +556 -0
- package/dist/controller/canonicalReview.d.ts +12 -0
- package/dist/controller/canonicalReview.js +19 -0
- package/dist/controller/canonicalSession.d.ts +167 -0
- package/dist/controller/canonicalSession.js +1186 -0
- package/dist/controller/canonicalStoryEditor.d.ts +36 -0
- package/dist/controller/canonicalStoryEditor.js +87 -0
- package/dist/controller/canonicalStructure.d.ts +16 -0
- package/dist/controller/canonicalStructure.js +322 -0
- package/dist/controller/canonicalTocSelection.d.ts +11 -0
- package/dist/controller/canonicalTocSelection.js +43 -0
- package/dist/controller/folioEditor.d.ts +3 -10
- package/dist/controller/folioEditor.js +14 -4
- package/dist/controller/headerFooterEditorManager.d.ts +5 -0
- package/dist/controller/headerFooterEditorManager.js +72 -3
- package/dist/controller/hiddenEditorApi.d.ts +45 -0
- package/dist/controller/hiddenEditorApi.js +29 -8
- package/dist/controller/hiddenEditorManager.d.ts +13 -2
- package/dist/controller/hiddenEditorManager.js +639 -31
- package/dist/controller/layoutPipeline.d.ts +1 -1
- package/dist/controller/layoutPipeline.js +2 -0
- package/dist/controller/noteEditorManager.d.ts +5 -0
- package/dist/controller/noteEditorManager.js +114 -15
- package/dist/document-operations.d.ts +8 -1
- package/dist/document-operations.js +6 -4
- package/dist/docx/attributeRemainder.js +1 -0
- package/dist/docx/blockPlainText.d.ts +1 -1
- package/dist/docx/blockPlainText.js +11 -3
- package/dist/docx/canonicalResourceSave.d.ts +16 -0
- package/dist/docx/canonicalResourceSave.js +27 -0
- package/dist/docx/canonicalSave.d.ts +29 -0
- package/dist/docx/canonicalSave.js +101 -0
- package/dist/docx/canonicalSessionInput.d.ts +11 -0
- package/dist/docx/canonicalSessionInput.js +27 -0
- package/dist/docx/canonicalStoryRepack.d.ts +10 -0
- package/dist/docx/canonicalStoryRepack.js +74 -0
- package/dist/docx/contentTypeRegistry.d.ts +8 -0
- package/dist/docx/contentTypeRegistry.js +64 -0
- package/dist/docx/documentParser.d.ts +1 -13
- package/dist/docx/documentParser.js +1 -47
- package/dist/docx/ensureParaIds.d.ts +7 -1
- package/dist/docx/ensureParaIds.js +173 -215
- package/dist/docx/foldedListNumberFields.d.ts +85 -0
- package/dist/docx/foldedListNumberFields.js +267 -0
- package/dist/docx/footnoteParser.d.ts +1 -11
- package/dist/docx/footnoteParser.js +1 -16
- package/dist/docx/headerFooterParser.d.ts +1 -9
- package/dist/docx/headerFooterParser.js +1 -12
- package/dist/docx/headerFooterVerbatim.d.ts +39 -1
- package/dist/docx/headerFooterVerbatim.js +173 -2
- package/dist/docx/imageParser.d.ts +13 -1
- package/dist/docx/imageParser.js +20 -11
- package/dist/docx/metadataPrivacy.js +6 -1
- package/dist/docx/noteIds.d.ts +5 -0
- package/dist/docx/noteIds.js +6 -2
- package/dist/docx/noteReferenceMark.d.ts +3 -1
- package/dist/docx/noteReferenceMark.js +15 -14
- package/dist/docx/numberingParser.d.ts +3 -1
- package/dist/docx/numberingParser.js +17 -3
- package/dist/docx/packageParts.d.ts +3 -1
- package/dist/docx/packageParts.js +74 -19
- package/dist/docx/paragraphMarkPropertyPresence.d.ts +9 -0
- package/dist/docx/paragraphMarkPropertyPresence.js +21 -0
- package/dist/docx/paragraphParser.js +101 -53
- package/dist/docx/paragraphPropertySource.d.ts +15 -1
- package/dist/docx/paragraphPropertySource.js +77 -22
- package/dist/docx/parser.js +3 -1
- package/dist/docx/removeHeaderFooterParts.js +17 -6
- package/dist/docx/reviewHistoryNamespace.d.ts +4 -0
- package/dist/docx/reviewHistoryNamespace.js +4 -0
- package/dist/docx/reviewResolutionProvenance.d.ts +25 -0
- package/dist/docx/reviewResolutionProvenance.js +153 -0
- package/dist/docx/rezip.d.ts +54 -6
- package/dist/docx/rezip.js +835 -257
- package/dist/docx/runConsolidator.d.ts +2 -58
- package/dist/docx/runConsolidator.js +3 -175
- package/dist/docx/runParser.d.ts +2 -1
- package/dist/docx/runParser.js +156 -116
- package/dist/docx/saveDiagnostics.d.ts +22 -0
- package/dist/docx/saveDiagnostics.js +0 -0
- package/dist/docx/selectiveSave.d.ts +3 -1
- package/dist/docx/selectiveSave.js +36 -4
- package/dist/docx/selectiveXmlPatch.d.ts +16 -8
- package/dist/docx/selectiveXmlPatch.js +81 -7
- package/dist/docx/serializer/documentSerializer.d.ts +9 -2
- package/dist/docx/serializer/documentSerializer.js +29 -10
- package/dist/docx/serializer/headerFooterSerializer.d.ts +5 -1
- package/dist/docx/serializer/headerFooterSerializer.js +19 -11
- package/dist/docx/serializer/noteSerializer.d.ts +2 -2
- package/dist/docx/serializer/noteSerializer.js +24 -8
- package/dist/docx/serializer/paragraphSerializer.js +41 -4
- package/dist/docx/serializer/partNamespaces.js +2 -1
- package/dist/docx/serializer/runSerializer.js +3 -1
- package/dist/docx/server/applyDocxXmlPatchProposal.js +14 -9
- package/dist/docx/settingsHeaderFooterUpdate.d.ts +5 -0
- package/dist/docx/settingsHeaderFooterUpdate.js +90 -0
- package/dist/docx/settingsParser.d.ts +1 -2
- package/dist/docx/settingsParser.js +5 -6
- package/dist/docx/storyBlockReplay.d.ts +31 -0
- package/dist/docx/storyBlockReplay.js +471 -0
- package/dist/docx/storyPlainText.d.ts +20 -0
- package/dist/docx/storyPlainText.js +23 -0
- package/dist/docx/streamingXmlParser.d.ts +22 -1
- package/dist/docx/streamingXmlParser.js +49 -3
- package/dist/docx/structuralXmlPatch.js +18 -23
- package/dist/docx/styleParser.d.ts +5 -1
- package/dist/docx/styleParser.js +1 -1
- package/dist/docx/tableParser.d.ts +1 -8
- package/dist/docx/tableParser.js +1 -28
- package/dist/docx/textBoxParser.d.ts +1 -5
- package/dist/docx/textBoxParser.js +1 -20
- package/dist/docx/unzip.js +6 -1
- package/dist/docx/verbatimCapture.d.ts +3 -1
- package/dist/docx/verbatimCapture.js +49 -6
- package/dist/i18n/messages/ar.gen.d.ts +5 -0
- package/dist/i18n/messages/ar.gen.js +656 -0
- package/dist/i18n/messages/cs.gen.d.ts +5 -0
- package/dist/i18n/messages/cs.gen.js +656 -0
- package/dist/i18n/messages/de.gen.d.ts +5 -0
- package/dist/i18n/messages/de.gen.js +656 -0
- package/dist/i18n/messages/en.gen.d.ts +5 -0
- package/dist/i18n/messages/en.gen.js +656 -0
- package/dist/i18n/messages/es.gen.d.ts +5 -0
- package/dist/i18n/messages/es.gen.js +656 -0
- package/dist/i18n/messages/et.gen.d.ts +5 -0
- package/dist/i18n/messages/et.gen.js +656 -0
- package/dist/i18n/messages/fr.gen.d.ts +5 -0
- package/dist/i18n/messages/fr.gen.js +656 -0
- package/dist/i18n/messages/he.gen.d.ts +5 -0
- package/dist/i18n/messages/he.gen.js +656 -0
- package/dist/i18n/messages/hi.gen.d.ts +5 -0
- package/dist/i18n/messages/hi.gen.js +656 -0
- package/dist/i18n/messages/hu.gen.d.ts +5 -0
- package/dist/i18n/messages/hu.gen.js +656 -0
- package/dist/i18n/messages/locales.d.ts +5 -0
- package/dist/i18n/messages/locales.js +22 -0
- package/dist/i18n/messages/lt.gen.d.ts +5 -0
- package/dist/i18n/messages/lt.gen.js +656 -0
- package/dist/i18n/messages/lv.gen.d.ts +5 -0
- package/dist/i18n/messages/lv.gen.js +656 -0
- package/dist/i18n/messages/pl.gen.d.ts +5 -0
- package/dist/i18n/messages/pl.gen.js +656 -0
- package/dist/i18n/messages/pt-BR.gen.d.ts +5 -0
- package/dist/i18n/messages/pt-BR.gen.js +656 -0
- package/dist/i18n/messages/sk.gen.d.ts +5 -0
- package/dist/i18n/messages/sk.gen.js +656 -0
- package/dist/i18n/messages/tr.gen.d.ts +5 -0
- package/dist/i18n/messages/tr.gen.js +656 -0
- package/dist/i18n/messages/zh-CN.gen.d.ts +5 -0
- package/dist/i18n/messages/zh-CN.gen.js +656 -0
- package/dist/i18n/messages.d.ts +2 -3
- package/dist/i18n/messages.js +1 -19
- package/dist/internal/acceptedBlockProjection.d.ts +12 -0
- package/dist/internal/acceptedBlockProjection.js +45 -0
- package/dist/internal/noteMarkerAttrs.d.ts +8 -0
- package/dist/internal/noteMarkerAttrs.js +16 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +1 -0
- package/dist/internal/prosemirrorAttrBoundary.d.ts +21 -0
- package/dist/internal/prosemirrorAttrBoundary.js +50 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +3 -3
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +1 -0
- package/dist/layout-bridge/convert/noteReferences.d.ts +1 -3
- package/dist/layout-bridge/convert/noteReferences.js +1 -6
- package/dist/layout-bridge/convert/paragraphRuns.js +2 -4
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +2 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +2 -2
- package/dist/managers/DocumentLoaderManager.d.ts +1 -0
- package/dist/managers/DocumentLoaderManager.js +9 -1
- package/dist/markdown/escape.d.ts +3 -1
- package/dist/markdown/escape.js +9 -2
- package/dist/markdown/renderBlock.js +20 -121
- package/dist/markdown/renderParagraph.js +13 -15
- package/dist/markdown/renderRuns.d.ts +7 -1
- package/dist/markdown/renderRuns.js +35 -15
- package/dist/markdown/renderTable.js +2 -0
- package/dist/markdown/types.d.ts +3 -9
- package/dist/prosemirror/attrs/index.d.ts +2 -12
- package/dist/prosemirror/attrs/index.js +22 -48
- package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +2 -1
- package/dist/prosemirror/canonicalCommands.d.ts +121 -0
- package/dist/prosemirror/canonicalCommands.js +35 -0
- package/dist/prosemirror/canonicalSelectionRange.d.ts +9 -0
- package/dist/prosemirror/canonicalSelectionRange.js +15 -0
- package/dist/prosemirror/clipboardIntent.d.ts +7 -0
- package/dist/prosemirror/clipboardIntent.js +9 -0
- package/dist/prosemirror/commands/pageBreak.js +15 -1
- package/dist/prosemirror/commands/pastePlainText.js +2 -1
- package/dist/prosemirror/commentReferenceAttrs.d.ts +2 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +91 -19
- package/dist/prosemirror/conversion/hyphenTextCarriers.d.ts +8 -0
- package/dist/prosemirror/conversion/hyphenTextCarriers.js +8 -0
- package/dist/prosemirror/conversion/listRenderingDefinition.d.ts +6 -0
- package/dist/prosemirror/conversion/listRenderingDefinition.js +97 -0
- package/dist/prosemirror/conversion/toProseDoc.d.ts +11 -4
- package/dist/prosemirror/conversion/toProseDoc.js +55 -14
- package/dist/prosemirror/executeEditorCommand.d.ts +11 -0
- package/dist/prosemirror/executeEditorCommand.js +26 -0
- package/dist/prosemirror/extensions/StarterKit.js +2 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +88 -48
- package/dist/prosemirror/extensions/features/ListExtension.js +18 -5
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +5 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +13 -1
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +56 -28
- package/dist/prosemirror/extensions/marks/TextColorExtension.js +3 -2
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
- package/dist/prosemirror/extensions/marks/markUtils.js +72 -8
- package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.js +36 -0
- package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +4 -2
- package/dist/prosemirror/insertOperations.js +2 -1
- package/dist/prosemirror/listMarker.d.ts +1 -0
- package/dist/prosemirror/listMarker.js +16 -7
- package/dist/prosemirror/listNumbering.js +26 -5
- package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
- package/dist/prosemirror/listRenderingAttrs.js +3 -0
- package/dist/prosemirror/markupViewNotes.js +3 -2
- package/dist/prosemirror/moveRangeBoundaryAttrs.d.ts +2 -1
- package/dist/prosemirror/noteReferenceReview.js +5 -3
- package/dist/prosemirror/paragraphPropertyCarry.d.ts +7 -1
- package/dist/prosemirror/paragraphPropertyCarry.js +8 -3
- package/dist/prosemirror/plugins/suggestionMode.js +57 -7
- package/dist/prosemirror/rangeAnchorAttrs.d.ts +2 -1
- package/dist/prosemirror/runFormattingFromMarks.d.ts +4 -1
- package/dist/prosemirror/runFormattingFromMarks.js +15 -1
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +2 -1
- package/dist/prosemirror/runFormattingInlineCarriers.js +4 -1
- package/dist/prosemirror/schema/marks.d.ts +2 -0
- package/dist/prosemirror/schema/nodes.d.ts +10 -2
- package/dist/prosemirror/textBoxAnchorAttrs.d.ts +2 -1
- package/dist/prosemirror/textInput.js +5 -1
- package/dist/prosemirror/trackedRevisionPath.js +2 -1
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -0
- package/dist/prosemirror/trackedRunInlineAtoms.js +1 -0
- package/dist/prosemirror/validation.js +4 -0
- package/dist/types/canonicalCapabilities.d.ts +215 -0
- package/dist/types/canonicalCapabilities.js +212 -0
- package/dist/types/canonicalSave.d.ts +11 -0
- package/dist/types/canonicalSave.js +0 -0
- package/dist/types/content.d.ts +2 -2
- package/dist/types/docxSerialization.d.ts +14 -0
- package/dist/types/docxSerialization.js +7 -0
- package/dist/utils/createDocument.js +2 -2
- package/package.json +16 -4
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { deterministicHexId } from "../utils/hexId.js";
|
|
2
|
-
import {
|
|
2
|
+
import { resolvePackageRelationshipTarget } from "./packageParts.js";
|
|
3
|
+
import { spliceXml } from "./selectiveXmlPatch.js";
|
|
3
4
|
import { loadDocxArchive } from "./server/boundedArchive.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import { scanStreamingXmlElements } from "./streamingXmlParser.js";
|
|
6
|
+
import { resolveNamespaceUri } from "./xmlNamespaceContext.js";
|
|
7
|
+
import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, WORDPROCESSINGML_NAMESPACE_URIS, getAttribute, getChildElements, getLocalName, getNamespaceUri, parseXmlDocument, resolveAttributeNamespaceUri } from "./xmlParser.js";
|
|
6
8
|
import { TaggedError } from "better-result";
|
|
9
|
+
import { writeZipPart } from "@stll/docx-core/zip";
|
|
7
10
|
import JSZip from "jszip";
|
|
8
11
|
//#region src/docx/ensureParaIds.ts
|
|
9
12
|
/**
|
|
@@ -27,7 +30,7 @@ import JSZip from "jszip";
|
|
|
27
30
|
* so documents carrying features folio's parser does not model come back
|
|
28
31
|
* with those features byte-identical. Contract:
|
|
29
32
|
*
|
|
30
|
-
* - Parts covered:
|
|
33
|
+
* - Parts covered: the officeDocument relationship target, `word/header*.xml`, `word/footer*.xml`,
|
|
31
34
|
* `word/footnotes.xml`, `word/endnotes.xml`. Paragraphs nested in table
|
|
32
35
|
* cells and in `mc:Choice` text boxes are plain `<w:p>` elements inside
|
|
33
36
|
* those parts and are covered by the same scan. The comments part mints its
|
|
@@ -50,12 +53,10 @@ import JSZip from "jszip";
|
|
|
50
53
|
* declarations and a `mc:Ignorable` listing `w14` when missing — non-Word
|
|
51
54
|
* producers declare neither, and absent `mc:Ignorable` handling is what
|
|
52
55
|
* makes pre-2010 consumers choke on the new attributes.
|
|
53
|
-
* - Prefixes are aliases
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* part whose bindings a string scan cannot follow (a nested element that
|
|
58
|
-
* rebinds one of them) is refused with {@link EnsureParaIdsError}.
|
|
56
|
+
* - Prefixes are aliases: elements and attributes resolve against their
|
|
57
|
+
* in-scope namespace URI, including Strict and Transitional profiles and
|
|
58
|
+
* nested rebinding. Minted attributes use a root prefix with no conflicting
|
|
59
|
+
* nested binding; a fresh prefix is declared when necessary.
|
|
59
60
|
* - Idempotent: a document that already has full coverage is returned as the
|
|
60
61
|
* original bytes, untouched (`alreadyComplete: true`). A body with
|
|
61
62
|
* paragraphs the scan did not see is refused, never reported complete.
|
|
@@ -63,6 +64,11 @@ import JSZip from "jszip";
|
|
|
63
64
|
* When normalization would rewrite the package, it fails unless the caller
|
|
64
65
|
* explicitly allows signature invalidation after warning the user.
|
|
65
66
|
*/
|
|
67
|
+
const ENSURE_PARA_IDS_REASONS = {
|
|
68
|
+
NORMALIZATION_FAILED: "normalization-failed",
|
|
69
|
+
NAMESPACE_INVALID: "namespace-invalid",
|
|
70
|
+
SIGNED_PACKAGE: "signed-package"
|
|
71
|
+
};
|
|
66
72
|
/** A malformed or unsupported package prevented paragraph-ID normalization. */
|
|
67
73
|
var EnsureParaIdsError = class extends TaggedError("EnsureParaIdsError") {};
|
|
68
74
|
const W14_NAMESPACE_URI = "http://schemas.microsoft.com/office/word/2010/wordml";
|
|
@@ -71,53 +77,15 @@ const MC_IGNORABLE_W14 = "w14";
|
|
|
71
77
|
const DIGITAL_SIGNATURE_PART_PREFIX = "_xmlsignatures/";
|
|
72
78
|
/** Word reads an all-zero `w14:paraId` as "no id assigned". */
|
|
73
79
|
const RESERVED_ZERO_ID_PATTERN = /^0*$/u;
|
|
74
|
-
const
|
|
80
|
+
const PACKAGE_RELATIONSHIP_URI = "http://schemas.openxmlformats.org/package/2006/relationships";
|
|
75
81
|
const TARGET_PART_PATTERNS = [
|
|
76
|
-
/^word\/document\.xml$/u,
|
|
77
82
|
/^word\/header\d*\.xml$/u,
|
|
78
83
|
/^word\/footer\d*\.xml$/u,
|
|
79
84
|
/^word\/footnotes\.xml$/u,
|
|
80
85
|
/^word\/endnotes\.xml$/u
|
|
81
86
|
];
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
open: "<!--",
|
|
85
|
-
close: "-->"
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
open: "<![CDATA[",
|
|
89
|
-
close: "]]>"
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
open: "<?",
|
|
93
|
-
close: "?>"
|
|
94
|
-
}
|
|
95
|
-
];
|
|
96
|
-
/**
|
|
97
|
-
* Every prefixed `paraId` counts toward uniqueness: the parser accepts the
|
|
98
|
-
* WordprocessingML fallback, comment parts link replies via `w15:paraId`, and
|
|
99
|
-
* a producer may spell any of them under a prefix of its own. Over-collecting
|
|
100
|
-
* only steers a minted id away from a value; it never keeps one.
|
|
101
|
-
*/
|
|
102
|
-
const ANY_PARA_ID_PATTERN = /\s[^\s=<>/"':]+:paraId=(?<quote>["'])(?<id>[\s\S]*?)\k<quote>/gu;
|
|
103
|
-
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
104
|
-
/** An attribute `localName` under any of `prefixes`, with `quote` and `value` groups. */
|
|
105
|
-
const prefixedAttributePattern = (prefixes, localName) => {
|
|
106
|
-
const names = prefixes.filter((prefix) => prefix !== "").map(escapeRegExp);
|
|
107
|
-
return names.length === 0 ? /(?!)/u : new RegExp(`\\s(?:${names.join("|")}):${localName}=(?<quote>["'])(?<value>[\\s\\S]*?)\\k<quote>`, "u");
|
|
108
|
-
};
|
|
109
|
-
const partSpelling = (xml, partPath) => {
|
|
110
|
-
const resolution = resolveWordprocessingPrefixes(xml);
|
|
111
|
-
if (resolution.type === "unsupported") throw createEnsureParaIdsError(`Cannot normalize paragraph ids in ${partPath}: ${resolution.reason}`);
|
|
112
|
-
const { prefixes } = resolution;
|
|
113
|
-
const attributePrefixes = [...prefixes.w14, ...prefixes.main];
|
|
114
|
-
return {
|
|
115
|
-
prefixes,
|
|
116
|
-
paraId: prefixedAttributePattern(attributePrefixes, "paraId"),
|
|
117
|
-
textId: prefixedAttributePattern(attributePrefixes, "textId"),
|
|
118
|
-
w14Prefix: (prefixes.w14Declared ? prefixes.w14[0] : void 0) ?? MC_IGNORABLE_W14
|
|
119
|
-
};
|
|
120
|
-
};
|
|
87
|
+
/** Reserve package ids even in parts whose content the pass leaves untouched. */
|
|
88
|
+
const ANY_PARA_ID_PATTERN = /\s[^\s=<>/"':]+:paraId\s*=\s*(?<quote>["'])(?<id>[\s\S]*?)\k<quote>/gu;
|
|
121
89
|
/**
|
|
122
90
|
* Stamp the edits into the part through the splice owner, which refuses a
|
|
123
91
|
* result that would leave a comment range with only one half. These edits
|
|
@@ -135,84 +103,98 @@ const applySplices = (xml, edits, partPath) => {
|
|
|
135
103
|
return patched;
|
|
136
104
|
};
|
|
137
105
|
const createEnsureParaIdsError = (message, cause) => new EnsureParaIdsError({
|
|
106
|
+
reason: ENSURE_PARA_IDS_REASONS.NORMALIZATION_FAILED,
|
|
138
107
|
message,
|
|
139
108
|
...cause === void 0 ? {} : { cause }
|
|
140
109
|
});
|
|
141
|
-
const skipOpaqueXmlRegion = (xml, start, partPath) => {
|
|
142
|
-
for (const { open, close } of OPAQUE_XML_REGIONS) {
|
|
143
|
-
if (!xml.startsWith(open, start)) continue;
|
|
144
|
-
const closeStart = xml.indexOf(close, start + open.length);
|
|
145
|
-
if (closeStart === -1) throw createEnsureParaIdsError(`Unterminated ${open} region in ${partPath}`);
|
|
146
|
-
return closeStart + close.length;
|
|
147
|
-
}
|
|
148
|
-
return null;
|
|
149
|
-
};
|
|
150
|
-
const attributeValueRange = (match, absoluteOffset, group) => {
|
|
151
|
-
const quote = match.groups["quote"];
|
|
152
|
-
const value = match.groups[group];
|
|
153
|
-
const start = absoluteOffset + match.index + match[0].indexOf(quote) + 1;
|
|
154
|
-
return {
|
|
155
|
-
start,
|
|
156
|
-
end: start + value.length
|
|
157
|
-
};
|
|
158
|
-
};
|
|
159
110
|
const collectExistingParaIds = (xml, into) => {
|
|
160
111
|
for (const match of xml.matchAll(ANY_PARA_ID_PATTERN)) {
|
|
161
112
|
const id = match.groups["id"];
|
|
162
113
|
if (id.length > 0) into.add(id.toUpperCase());
|
|
163
114
|
}
|
|
164
115
|
};
|
|
165
|
-
/**
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
116
|
+
/** Resolve every element and attribute in its own scope; retain source offsets for splices. */
|
|
117
|
+
const partSpelling = (xml, partPath) => {
|
|
118
|
+
let root;
|
|
119
|
+
let rootNameEnd = 0;
|
|
120
|
+
let ignorable;
|
|
121
|
+
const paragraphs = [];
|
|
122
|
+
const fallbacks = /* @__PURE__ */ new WeakSet();
|
|
123
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
124
|
+
if (scanStreamingXmlElements(xml, ({ element, attributeValueSpans: spans, nameEnd, parent }) => {
|
|
125
|
+
const unboundElement = (element.name ?? "").includes(":") && getNamespaceUri(element) === void 0;
|
|
126
|
+
const unboundAttribute = Object.keys(element.attributes ?? {}).some((attribute) => attribute.includes(":") && !attribute.startsWith("xmlns:") && resolveAttributeNamespaceUri(element, attribute) === void 0);
|
|
127
|
+
if (unboundElement || unboundAttribute) throw new EnsureParaIdsError({
|
|
128
|
+
reason: ENSURE_PARA_IDS_REASONS.NAMESPACE_INVALID,
|
|
129
|
+
message: `Undeclared namespace prefix in ${partPath}`
|
|
130
|
+
});
|
|
131
|
+
if (parent === void 0) {
|
|
132
|
+
if (root !== void 0) throw createEnsureParaIdsError(`Multiple roots in ${partPath}`);
|
|
133
|
+
root = element;
|
|
134
|
+
rootNameEnd = nameEnd;
|
|
180
135
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
136
|
+
for (const [name, value] of Object.entries(element.attributes ?? {})) {
|
|
137
|
+
if (name !== "xmlns" && !name.startsWith("xmlns:")) continue;
|
|
138
|
+
const prefix = name === "xmlns" ? "" : name.slice(6);
|
|
139
|
+
let uris = bindings.get(prefix);
|
|
140
|
+
if (uris === void 0) {
|
|
141
|
+
uris = /* @__PURE__ */ new Set();
|
|
142
|
+
bindings.set(prefix, uris);
|
|
143
|
+
}
|
|
144
|
+
uris.add(String(value));
|
|
187
145
|
}
|
|
188
|
-
const
|
|
189
|
-
if (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
146
|
+
const inFallback = parent !== void 0 && fallbacks.has(parent) || getLocalName(element.name ?? "") === "Fallback" && getNamespaceUri(element) === MC_NAMESPACE_URI;
|
|
147
|
+
if (inFallback) fallbacks.add(element);
|
|
148
|
+
let paraId;
|
|
149
|
+
let textId;
|
|
150
|
+
for (const [name, span] of spans) {
|
|
151
|
+
const namespace = resolveAttributeNamespaceUri(element, name);
|
|
152
|
+
const localName = getLocalName(name);
|
|
153
|
+
const value = String(element.attributes?.[name] ?? "");
|
|
154
|
+
if (element === root && namespace === MC_NAMESPACE_URI && localName === "Ignorable") ignorable = {
|
|
155
|
+
...span,
|
|
156
|
+
value
|
|
157
|
+
};
|
|
158
|
+
if (namespace !== W14_NAMESPACE_URI && !WORDPROCESSINGML_NAMESPACE_URIS.has(namespace ?? "")) continue;
|
|
159
|
+
if (localName === "paraId") {
|
|
160
|
+
if (paraId === void 0 || namespace === W14_NAMESPACE_URI) paraId = {
|
|
161
|
+
...span,
|
|
162
|
+
value
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (localName === "textId") textId = {
|
|
166
|
+
...span,
|
|
167
|
+
value
|
|
168
|
+
};
|
|
193
169
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
170
|
+
if (getLocalName(element.name ?? "") === "p" && WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(element) ?? "")) paragraphs.push({
|
|
171
|
+
nameEnd,
|
|
172
|
+
inFallback,
|
|
173
|
+
paraId,
|
|
174
|
+
textId
|
|
175
|
+
});
|
|
176
|
+
}).status === "unsupported" || root === void 0) throw createEnsureParaIdsError(`Malformed XML in ${partPath}`);
|
|
177
|
+
const rootElement = root;
|
|
178
|
+
const prefixFor = (uri, preferred) => {
|
|
179
|
+
for (const [prefix, boundUri] of rootElement.namespaceScope?.bindings ?? []) {
|
|
180
|
+
if (prefix === "" || boundUri !== uri) continue;
|
|
181
|
+
if (uri !== W14_NAMESPACE_URI || bindings.get(prefix)?.size === 1) return prefix;
|
|
198
182
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (id) into.add(id.toUpperCase());
|
|
215
|
-
}
|
|
183
|
+
let candidate = preferred;
|
|
184
|
+
for (let suffix = 1; bindings.has(candidate); suffix += 1) candidate = `${preferred}_${suffix}`;
|
|
185
|
+
return candidate;
|
|
186
|
+
};
|
|
187
|
+
return {
|
|
188
|
+
root,
|
|
189
|
+
rootNameEnd,
|
|
190
|
+
ignorable,
|
|
191
|
+
paragraphs,
|
|
192
|
+
w14Prefix: prefixFor(W14_NAMESPACE_URI, MC_IGNORABLE_W14),
|
|
193
|
+
mcPrefix: prefixFor(MC_NAMESPACE_URI, "mc")
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
const collectFallbackParaIds = (spelling, into) => {
|
|
197
|
+
for (const { inFallback, paraId } of spelling.paragraphs) if (inFallback && paraId?.value) into.add(paraId.value.toUpperCase());
|
|
216
198
|
};
|
|
217
199
|
/**
|
|
218
200
|
* Deterministic fresh id for the paragraph at `ordinal` in `partPath`,
|
|
@@ -225,35 +207,27 @@ const mintParaId = (context, partPath, ordinal) => {
|
|
|
225
207
|
context.taken.add(id);
|
|
226
208
|
return id;
|
|
227
209
|
};
|
|
228
|
-
|
|
229
|
-
* One forward scan over a part: every paragraph open tag outside
|
|
230
|
-
* `mc:Fallback` either keeps its paraId (valid, first occurrence) or gets a
|
|
231
|
-
* splice edit minting / replacing one. `seen` spans parts so the
|
|
232
|
-
* first-occurrence rule is document-wide across the scan order.
|
|
233
|
-
*/
|
|
234
|
-
const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
210
|
+
const scanPart = ({ partPath, spelling, context, seen }) => {
|
|
235
211
|
const edits = [];
|
|
236
212
|
const minted = [];
|
|
237
213
|
const w14 = spelling.w14Prefix;
|
|
238
214
|
let assigned = 0;
|
|
239
215
|
let deduplicated = 0;
|
|
240
216
|
let ordinal = 0;
|
|
241
|
-
for (const {
|
|
217
|
+
for (const { nameEnd, inFallback, paraId, textId } of spelling.paragraphs) {
|
|
242
218
|
if (inFallback) continue;
|
|
243
219
|
ordinal += 1;
|
|
244
|
-
|
|
245
|
-
const paraIdMatch = spelling.paraId.exec(openTag);
|
|
246
|
-
if (!paraIdMatch) {
|
|
220
|
+
if (paraId === void 0) {
|
|
247
221
|
const id = mintParaId(context, partPath, ordinal);
|
|
248
|
-
|
|
249
|
-
if (textIdMatch) {
|
|
222
|
+
if (textId !== void 0) {
|
|
250
223
|
edits.push({
|
|
251
224
|
start: nameEnd,
|
|
252
225
|
end: nameEnd,
|
|
253
226
|
text: ` ${w14}:paraId="${id}"`
|
|
254
227
|
});
|
|
255
228
|
edits.push({
|
|
256
|
-
|
|
229
|
+
start: textId.start,
|
|
230
|
+
end: textId.end,
|
|
257
231
|
text: id
|
|
258
232
|
});
|
|
259
233
|
} else edits.push({
|
|
@@ -266,7 +240,7 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
|
266
240
|
seen.add(id);
|
|
267
241
|
continue;
|
|
268
242
|
}
|
|
269
|
-
const value =
|
|
243
|
+
const value = paraId.value.toUpperCase();
|
|
270
244
|
const unassigned = RESERVED_ZERO_ID_PATTERN.test(value);
|
|
271
245
|
if (!unassigned && !seen.has(value)) {
|
|
272
246
|
seen.add(value);
|
|
@@ -274,12 +248,13 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
|
274
248
|
}
|
|
275
249
|
const id = mintParaId(context, partPath, ordinal);
|
|
276
250
|
edits.push({
|
|
277
|
-
|
|
251
|
+
start: paraId.start,
|
|
252
|
+
end: paraId.end,
|
|
278
253
|
text: id
|
|
279
254
|
});
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
255
|
+
if (textId !== void 0) edits.push({
|
|
256
|
+
start: textId.start,
|
|
257
|
+
end: textId.end,
|
|
283
258
|
text: id
|
|
284
259
|
});
|
|
285
260
|
else edits.push({
|
|
@@ -296,8 +271,7 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
|
296
271
|
edits,
|
|
297
272
|
minted,
|
|
298
273
|
assigned,
|
|
299
|
-
deduplicated
|
|
300
|
-
paragraphs: ordinal
|
|
274
|
+
deduplicated
|
|
301
275
|
};
|
|
302
276
|
};
|
|
303
277
|
/**
|
|
@@ -305,71 +279,42 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
|
305
279
|
* `xmlns:mc` and lists `w14` in `mc:Ignorable`, so consumers that predate the
|
|
306
280
|
* 2010 extensions skip the new attributes instead of rejecting the part.
|
|
307
281
|
*/
|
|
308
|
-
const ensureRootNamespaces = (
|
|
309
|
-
let pos = 0;
|
|
310
|
-
let rootStart = -1;
|
|
311
|
-
while (pos < xml.length) {
|
|
312
|
-
const lt = xml.indexOf("<", pos);
|
|
313
|
-
if (lt === -1) break;
|
|
314
|
-
const opaqueEnd = skipOpaqueXmlRegion(xml, lt, partPath);
|
|
315
|
-
if (opaqueEnd !== null) {
|
|
316
|
-
pos = opaqueEnd;
|
|
317
|
-
continue;
|
|
318
|
-
}
|
|
319
|
-
if (xml[lt + 1] === "!") {
|
|
320
|
-
const skipTo = xml.indexOf(">", lt);
|
|
321
|
-
if (skipTo === -1) throw createEnsureParaIdsError(`Unterminated prolog in ${partPath}`);
|
|
322
|
-
pos = skipTo + 1;
|
|
323
|
-
continue;
|
|
324
|
-
}
|
|
325
|
-
rootStart = lt;
|
|
326
|
-
break;
|
|
327
|
-
}
|
|
328
|
-
if (rootStart === -1) throw createEnsureParaIdsError(`No root element in ${partPath}`);
|
|
329
|
-
const rootEnd = xml.indexOf(">", rootStart);
|
|
330
|
-
if (rootEnd === -1) throw createEnsureParaIdsError(`Unterminated root element in ${partPath}`);
|
|
331
|
-
const rootTag = xml.slice(rootStart, rootEnd + 1);
|
|
332
|
-
if (!prefixes.mcDeclared && prefixes.mc.length === 0) throw createEnsureParaIdsError(`xmlns:mc has an unexpected namespace URI in ${partPath}`);
|
|
333
|
-
if (!prefixes.w14Declared && prefixes.w14.length === 0) throw createEnsureParaIdsError(`xmlns:w14 has an unexpected namespace URI in ${partPath}`);
|
|
282
|
+
const ensureRootNamespaces = ({ root, rootNameEnd, ignorable, w14Prefix, mcPrefix }) => {
|
|
334
283
|
const edits = [];
|
|
335
284
|
const declarations = [];
|
|
336
|
-
|
|
337
|
-
if (
|
|
338
|
-
if (
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const text = tokens.length === 0 ? w14Prefix : ` ${w14Prefix}`;
|
|
345
|
-
edits.push({
|
|
346
|
-
start: end,
|
|
347
|
-
end,
|
|
348
|
-
text
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
} else declarations.push(` ${mcPrefix}:Ignorable="${w14Prefix}"`);
|
|
352
|
-
if (declarations.length > 0) {
|
|
353
|
-
let nameEnd = rootStart + 1;
|
|
354
|
-
while (nameEnd < xml.length && !isXmlNameBoundary(xml[nameEnd])) nameEnd += 1;
|
|
355
|
-
edits.push({
|
|
356
|
-
start: nameEnd,
|
|
357
|
-
end: nameEnd,
|
|
358
|
-
text: declarations.join("")
|
|
285
|
+
if (resolveNamespaceUri(root.namespaceScope, mcPrefix) !== MC_NAMESPACE_URI) declarations.push(` xmlns:${mcPrefix}="${MC_NAMESPACE_URI}"`);
|
|
286
|
+
if (resolveNamespaceUri(root.namespaceScope, w14Prefix) !== W14_NAMESPACE_URI) declarations.push(` xmlns:${w14Prefix}="${W14_NAMESPACE_URI}"`);
|
|
287
|
+
if (ignorable !== void 0) {
|
|
288
|
+
const tokens = ignorable.value.split(/\s+/u).filter((token) => token.length > 0);
|
|
289
|
+
if (!tokens.includes(w14Prefix)) edits.push({
|
|
290
|
+
start: ignorable.end,
|
|
291
|
+
end: ignorable.end,
|
|
292
|
+
text: tokens.length === 0 ? w14Prefix : ` ${w14Prefix}`
|
|
359
293
|
});
|
|
360
|
-
}
|
|
294
|
+
} else declarations.push(` ${mcPrefix}:Ignorable="${w14Prefix}"`);
|
|
295
|
+
if (declarations.length > 0) edits.push({
|
|
296
|
+
start: rootNameEnd,
|
|
297
|
+
end: rootNameEnd,
|
|
298
|
+
text: declarations.join("")
|
|
299
|
+
});
|
|
361
300
|
return edits;
|
|
362
301
|
};
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const
|
|
372
|
-
if (
|
|
302
|
+
const mainDocumentPart = (rels, entries) => {
|
|
303
|
+
const root = parseXmlDocument(rels);
|
|
304
|
+
if (root === null || getLocalName(root.name ?? "") !== "Relationships" || getNamespaceUri(root) !== PACKAGE_RELATIONSHIP_URI) throw createEnsureParaIdsError("Malformed package relationships");
|
|
305
|
+
const documents = getChildElements(root).filter((element) => {
|
|
306
|
+
if (getLocalName(element.name ?? "") !== "Relationship" || getNamespaceUri(element) !== PACKAGE_RELATIONSHIP_URI) return false;
|
|
307
|
+
const type = getAttribute(element, null, "Type");
|
|
308
|
+
return [...OFFICE_RELATIONSHIP_NAMESPACE_URIS].some((uri) => type === `${uri}/officeDocument`);
|
|
309
|
+
});
|
|
310
|
+
const document = documents.at(0);
|
|
311
|
+
if (documents.length !== 1 || document === void 0 || getAttribute(document, null, "TargetMode") === "External") throw createEnsureParaIdsError("Package must have one internal officeDocument relationship");
|
|
312
|
+
const target = getAttribute(document, null, "Target");
|
|
313
|
+
const partPath = target === null ? void 0 : resolvePackageRelationshipTarget(target, "_rels/.rels");
|
|
314
|
+
if (partPath === void 0) throw createEnsureParaIdsError("Invalid officeDocument target");
|
|
315
|
+
const name = entries.find((entry) => entry.toLowerCase() === partPath);
|
|
316
|
+
if (name === void 0) throw createEnsureParaIdsError("officeDocument part not found");
|
|
317
|
+
return name;
|
|
373
318
|
};
|
|
374
319
|
/** OPC part names are case-insensitive; compare lowercased. */
|
|
375
320
|
const isTargetPart = (path) => TARGET_PART_PATTERNS.some((pattern) => pattern.test(path.toLowerCase()));
|
|
@@ -382,12 +327,11 @@ const hasDigitalSignatureParts = (zip) => Object.values(zip.files).some(({ dir,
|
|
|
382
327
|
*/
|
|
383
328
|
const ensureParaIdsInternal = async (docx, options) => {
|
|
384
329
|
const archive = await loadDocxArchive(docx);
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
const
|
|
390
|
-
if (documentPartName === void 0) throw createEnsureParaIdsError("word/document.xml not found: not a WordprocessingML package");
|
|
330
|
+
const relsPath = archive.entries.find((name) => name.toLowerCase() === "_rels/.rels");
|
|
331
|
+
const rels = relsPath === void 0 ? null : await archive.readEntryString(relsPath);
|
|
332
|
+
if (rels === null) throw createEnsureParaIdsError("Package relationships not found");
|
|
333
|
+
const documentPartName = mainDocumentPart(rels, archive.entries);
|
|
334
|
+
const xmlPartNames = archive.entries.filter((name) => name.toLowerCase().endsWith(".xml") || name === documentPartName);
|
|
391
335
|
const partTexts = /* @__PURE__ */ new Map();
|
|
392
336
|
for (const name of xmlPartNames) {
|
|
393
337
|
const text = await archive.readEntryString(name);
|
|
@@ -395,17 +339,19 @@ const ensureParaIdsInternal = async (docx, options) => {
|
|
|
395
339
|
}
|
|
396
340
|
const taken = /* @__PURE__ */ new Set();
|
|
397
341
|
for (const text of partTexts.values()) collectExistingParaIds(text, taken);
|
|
342
|
+
const documentXml = partTexts.get(documentPartName);
|
|
343
|
+
if (documentXml === void 0) throw createEnsureParaIdsError("officeDocument part is unreadable");
|
|
398
344
|
const context = {
|
|
399
345
|
taken,
|
|
400
|
-
docKey: deterministicHexId(
|
|
346
|
+
docKey: deterministicHexId(documentXml)
|
|
401
347
|
};
|
|
402
348
|
const targetParts = [documentPartName, ...xmlPartNames.filter((name) => name !== documentPartName && isTargetPart(name)).sort((a, b) => a.localeCompare(b))];
|
|
403
349
|
const spellings = /* @__PURE__ */ new Map();
|
|
404
350
|
const seen = /* @__PURE__ */ new Set();
|
|
405
|
-
for (const [partPath, xml] of partTexts) if (isTargetPart(partPath)) {
|
|
351
|
+
for (const [partPath, xml] of partTexts) if (partPath === documentPartName || isTargetPart(partPath)) {
|
|
406
352
|
const spelling = partSpelling(xml, partPath);
|
|
407
353
|
spellings.set(partPath, spelling);
|
|
408
|
-
collectFallbackParaIds(
|
|
354
|
+
collectFallbackParaIds(spelling, seen);
|
|
409
355
|
} else collectExistingParaIds(xml, seen);
|
|
410
356
|
const updates = /* @__PURE__ */ new Map();
|
|
411
357
|
const mintedParaIds = [];
|
|
@@ -414,13 +360,17 @@ const ensureParaIdsInternal = async (docx, options) => {
|
|
|
414
360
|
for (const partPath of targetParts) {
|
|
415
361
|
const xml = partTexts.get(partPath);
|
|
416
362
|
const spelling = spellings.get(partPath);
|
|
417
|
-
const scan = scanPart(
|
|
418
|
-
|
|
363
|
+
const scan = scanPart({
|
|
364
|
+
partPath,
|
|
365
|
+
spelling,
|
|
366
|
+
context,
|
|
367
|
+
seen
|
|
368
|
+
});
|
|
419
369
|
if (scan.edits.length === 0) continue;
|
|
420
370
|
assigned += scan.assigned;
|
|
421
371
|
deduplicated += scan.deduplicated;
|
|
422
372
|
for (const id of scan.minted) mintedParaIds.push(id);
|
|
423
|
-
updates.set(partPath, applySplices(xml, [...scan.edits, ...ensureRootNamespaces(
|
|
373
|
+
updates.set(partPath, applySplices(xml, [...scan.edits, ...ensureRootNamespaces(spelling)], partPath));
|
|
424
374
|
}
|
|
425
375
|
if (updates.size === 0) return {
|
|
426
376
|
docx: toUint8Array(docx),
|
|
@@ -430,14 +380,22 @@ const ensureParaIdsInternal = async (docx, options) => {
|
|
|
430
380
|
mintedParaIds: []
|
|
431
381
|
};
|
|
432
382
|
const zip = await JSZip.loadAsync(docx);
|
|
433
|
-
if (hasDigitalSignatureParts(zip) && options.allowSignedPackageMutation !== true) throw
|
|
383
|
+
if (hasDigitalSignatureParts(zip) && options.allowSignedPackageMutation !== true) throw new EnsureParaIdsError({
|
|
384
|
+
reason: ENSURE_PARA_IDS_REASONS.SIGNED_PACKAGE,
|
|
385
|
+
message: "Refusing to normalize a digitally signed package because rewriting OOXML invalidates its signatures. Warn the user and pass allowSignedPackageMutation only if invalidation is acceptable."
|
|
386
|
+
});
|
|
434
387
|
for (const [partPath, content] of updates) {
|
|
435
388
|
const sourceEntry = zip.file(partPath);
|
|
436
389
|
if (sourceEntry === null) throw createEnsureParaIdsError(`Package part disappeared during normalization: ${partPath}`);
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
390
|
+
writeZipPart({
|
|
391
|
+
zip,
|
|
392
|
+
path: partPath,
|
|
393
|
+
data: content,
|
|
394
|
+
options: {
|
|
395
|
+
compression: "DEFLATE",
|
|
396
|
+
compressionOptions: { level: 6 },
|
|
397
|
+
date: sourceEntry.date
|
|
398
|
+
}
|
|
441
399
|
});
|
|
442
400
|
}
|
|
443
401
|
return {
|
|
@@ -466,4 +424,4 @@ const ensureParaIds = async (docx, options = {}) => {
|
|
|
466
424
|
}
|
|
467
425
|
};
|
|
468
426
|
//#endregion
|
|
469
|
-
export { EnsureParaIdsError, ensureParaIds };
|
|
427
|
+
export { ENSURE_PARA_IDS_REASONS, EnsureParaIdsError, ensureParaIds };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
//#region src/docx/foldedListNumberFields.d.ts
|
|
3
|
+
/** Whether `content` is markup that may stand between a field and the tab that follows it. */
|
|
4
|
+
declare const isListNumberGapMarker: (content: document_d_exports.ParagraphContent) => boolean;
|
|
5
|
+
/** Whether `content` is a `LISTNUM` field, by its parsed type or its instruction. */
|
|
6
|
+
declare const isListNumberField: (content: document_d_exports.ParagraphContent) => content is document_d_exports.ComplexField;
|
|
7
|
+
/** A run that holds one tab and nothing else. */
|
|
8
|
+
declare const isTabOnlyRun: (content: document_d_exports.ParagraphContent) => content is document_d_exports.Run;
|
|
9
|
+
/** Whether `value` is what a capture stands for: a complex field, or a run. */
|
|
10
|
+
declare const isFoldedListNumber: (value: unknown) => value is document_d_exports.FoldedListNumber;
|
|
11
|
+
/** What `content` stands for when it is a capture the fold left, else nothing. */
|
|
12
|
+
declare const foldedListNumberOf: (content: document_d_exports.ParagraphContent) => document_d_exports.FoldedListNumber | undefined;
|
|
13
|
+
/** Whether `content` is a capture the fold left in place of a field or its tab. */
|
|
14
|
+
declare const isFoldedListNumberCapture: (content: document_d_exports.ParagraphContent) => boolean;
|
|
15
|
+
type ListNumberFieldFold = {
|
|
16
|
+
/** The content, each folded field and its tab replaced by a capture. */
|
|
17
|
+
content: document_d_exports.ParagraphContent[];
|
|
18
|
+
/** The cached display of each folded field that has one, in source order. */
|
|
19
|
+
cached: string[];
|
|
20
|
+
/** Every `LISTNUM` field the content holds, folded or not. */
|
|
21
|
+
fieldCount: number;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Replace the `LISTNUM` fields that open a paragraph, and the tab after each,
|
|
25
|
+
* by captures of the markup they were read from.
|
|
26
|
+
*
|
|
27
|
+
* Only what stands ahead of the first thing the line shows is folded: a field
|
|
28
|
+
* after text is on the line where the text is. `sourceMarkupOf` answers the
|
|
29
|
+
* markup; a field it has none for is left as the field it is, and ends the
|
|
30
|
+
* fold. Bookmark and comment markers between a field and its tab stay where
|
|
31
|
+
* they are, and the tab is still the field's when only they separate the two.
|
|
32
|
+
*/
|
|
33
|
+
declare const foldListNumberFields: (content: readonly document_d_exports.ParagraphContent[], sourceMarkupOf: (item: document_d_exports.ComplexField | document_d_exports.Run) => string | undefined) => ListNumberFieldFold;
|
|
34
|
+
/** One item of a paragraph, as far as the fold needs to know it. */
|
|
35
|
+
type ListNumberFoldItem =
|
|
36
|
+
/** A capture of a field, with the text its cached result shows. */
|
|
37
|
+
{
|
|
38
|
+
kind: "field";
|
|
39
|
+
cached: string;
|
|
40
|
+
} |
|
|
41
|
+
/** A capture of the tab after a field. */
|
|
42
|
+
{
|
|
43
|
+
kind: "tab";
|
|
44
|
+
} |
|
|
45
|
+
/** Markup that shows nothing. */
|
|
46
|
+
{
|
|
47
|
+
kind: "hidden";
|
|
48
|
+
} |
|
|
49
|
+
/** Anything the line shows. */
|
|
50
|
+
{
|
|
51
|
+
kind: "shown";
|
|
52
|
+
};
|
|
53
|
+
type ListNumberFoldPlan = {
|
|
54
|
+
/** The items' indices in the order they are to stand in. */
|
|
55
|
+
order: number[];
|
|
56
|
+
/** Indices of the captures that stay hidden; every other capture goes on the line. */
|
|
57
|
+
hidden: ReadonlySet<number>;
|
|
58
|
+
/** What the marker shows after its own text, or nothing. */
|
|
59
|
+
suffix: string | undefined;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Decide which captures of a paragraph stay hidden, and what its marker shows.
|
|
63
|
+
*
|
|
64
|
+
* `markerShowsFields` says whether the paragraph's marker shows folded fields
|
|
65
|
+
* at all. When it does not, no capture is hidden.
|
|
66
|
+
*
|
|
67
|
+
* When it does, the captures it shows are the ones that open the paragraph:
|
|
68
|
+
* those ahead of the first thing the line shows, each field followed at most
|
|
69
|
+
* by its own tab. Text put in front of them was put in front of the body, not
|
|
70
|
+
* of the marker, so if the first capture no longer opens the paragraph, it
|
|
71
|
+
* and the captures that stand with it go back to the start. Every capture
|
|
72
|
+
* further in is on the line.
|
|
73
|
+
*/
|
|
74
|
+
declare const planListNumberFold: (items: readonly ListNumberFoldItem[], markerShowsFields: boolean) => ListNumberFoldPlan;
|
|
75
|
+
/** The item a capture stands for, which shows on the line; any other item is itself. */
|
|
76
|
+
declare const unfoldedListNumberContent: (content: document_d_exports.ParagraphContent) => document_d_exports.ParagraphContent;
|
|
77
|
+
/**
|
|
78
|
+
* Bring `paragraph` to the one form the fold allows: the captures its marker
|
|
79
|
+
* shows stand at its start, every other capture is the field or the tab it
|
|
80
|
+
* stood for, and the marker shows exactly the fields still hidden behind it.
|
|
81
|
+
* A capture under a tracked change is never hidden.
|
|
82
|
+
*/
|
|
83
|
+
declare const normalizeFoldedListNumbers: (paragraph: document_d_exports.Paragraph) => void;
|
|
84
|
+
//#endregion
|
|
85
|
+
export { ListNumberFieldFold, ListNumberFoldItem, ListNumberFoldPlan, foldListNumberFields, foldedListNumberOf, isFoldedListNumber, isFoldedListNumberCapture, isListNumberField, isListNumberGapMarker, isTabOnlyRun, normalizeFoldedListNumbers, planListNumberFold, unfoldedListNumberContent };
|