@stll/folio-core 0.54.1 → 0.55.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.d.ts +1 -1
- package/dist/ai-edits/apply.js +40 -32
- package/dist/ai-edits/headless.d.ts +1 -2
- package/dist/ai-edits/headless.js +13 -27
- package/dist/ai-edits/read.d.ts +1 -0
- package/dist/ai-edits/read.js +7 -2
- package/dist/ai-edits/table-mutation-plan.d.ts +2 -1
- package/dist/ai-edits/table-mutation-plan.js +39 -1
- package/dist/ai-suggestions/apply.d.ts +1 -1
- package/dist/ai-suggestions/apply.js +3 -2
- package/dist/compare/compare.js +2 -1
- package/dist/compare/content-types.d.ts +3 -15
- package/dist/controller/canonicalComposition.d.ts +36 -0
- package/dist/controller/canonicalComposition.js +168 -0
- package/dist/controller/canonicalInput.d.ts +45 -0
- package/dist/controller/canonicalInput.js +338 -0
- package/dist/controller/canonicalOperations.d.ts +22 -0
- package/dist/controller/canonicalOperations.js +85 -0
- package/dist/controller/canonicalReview.d.ts +12 -0
- package/dist/controller/canonicalReview.js +19 -0
- package/dist/controller/canonicalSession.d.ts +149 -0
- package/dist/controller/canonicalSession.js +1038 -0
- package/dist/controller/canonicalStoryEditor.d.ts +34 -0
- package/dist/controller/canonicalStoryEditor.js +82 -0
- package/dist/controller/canonicalStructure.d.ts +16 -0
- package/dist/controller/canonicalStructure.js +269 -0
- package/dist/controller/folioEditor.d.ts +1 -1
- package/dist/controller/folioEditor.js +9 -0
- package/dist/controller/headerFooterEditorManager.d.ts +4 -0
- package/dist/controller/headerFooterEditorManager.js +71 -3
- package/dist/controller/hiddenEditorApi.d.ts +35 -0
- package/dist/controller/hiddenEditorApi.js +25 -8
- package/dist/controller/hiddenEditorManager.d.ts +11 -2
- package/dist/controller/hiddenEditorManager.js +456 -30
- package/dist/controller/layoutPipeline.d.ts +1 -1
- package/dist/controller/layoutPipeline.js +2 -0
- package/dist/controller/noteEditorManager.d.ts +4 -0
- package/dist/controller/noteEditorManager.js +113 -15
- package/dist/docx/attributeRemainder.js +1 -0
- package/dist/docx/bookmarkIds.d.ts +11 -0
- package/dist/docx/bookmarkIds.js +33 -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 +41 -0
- package/dist/docx/contentTypeRegistry.d.ts +8 -0
- package/dist/docx/contentTypeRegistry.js +64 -0
- package/dist/docx/documentParser.d.ts +14 -1
- package/dist/docx/documentParser.js +17 -4
- package/dist/docx/drawingIdNormalization.js +23 -8
- package/dist/docx/ensureParaIds.d.ts +12 -1
- package/dist/docx/ensureParaIds.js +173 -213
- package/dist/docx/foldedListNumberFields.d.ts +85 -0
- package/dist/docx/foldedListNumberFields.js +267 -0
- package/dist/docx/headerFooterVerbatim.d.ts +14 -1
- package/dist/docx/headerFooterVerbatim.js +95 -2
- package/dist/docx/listNumberingInstances.js +11 -9
- package/dist/docx/noteIds.d.ts +9 -0
- package/dist/docx/noteIds.js +17 -0
- package/dist/docx/noteReferenceMark.d.ts +3 -1
- package/dist/docx/noteReferenceMark.js +15 -14
- package/dist/docx/numberingIds.d.ts +14 -0
- package/dist/docx/numberingIds.js +16 -0
- package/dist/docx/numberingParser.d.ts +3 -1
- package/dist/docx/numberingParser.js +17 -3
- package/dist/docx/numericIdAllocator.d.ts +12 -0
- package/dist/docx/numericIdAllocator.js +33 -0
- package/dist/docx/numericIdNormalization.d.ts +16 -0
- package/dist/docx/numericIdNormalization.js +174 -0
- package/dist/docx/packageParts.d.ts +3 -1
- package/dist/docx/packageParts.js +65 -16
- 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 +76 -22
- package/dist/docx/paragraphTextBoxEnrichment.js +2 -1
- package/dist/docx/parser.js +19 -6
- package/dist/docx/rasterMime.d.ts +64 -0
- package/dist/docx/rasterMime.js +116 -0
- package/dist/docx/replyToComment.js +13 -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 +7 -3
- package/dist/docx/rezip.js +121 -52
- 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 +11 -0
- package/dist/docx/saveDiagnostics.js +0 -0
- package/dist/docx/selectiveSave.d.ts +2 -1
- package/dist/docx/selectiveSave.js +14 -1
- package/dist/docx/selectiveXmlPatch.d.ts +16 -8
- package/dist/docx/selectiveXmlPatch.js +74 -2
- 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 +6 -3
- package/dist/docx/serializer/runSerializer.js +8 -5
- package/dist/docx/serializer/trackedChangeAttributes.js +7 -1
- package/dist/docx/server/build.js +14 -3
- package/dist/docx/server/createBilingualDocument.js +10 -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 +11 -0
- package/dist/docx/storyBlockReplay.js +367 -0
- package/dist/docx/streamingXmlParser.d.ts +31 -1
- package/dist/docx/streamingXmlParser.js +136 -21
- package/dist/docx/structuralXmlPatch.js +8 -1
- package/dist/docx/unzip.d.ts +3 -1
- package/dist/docx/unzip.js +36 -16
- 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/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/fixedTableColumnWidths.d.ts +6 -0
- package/dist/layout-bridge/convert/fixedTableColumnWidths.js +23 -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/tableConversion.js +4 -1
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +2 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +2 -2
- package/dist/layout-painter/index.js +2 -5
- package/dist/managers/DocumentLoaderManager.d.ts +1 -0
- package/dist/managers/DocumentLoaderManager.js +8 -1
- package/dist/markdown/renderBlock.js +15 -1
- package/dist/markdown/renderParagraph.js +9 -13
- package/dist/markdown/renderRuns.js +3 -2
- package/dist/markdown/renderTable.js +2 -0
- package/dist/markdown/types.d.ts +3 -9
- package/dist/paged-layout/editorScrollRoot.d.ts +15 -0
- package/dist/paged-layout/editorScrollRoot.js +72 -0
- package/dist/paged-layout/scrollToPmPosition.js +8 -22
- 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 +97 -0
- package/dist/prosemirror/canonicalCommands.js +28 -0
- package/dist/prosemirror/commands/clearParagraphIndent.d.ts +6 -0
- package/dist/prosemirror/commands/clearParagraphIndent.js +25 -0
- package/dist/prosemirror/commands/pageBreak.js +15 -1
- package/dist/prosemirror/commentIdAllocator.d.ts +15 -22
- package/dist/prosemirror/commentIdAllocator.js +50 -25
- package/dist/prosemirror/commentReferenceAttrs.d.ts +2 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +91 -19
- package/dist/prosemirror/conversion/listRenderingDefinition.d.ts +6 -0
- package/dist/prosemirror/conversion/listRenderingDefinition.js +97 -0
- package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -3
- package/dist/prosemirror/conversion/toProseDoc.js +51 -11
- 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/HistoryExtension.js +97 -5
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +53 -20
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +22 -28
- 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/TextColorExtension.js +3 -2
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
- package/dist/prosemirror/extensions/marks/markUtils.js +70 -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/extensions/nodes/ShapeExtension.js +8 -6
- 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/plugins/revisionIds.d.ts +23 -19
- package/dist/prosemirror/plugins/revisionIds.js +110 -29
- package/dist/prosemirror/plugins/suggestionMode.d.ts +2 -2
- package/dist/prosemirror/plugins/suggestionMode.js +246 -42
- 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/storyListNumbering.js +11 -9
- package/dist/prosemirror/textBoxAnchorAttrs.d.ts +2 -1
- package/dist/prosemirror/textInput.d.ts +6 -1
- package/dist/prosemirror/textInput.js +14 -3
- 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/utils/visualLineNavigation.js +6 -8
- package/dist/prosemirror/validation.js +4 -0
- package/dist/types/content.d.ts +2 -2
- package/dist/utils/createDocument.js +2 -2
- package/dist/utils/mergeDocumentContent.d.ts +2 -2
- package/dist/utils/mergeDocumentContent.js +11 -8
- package/package.json +14 -2
|
@@ -1,8 +1,10 @@
|
|
|
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";
|
|
7
9
|
import JSZip from "jszip";
|
|
8
10
|
//#region src/docx/ensureParaIds.ts
|
|
@@ -27,7 +29,7 @@ import JSZip from "jszip";
|
|
|
27
29
|
* so documents carrying features folio's parser does not model come back
|
|
28
30
|
* with those features byte-identical. Contract:
|
|
29
31
|
*
|
|
30
|
-
* - Parts covered:
|
|
32
|
+
* - Parts covered: the officeDocument relationship target, `word/header*.xml`, `word/footer*.xml`,
|
|
31
33
|
* `word/footnotes.xml`, `word/endnotes.xml`. Paragraphs nested in table
|
|
32
34
|
* cells and in `mc:Choice` text boxes are plain `<w:p>` elements inside
|
|
33
35
|
* those parts and are covered by the same scan. The comments part mints its
|
|
@@ -50,12 +52,10 @@ import JSZip from "jszip";
|
|
|
50
52
|
* declarations and a `mc:Ignorable` listing `w14` when missing — non-Word
|
|
51
53
|
* producers declare neither, and absent `mc:Ignorable` handling is what
|
|
52
54
|
* 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}.
|
|
55
|
+
* - Prefixes are aliases: elements and attributes resolve against their
|
|
56
|
+
* in-scope namespace URI, including Strict and Transitional profiles and
|
|
57
|
+
* nested rebinding. Minted attributes use a root prefix with no conflicting
|
|
58
|
+
* nested binding; a fresh prefix is declared when necessary.
|
|
59
59
|
* - Idempotent: a document that already has full coverage is returned as the
|
|
60
60
|
* original bytes, untouched (`alreadyComplete: true`). A body with
|
|
61
61
|
* paragraphs the scan did not see is refused, never reported complete.
|
|
@@ -63,6 +63,11 @@ import JSZip from "jszip";
|
|
|
63
63
|
* When normalization would rewrite the package, it fails unless the caller
|
|
64
64
|
* explicitly allows signature invalidation after warning the user.
|
|
65
65
|
*/
|
|
66
|
+
const ENSURE_PARA_IDS_REASONS = {
|
|
67
|
+
NORMALIZATION_FAILED: "normalization-failed",
|
|
68
|
+
NAMESPACE_INVALID: "namespace-invalid",
|
|
69
|
+
SIGNED_PACKAGE: "signed-package"
|
|
70
|
+
};
|
|
66
71
|
/** A malformed or unsupported package prevented paragraph-ID normalization. */
|
|
67
72
|
var EnsureParaIdsError = class extends TaggedError("EnsureParaIdsError") {};
|
|
68
73
|
const W14_NAMESPACE_URI = "http://schemas.microsoft.com/office/word/2010/wordml";
|
|
@@ -71,53 +76,15 @@ const MC_IGNORABLE_W14 = "w14";
|
|
|
71
76
|
const DIGITAL_SIGNATURE_PART_PREFIX = "_xmlsignatures/";
|
|
72
77
|
/** Word reads an all-zero `w14:paraId` as "no id assigned". */
|
|
73
78
|
const RESERVED_ZERO_ID_PATTERN = /^0*$/u;
|
|
74
|
-
const
|
|
79
|
+
const PACKAGE_RELATIONSHIP_URI = "http://schemas.openxmlformats.org/package/2006/relationships";
|
|
75
80
|
const TARGET_PART_PATTERNS = [
|
|
76
|
-
/^word\/document\.xml$/u,
|
|
77
81
|
/^word\/header\d*\.xml$/u,
|
|
78
82
|
/^word\/footer\d*\.xml$/u,
|
|
79
83
|
/^word\/footnotes\.xml$/u,
|
|
80
84
|
/^word\/endnotes\.xml$/u
|
|
81
85
|
];
|
|
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
|
-
};
|
|
86
|
+
/** Reserve package ids even in parts whose content the pass leaves untouched. */
|
|
87
|
+
const ANY_PARA_ID_PATTERN = /\s[^\s=<>/"':]+:paraId\s*=\s*(?<quote>["'])(?<id>[\s\S]*?)\k<quote>/gu;
|
|
121
88
|
/**
|
|
122
89
|
* Stamp the edits into the part through the splice owner, which refuses a
|
|
123
90
|
* result that would leave a comment range with only one half. These edits
|
|
@@ -135,84 +102,98 @@ const applySplices = (xml, edits, partPath) => {
|
|
|
135
102
|
return patched;
|
|
136
103
|
};
|
|
137
104
|
const createEnsureParaIdsError = (message, cause) => new EnsureParaIdsError({
|
|
105
|
+
reason: ENSURE_PARA_IDS_REASONS.NORMALIZATION_FAILED,
|
|
138
106
|
message,
|
|
139
107
|
...cause === void 0 ? {} : { cause }
|
|
140
108
|
});
|
|
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
109
|
const collectExistingParaIds = (xml, into) => {
|
|
160
110
|
for (const match of xml.matchAll(ANY_PARA_ID_PATTERN)) {
|
|
161
111
|
const id = match.groups["id"];
|
|
162
112
|
if (id.length > 0) into.add(id.toUpperCase());
|
|
163
113
|
}
|
|
164
114
|
};
|
|
165
|
-
/**
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
115
|
+
/** Resolve every element and attribute in its own scope; retain source offsets for splices. */
|
|
116
|
+
const partSpelling = (xml, partPath) => {
|
|
117
|
+
let root;
|
|
118
|
+
let rootNameEnd = 0;
|
|
119
|
+
let ignorable;
|
|
120
|
+
const paragraphs = [];
|
|
121
|
+
const fallbacks = /* @__PURE__ */ new WeakSet();
|
|
122
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
123
|
+
if (scanStreamingXmlElements(xml, ({ element, attributeValueSpans: spans, nameEnd, parent }) => {
|
|
124
|
+
const unboundElement = (element.name ?? "").includes(":") && getNamespaceUri(element) === void 0;
|
|
125
|
+
const unboundAttribute = Object.keys(element.attributes ?? {}).some((attribute) => attribute.includes(":") && !attribute.startsWith("xmlns:") && resolveAttributeNamespaceUri(element, attribute) === void 0);
|
|
126
|
+
if (unboundElement || unboundAttribute) throw new EnsureParaIdsError({
|
|
127
|
+
reason: ENSURE_PARA_IDS_REASONS.NAMESPACE_INVALID,
|
|
128
|
+
message: `Undeclared namespace prefix in ${partPath}`
|
|
129
|
+
});
|
|
130
|
+
if (parent === void 0) {
|
|
131
|
+
if (root !== void 0) throw createEnsureParaIdsError(`Multiple roots in ${partPath}`);
|
|
132
|
+
root = element;
|
|
133
|
+
rootNameEnd = nameEnd;
|
|
180
134
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
135
|
+
for (const [name, value] of Object.entries(element.attributes ?? {})) {
|
|
136
|
+
if (name !== "xmlns" && !name.startsWith("xmlns:")) continue;
|
|
137
|
+
const prefix = name === "xmlns" ? "" : name.slice(6);
|
|
138
|
+
let uris = bindings.get(prefix);
|
|
139
|
+
if (uris === void 0) {
|
|
140
|
+
uris = /* @__PURE__ */ new Set();
|
|
141
|
+
bindings.set(prefix, uris);
|
|
142
|
+
}
|
|
143
|
+
uris.add(String(value));
|
|
187
144
|
}
|
|
188
|
-
const
|
|
189
|
-
if (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
145
|
+
const inFallback = parent !== void 0 && fallbacks.has(parent) || getLocalName(element.name ?? "") === "Fallback" && getNamespaceUri(element) === MC_NAMESPACE_URI;
|
|
146
|
+
if (inFallback) fallbacks.add(element);
|
|
147
|
+
let paraId;
|
|
148
|
+
let textId;
|
|
149
|
+
for (const [name, span] of spans) {
|
|
150
|
+
const namespace = resolveAttributeNamespaceUri(element, name);
|
|
151
|
+
const localName = getLocalName(name);
|
|
152
|
+
const value = String(element.attributes?.[name] ?? "");
|
|
153
|
+
if (element === root && namespace === MC_NAMESPACE_URI && localName === "Ignorable") ignorable = {
|
|
154
|
+
...span,
|
|
155
|
+
value
|
|
156
|
+
};
|
|
157
|
+
if (namespace !== W14_NAMESPACE_URI && !WORDPROCESSINGML_NAMESPACE_URIS.has(namespace ?? "")) continue;
|
|
158
|
+
if (localName === "paraId") {
|
|
159
|
+
if (paraId === void 0 || namespace === W14_NAMESPACE_URI) paraId = {
|
|
160
|
+
...span,
|
|
161
|
+
value
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (localName === "textId") textId = {
|
|
165
|
+
...span,
|
|
166
|
+
value
|
|
167
|
+
};
|
|
193
168
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
169
|
+
if (getLocalName(element.name ?? "") === "p" && WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(element) ?? "")) paragraphs.push({
|
|
170
|
+
nameEnd,
|
|
171
|
+
inFallback,
|
|
172
|
+
paraId,
|
|
173
|
+
textId
|
|
174
|
+
});
|
|
175
|
+
}).status === "unsupported" || root === void 0) throw createEnsureParaIdsError(`Malformed XML in ${partPath}`);
|
|
176
|
+
const rootElement = root;
|
|
177
|
+
const prefixFor = (uri, preferred) => {
|
|
178
|
+
for (const [prefix, boundUri] of rootElement.namespaceScope?.bindings ?? []) {
|
|
179
|
+
if (prefix === "" || boundUri !== uri) continue;
|
|
180
|
+
if (uri !== W14_NAMESPACE_URI || bindings.get(prefix)?.size === 1) return prefix;
|
|
198
181
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (id) into.add(id.toUpperCase());
|
|
215
|
-
}
|
|
182
|
+
let candidate = preferred;
|
|
183
|
+
for (let suffix = 1; bindings.has(candidate); suffix += 1) candidate = `${preferred}_${suffix}`;
|
|
184
|
+
return candidate;
|
|
185
|
+
};
|
|
186
|
+
return {
|
|
187
|
+
root,
|
|
188
|
+
rootNameEnd,
|
|
189
|
+
ignorable,
|
|
190
|
+
paragraphs,
|
|
191
|
+
w14Prefix: prefixFor(W14_NAMESPACE_URI, MC_IGNORABLE_W14),
|
|
192
|
+
mcPrefix: prefixFor(MC_NAMESPACE_URI, "mc")
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
const collectFallbackParaIds = (spelling, into) => {
|
|
196
|
+
for (const { inFallback, paraId } of spelling.paragraphs) if (inFallback && paraId?.value) into.add(paraId.value.toUpperCase());
|
|
216
197
|
};
|
|
217
198
|
/**
|
|
218
199
|
* Deterministic fresh id for the paragraph at `ordinal` in `partPath`,
|
|
@@ -225,34 +206,27 @@ const mintParaId = (context, partPath, ordinal) => {
|
|
|
225
206
|
context.taken.add(id);
|
|
226
207
|
return id;
|
|
227
208
|
};
|
|
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) => {
|
|
209
|
+
const scanPart = ({ partPath, spelling, context, seen }) => {
|
|
235
210
|
const edits = [];
|
|
211
|
+
const minted = [];
|
|
236
212
|
const w14 = spelling.w14Prefix;
|
|
237
213
|
let assigned = 0;
|
|
238
214
|
let deduplicated = 0;
|
|
239
215
|
let ordinal = 0;
|
|
240
|
-
for (const {
|
|
216
|
+
for (const { nameEnd, inFallback, paraId, textId } of spelling.paragraphs) {
|
|
241
217
|
if (inFallback) continue;
|
|
242
218
|
ordinal += 1;
|
|
243
|
-
|
|
244
|
-
const paraIdMatch = spelling.paraId.exec(openTag);
|
|
245
|
-
if (!paraIdMatch) {
|
|
219
|
+
if (paraId === void 0) {
|
|
246
220
|
const id = mintParaId(context, partPath, ordinal);
|
|
247
|
-
|
|
248
|
-
if (textIdMatch) {
|
|
221
|
+
if (textId !== void 0) {
|
|
249
222
|
edits.push({
|
|
250
223
|
start: nameEnd,
|
|
251
224
|
end: nameEnd,
|
|
252
225
|
text: ` ${w14}:paraId="${id}"`
|
|
253
226
|
});
|
|
254
227
|
edits.push({
|
|
255
|
-
|
|
228
|
+
start: textId.start,
|
|
229
|
+
end: textId.end,
|
|
256
230
|
text: id
|
|
257
231
|
});
|
|
258
232
|
} else edits.push({
|
|
@@ -261,10 +235,11 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
|
261
235
|
text: ` ${w14}:paraId="${id}" ${w14}:textId="${id}"`
|
|
262
236
|
});
|
|
263
237
|
assigned += 1;
|
|
238
|
+
minted.push(id);
|
|
264
239
|
seen.add(id);
|
|
265
240
|
continue;
|
|
266
241
|
}
|
|
267
|
-
const value =
|
|
242
|
+
const value = paraId.value.toUpperCase();
|
|
268
243
|
const unassigned = RESERVED_ZERO_ID_PATTERN.test(value);
|
|
269
244
|
if (!unassigned && !seen.has(value)) {
|
|
270
245
|
seen.add(value);
|
|
@@ -272,12 +247,13 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
|
272
247
|
}
|
|
273
248
|
const id = mintParaId(context, partPath, ordinal);
|
|
274
249
|
edits.push({
|
|
275
|
-
|
|
250
|
+
start: paraId.start,
|
|
251
|
+
end: paraId.end,
|
|
276
252
|
text: id
|
|
277
253
|
});
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
254
|
+
if (textId !== void 0) edits.push({
|
|
255
|
+
start: textId.start,
|
|
256
|
+
end: textId.end,
|
|
281
257
|
text: id
|
|
282
258
|
});
|
|
283
259
|
else edits.push({
|
|
@@ -287,13 +263,14 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
|
287
263
|
});
|
|
288
264
|
if (unassigned) assigned += 1;
|
|
289
265
|
else deduplicated += 1;
|
|
266
|
+
minted.push(id);
|
|
290
267
|
seen.add(id);
|
|
291
268
|
}
|
|
292
269
|
return {
|
|
293
270
|
edits,
|
|
271
|
+
minted,
|
|
294
272
|
assigned,
|
|
295
|
-
deduplicated
|
|
296
|
-
paragraphs: ordinal
|
|
273
|
+
deduplicated
|
|
297
274
|
};
|
|
298
275
|
};
|
|
299
276
|
/**
|
|
@@ -301,71 +278,42 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
|
301
278
|
* `xmlns:mc` and lists `w14` in `mc:Ignorable`, so consumers that predate the
|
|
302
279
|
* 2010 extensions skip the new attributes instead of rejecting the part.
|
|
303
280
|
*/
|
|
304
|
-
const ensureRootNamespaces = (
|
|
305
|
-
let pos = 0;
|
|
306
|
-
let rootStart = -1;
|
|
307
|
-
while (pos < xml.length) {
|
|
308
|
-
const lt = xml.indexOf("<", pos);
|
|
309
|
-
if (lt === -1) break;
|
|
310
|
-
const opaqueEnd = skipOpaqueXmlRegion(xml, lt, partPath);
|
|
311
|
-
if (opaqueEnd !== null) {
|
|
312
|
-
pos = opaqueEnd;
|
|
313
|
-
continue;
|
|
314
|
-
}
|
|
315
|
-
if (xml[lt + 1] === "!") {
|
|
316
|
-
const skipTo = xml.indexOf(">", lt);
|
|
317
|
-
if (skipTo === -1) throw createEnsureParaIdsError(`Unterminated prolog in ${partPath}`);
|
|
318
|
-
pos = skipTo + 1;
|
|
319
|
-
continue;
|
|
320
|
-
}
|
|
321
|
-
rootStart = lt;
|
|
322
|
-
break;
|
|
323
|
-
}
|
|
324
|
-
if (rootStart === -1) throw createEnsureParaIdsError(`No root element in ${partPath}`);
|
|
325
|
-
const rootEnd = xml.indexOf(">", rootStart);
|
|
326
|
-
if (rootEnd === -1) throw createEnsureParaIdsError(`Unterminated root element in ${partPath}`);
|
|
327
|
-
const rootTag = xml.slice(rootStart, rootEnd + 1);
|
|
328
|
-
if (!prefixes.mcDeclared && prefixes.mc.length === 0) throw createEnsureParaIdsError(`xmlns:mc has an unexpected namespace URI in ${partPath}`);
|
|
329
|
-
if (!prefixes.w14Declared && prefixes.w14.length === 0) throw createEnsureParaIdsError(`xmlns:w14 has an unexpected namespace URI in ${partPath}`);
|
|
281
|
+
const ensureRootNamespaces = ({ root, rootNameEnd, ignorable, w14Prefix, mcPrefix }) => {
|
|
330
282
|
const edits = [];
|
|
331
283
|
const declarations = [];
|
|
332
|
-
|
|
333
|
-
if (
|
|
334
|
-
if (
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const text = tokens.length === 0 ? w14Prefix : ` ${w14Prefix}`;
|
|
341
|
-
edits.push({
|
|
342
|
-
start: end,
|
|
343
|
-
end,
|
|
344
|
-
text
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
} else declarations.push(` ${mcPrefix}:Ignorable="${w14Prefix}"`);
|
|
348
|
-
if (declarations.length > 0) {
|
|
349
|
-
let nameEnd = rootStart + 1;
|
|
350
|
-
while (nameEnd < xml.length && !isXmlNameBoundary(xml[nameEnd])) nameEnd += 1;
|
|
351
|
-
edits.push({
|
|
352
|
-
start: nameEnd,
|
|
353
|
-
end: nameEnd,
|
|
354
|
-
text: declarations.join("")
|
|
284
|
+
if (resolveNamespaceUri(root.namespaceScope, mcPrefix) !== MC_NAMESPACE_URI) declarations.push(` xmlns:${mcPrefix}="${MC_NAMESPACE_URI}"`);
|
|
285
|
+
if (resolveNamespaceUri(root.namespaceScope, w14Prefix) !== W14_NAMESPACE_URI) declarations.push(` xmlns:${w14Prefix}="${W14_NAMESPACE_URI}"`);
|
|
286
|
+
if (ignorable !== void 0) {
|
|
287
|
+
const tokens = ignorable.value.split(/\s+/u).filter((token) => token.length > 0);
|
|
288
|
+
if (!tokens.includes(w14Prefix)) edits.push({
|
|
289
|
+
start: ignorable.end,
|
|
290
|
+
end: ignorable.end,
|
|
291
|
+
text: tokens.length === 0 ? w14Prefix : ` ${w14Prefix}`
|
|
355
292
|
});
|
|
356
|
-
}
|
|
293
|
+
} else declarations.push(` ${mcPrefix}:Ignorable="${w14Prefix}"`);
|
|
294
|
+
if (declarations.length > 0) edits.push({
|
|
295
|
+
start: rootNameEnd,
|
|
296
|
+
end: rootNameEnd,
|
|
297
|
+
text: declarations.join("")
|
|
298
|
+
});
|
|
357
299
|
return edits;
|
|
358
300
|
};
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const
|
|
368
|
-
if (
|
|
301
|
+
const mainDocumentPart = (rels, entries) => {
|
|
302
|
+
const root = parseXmlDocument(rels);
|
|
303
|
+
if (root === null || getLocalName(root.name ?? "") !== "Relationships" || getNamespaceUri(root) !== PACKAGE_RELATIONSHIP_URI) throw createEnsureParaIdsError("Malformed package relationships");
|
|
304
|
+
const documents = getChildElements(root).filter((element) => {
|
|
305
|
+
if (getLocalName(element.name ?? "") !== "Relationship" || getNamespaceUri(element) !== PACKAGE_RELATIONSHIP_URI) return false;
|
|
306
|
+
const type = getAttribute(element, null, "Type");
|
|
307
|
+
return [...OFFICE_RELATIONSHIP_NAMESPACE_URIS].some((uri) => type === `${uri}/officeDocument`);
|
|
308
|
+
});
|
|
309
|
+
const document = documents.at(0);
|
|
310
|
+
if (documents.length !== 1 || document === void 0 || getAttribute(document, null, "TargetMode") === "External") throw createEnsureParaIdsError("Package must have one internal officeDocument relationship");
|
|
311
|
+
const target = getAttribute(document, null, "Target");
|
|
312
|
+
const partPath = target === null ? void 0 : resolvePackageRelationshipTarget(target, "_rels/.rels");
|
|
313
|
+
if (partPath === void 0) throw createEnsureParaIdsError("Invalid officeDocument target");
|
|
314
|
+
const name = entries.find((entry) => entry.toLowerCase() === partPath);
|
|
315
|
+
if (name === void 0) throw createEnsureParaIdsError("officeDocument part not found");
|
|
316
|
+
return name;
|
|
369
317
|
};
|
|
370
318
|
/** OPC part names are case-insensitive; compare lowercased. */
|
|
371
319
|
const isTargetPart = (path) => TARGET_PART_PATTERNS.some((pattern) => pattern.test(path.toLowerCase()));
|
|
@@ -378,12 +326,11 @@ const hasDigitalSignatureParts = (zip) => Object.values(zip.files).some(({ dir,
|
|
|
378
326
|
*/
|
|
379
327
|
const ensureParaIdsInternal = async (docx, options) => {
|
|
380
328
|
const archive = await loadDocxArchive(docx);
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
const
|
|
386
|
-
if (documentPartName === void 0) throw createEnsureParaIdsError("word/document.xml not found: not a WordprocessingML package");
|
|
329
|
+
const relsPath = archive.entries.find((name) => name.toLowerCase() === "_rels/.rels");
|
|
330
|
+
const rels = relsPath === void 0 ? null : await archive.readEntryString(relsPath);
|
|
331
|
+
if (rels === null) throw createEnsureParaIdsError("Package relationships not found");
|
|
332
|
+
const documentPartName = mainDocumentPart(rels, archive.entries);
|
|
333
|
+
const xmlPartNames = archive.entries.filter((name) => name.toLowerCase().endsWith(".xml") || name === documentPartName);
|
|
387
334
|
const partTexts = /* @__PURE__ */ new Map();
|
|
388
335
|
for (const name of xmlPartNames) {
|
|
389
336
|
const text = await archive.readEntryString(name);
|
|
@@ -391,39 +338,51 @@ const ensureParaIdsInternal = async (docx, options) => {
|
|
|
391
338
|
}
|
|
392
339
|
const taken = /* @__PURE__ */ new Set();
|
|
393
340
|
for (const text of partTexts.values()) collectExistingParaIds(text, taken);
|
|
341
|
+
const documentXml = partTexts.get(documentPartName);
|
|
342
|
+
if (documentXml === void 0) throw createEnsureParaIdsError("officeDocument part is unreadable");
|
|
394
343
|
const context = {
|
|
395
344
|
taken,
|
|
396
|
-
docKey: deterministicHexId(
|
|
345
|
+
docKey: deterministicHexId(documentXml)
|
|
397
346
|
};
|
|
398
347
|
const targetParts = [documentPartName, ...xmlPartNames.filter((name) => name !== documentPartName && isTargetPart(name)).sort((a, b) => a.localeCompare(b))];
|
|
399
348
|
const spellings = /* @__PURE__ */ new Map();
|
|
400
349
|
const seen = /* @__PURE__ */ new Set();
|
|
401
|
-
for (const [partPath, xml] of partTexts) if (isTargetPart(partPath)) {
|
|
350
|
+
for (const [partPath, xml] of partTexts) if (partPath === documentPartName || isTargetPart(partPath)) {
|
|
402
351
|
const spelling = partSpelling(xml, partPath);
|
|
403
352
|
spellings.set(partPath, spelling);
|
|
404
|
-
collectFallbackParaIds(
|
|
353
|
+
collectFallbackParaIds(spelling, seen);
|
|
405
354
|
} else collectExistingParaIds(xml, seen);
|
|
406
355
|
const updates = /* @__PURE__ */ new Map();
|
|
356
|
+
const mintedParaIds = [];
|
|
407
357
|
let assigned = 0;
|
|
408
358
|
let deduplicated = 0;
|
|
409
359
|
for (const partPath of targetParts) {
|
|
410
360
|
const xml = partTexts.get(partPath);
|
|
411
361
|
const spelling = spellings.get(partPath);
|
|
412
|
-
const scan = scanPart(
|
|
413
|
-
|
|
362
|
+
const scan = scanPart({
|
|
363
|
+
partPath,
|
|
364
|
+
spelling,
|
|
365
|
+
context,
|
|
366
|
+
seen
|
|
367
|
+
});
|
|
414
368
|
if (scan.edits.length === 0) continue;
|
|
415
369
|
assigned += scan.assigned;
|
|
416
370
|
deduplicated += scan.deduplicated;
|
|
417
|
-
|
|
371
|
+
for (const id of scan.minted) mintedParaIds.push(id);
|
|
372
|
+
updates.set(partPath, applySplices(xml, [...scan.edits, ...ensureRootNamespaces(spelling)], partPath));
|
|
418
373
|
}
|
|
419
374
|
if (updates.size === 0) return {
|
|
420
375
|
docx: toUint8Array(docx),
|
|
421
376
|
assigned: 0,
|
|
422
377
|
deduplicated: 0,
|
|
423
|
-
alreadyComplete: true
|
|
378
|
+
alreadyComplete: true,
|
|
379
|
+
mintedParaIds: []
|
|
424
380
|
};
|
|
425
381
|
const zip = await JSZip.loadAsync(docx);
|
|
426
|
-
if (hasDigitalSignatureParts(zip) && options.allowSignedPackageMutation !== true) throw
|
|
382
|
+
if (hasDigitalSignatureParts(zip) && options.allowSignedPackageMutation !== true) throw new EnsureParaIdsError({
|
|
383
|
+
reason: ENSURE_PARA_IDS_REASONS.SIGNED_PACKAGE,
|
|
384
|
+
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."
|
|
385
|
+
});
|
|
427
386
|
for (const [partPath, content] of updates) {
|
|
428
387
|
const sourceEntry = zip.file(partPath);
|
|
429
388
|
if (sourceEntry === null) throw createEnsureParaIdsError(`Package part disappeared during normalization: ${partPath}`);
|
|
@@ -441,7 +400,8 @@ const ensureParaIdsInternal = async (docx, options) => {
|
|
|
441
400
|
}),
|
|
442
401
|
assigned,
|
|
443
402
|
deduplicated,
|
|
444
|
-
alreadyComplete: false
|
|
403
|
+
alreadyComplete: false,
|
|
404
|
+
mintedParaIds
|
|
445
405
|
};
|
|
446
406
|
};
|
|
447
407
|
/**
|
|
@@ -458,4 +418,4 @@ const ensureParaIds = async (docx, options = {}) => {
|
|
|
458
418
|
}
|
|
459
419
|
};
|
|
460
420
|
//#endregion
|
|
461
|
-
export { EnsureParaIdsError, ensureParaIds };
|
|
421
|
+
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 };
|