@stll/folio-core 0.51.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/dist/ai-edits/apply.d.ts +26 -1
- package/dist/ai-edits/apply.js +1519 -105
- package/dist/ai-edits/batch-claims.d.ts +130 -0
- package/dist/ai-edits/batch-claims.js +244 -0
- package/dist/ai-edits/blockRange.js +9 -3
- package/dist/ai-edits/character-boundaries.d.ts +43 -0
- package/dist/ai-edits/character-boundaries.js +95 -0
- package/dist/ai-edits/clean-text.d.ts +33 -3
- package/dist/ai-edits/clean-text.js +52 -6
- package/dist/ai-edits/comment-lifecycle.d.ts +18 -0
- package/dist/ai-edits/comment-lifecycle.js +52 -0
- package/dist/ai-edits/headless.d.ts +71 -7
- package/dist/ai-edits/headless.js +381 -41
- package/dist/ai-edits/index.d.ts +2 -1
- package/dist/ai-edits/index.js +2 -1
- package/dist/ai-edits/minimal-replacement.d.ts +21 -6
- package/dist/ai-edits/minimal-replacement.js +134 -15
- package/dist/ai-edits/newListNumbering.d.ts +23 -0
- package/dist/ai-edits/newListNumbering.js +94 -0
- package/dist/ai-edits/note-references.d.ts +96 -0
- package/dist/ai-edits/note-references.js +139 -0
- package/dist/ai-edits/pending-suggestions.d.ts +79 -0
- package/dist/ai-edits/pending-suggestions.js +306 -0
- package/dist/ai-edits/read.d.ts +1 -1
- package/dist/ai-edits/read.js +56 -43
- package/dist/ai-edits/result-validation.d.ts +29 -0
- package/dist/ai-edits/result-validation.js +164 -0
- package/dist/ai-edits/snapshot.d.ts +36 -3
- package/dist/ai-edits/snapshot.js +177 -23
- package/dist/ai-edits/table-cell-mutations.d.ts +1 -1
- package/dist/ai-edits/table-cell-mutations.js +4 -1
- package/dist/ai-edits/table-geometry.d.ts +1 -1
- package/dist/ai-edits/table-row-column-mutations.d.ts +38 -16
- package/dist/ai-edits/table-row-column-mutations.js +61 -26
- package/dist/ai-edits/types.d.ts +155 -16
- package/dist/compare/compare.d.ts +1 -1
- package/dist/compare/compare.js +65 -16
- package/dist/compare/content-alignment.js +13 -2
- package/dist/compare/content-types.d.ts +7 -0
- package/dist/compare/content.d.ts +4 -1
- package/dist/compare/content.js +6 -1
- package/dist/compare/formatting.js +2 -1
- package/dist/compare/inline-atoms.d.ts +21 -4
- package/dist/compare/inline-atoms.js +138 -16
- package/dist/compare/inline-provenance.js +18 -22
- package/dist/compare/plan.js +10 -5
- package/dist/compare/scenario.js +14 -3
- package/dist/compare/section-boundary-properties.js +14 -1
- package/dist/compare/types.d.ts +4 -2
- package/dist/compare/types.js +2 -1
- package/dist/compare/verification.d.ts +1 -1
- package/dist/compare/verification.js +15 -3
- package/dist/controller/committedLayoutDiff.d.ts +25 -0
- package/dist/controller/committedLayoutDiff.js +71 -0
- package/dist/controller/contentControlWidgetController.d.ts +1 -1
- package/dist/controller/folioEditor.d.ts +1 -1
- package/dist/controller/fontReadiness.d.ts +70 -3
- package/dist/controller/fontReadiness.js +100 -10
- package/dist/controller/headerFooterEditorManager.js +6 -1
- package/dist/controller/layoutPipeline.d.ts +12 -10
- package/dist/controller/layoutPipeline.js +62 -9
- package/dist/controller/layoutRunOptions.d.ts +24 -0
- package/dist/controller/layoutRunOptions.js +17 -0
- package/dist/controller/layoutScheduler.d.ts +19 -14
- package/dist/controller/layoutScheduler.js +15 -12
- package/dist/controller/layoutSession.d.ts +26 -2
- package/dist/controller/layoutSession.js +1 -1
- package/dist/controller/noteEditorManager.js +8 -3
- package/dist/document-operations.d.ts +11 -2
- package/dist/document-operations.js +230 -21
- package/dist/docx/altChunk.d.ts +8 -0
- package/dist/docx/altChunk.js +17 -0
- package/dist/docx/attributeRemainder.d.ts +1 -1
- package/dist/docx/blockContentParser.d.ts +64 -1
- package/dist/docx/blockContentParser.js +17 -4
- package/dist/docx/blockCustomXmlShell.d.ts +7 -0
- package/dist/docx/blockCustomXmlShell.js +14 -0
- package/dist/docx/blockPlainText.js +7 -0
- package/dist/docx/commentAnchorIndex.js +1 -1
- package/dist/docx/commentRangeIntegrity.js +8 -3
- package/dist/docx/commentReferenceNormalization.js +1 -1
- package/dist/docx/commentReplyMarkers.js +20 -1
- package/dist/docx/compatibility.d.ts +1 -1
- package/dist/docx/compatibility.js +34 -13
- package/dist/docx/containerChildren.gen.d.ts +1 -1
- package/dist/docx/containerChildren.gen.js +1 -0
- package/dist/docx/documentParser.js +12 -12
- package/dist/docx/drawingIdNormalization.d.ts +3 -1
- package/dist/docx/drawingIdNormalization.js +6 -1
- package/dist/docx/ensureParaIds.js +119 -97
- package/dist/docx/footnoteParser.js +8 -4
- package/dist/docx/headerFooterParser.js +3 -3
- package/dist/docx/headerFooterReferenceNormalization.d.ts +20 -1
- package/dist/docx/headerFooterReferenceNormalization.js +52 -27
- package/dist/docx/hyperlinkParser.d.ts +1 -1
- package/dist/docx/index.d.ts +1 -1
- package/dist/docx/listNumberingInstances.d.ts +59 -0
- package/dist/docx/listNumberingInstances.js +249 -0
- package/dist/docx/normalizeBaseDirection.js +2 -1
- package/dist/docx/opaqueCarrier.d.ts +7 -0
- package/dist/docx/opaqueCarrier.js +38 -0
- package/dist/docx/packageParts.js +4 -1
- package/dist/docx/paragraphParser.js +91 -24
- package/dist/docx/paragraphPropertySource.d.ts +4 -2
- package/dist/docx/paragraphPropertySource.js +7 -1
- package/dist/docx/paragraphTraversal.js +1 -0
- package/dist/docx/parseWarningMessage.js +4 -1
- package/dist/docx/parser.js +173 -20
- package/dist/docx/rezip.js +27 -5
- package/dist/docx/sdtProperties.js +1 -1
- package/dist/docx/sdtPropertiesPatch.js +3 -0
- package/dist/docx/selectiveSave.js +16 -9
- package/dist/docx/selectiveXmlPatch.js +106 -37
- package/dist/docx/serializer/blockCustomXmlSerializer.d.ts +5 -0
- package/dist/docx/serializer/blockCustomXmlSerializer.js +4 -0
- package/dist/docx/serializer/documentSerializer.js +2 -0
- package/dist/docx/serializer/headerFooterSerializer.js +2 -0
- package/dist/docx/serializer/noteSerializer.js +3 -1
- package/dist/docx/serializer/paragraphSerializer.js +33 -9
- package/dist/docx/serializer/tableSerializer.js +25 -38
- package/dist/docx/server/createBilingualDocument.js +3 -3
- package/dist/docx/tableParser.d.ts +157 -1
- package/dist/docx/tableParser.js +89 -10
- package/dist/docx/textBoxParser.js +1 -1
- package/dist/docx/unzip.d.ts +2 -1
- package/dist/docx/unzip.js +1 -1
- package/dist/docx/wordprocessingPrefixes.d.ts +78 -0
- package/dist/docx/wordprocessingPrefixes.js +286 -0
- package/dist/generated/text_shaper.js +26 -0
- package/dist/generated/text_shaper_bg.wasm +0 -0
- package/dist/i18n/messages/catalogs.gen.d.ts +136 -0
- package/dist/i18n/messages/catalogs.gen.js +153 -17
- package/dist/i18n/messages/messages.gen.d.ts +8 -0
- package/dist/internal/compare/inline-presentation.d.ts +2 -1
- package/dist/internal/compare/inline-presentation.js +11 -10
- package/dist/internal/headlessRevisionResolutionGuard.d.ts +2 -8
- package/dist/internal/headlessRevisionResolutionGuard.js +2 -11
- package/dist/internal/indexedPositionMap.d.ts +11 -0
- package/dist/internal/indexedPositionMap.js +42 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
- package/dist/internal/revisionResolutionEdits.d.ts +13 -0
- package/dist/internal/revisionResolutionEdits.js +19 -0
- package/dist/internal/revisionResolutionInline.d.ts +24 -0
- package/dist/internal/{headlessRevisionResolution.js → revisionResolutionInline.js} +61 -43
- package/dist/internal/revisionResolutionStep.d.ts +34 -0
- package/dist/internal/revisionResolutionStep.js +282 -0
- package/dist/internal/revisionResolutionTracking.d.ts +11 -0
- package/dist/internal/revisionResolutionTracking.js +124 -0
- package/dist/internal/wholeStoryRevisionResolution.d.ts +56 -0
- package/dist/internal/wholeStoryRevisionResolution.js +530 -0
- package/dist/layout-bridge/convert/markupViewFlow.d.ts +20 -0
- package/dist/layout-bridge/convert/markupViewFlow.js +123 -0
- package/dist/layout-bridge/convert/tableConversion.js +1 -1
- package/dist/layout-bridge/convert/toFlowBlocks.js +10 -1
- package/dist/layout-engine/layoutInstrumentation.d.ts +9 -1
- package/dist/layout-engine/layoutInstrumentation.js +4 -1
- package/dist/layout-engine/measure/cache.d.ts +18 -1
- package/dist/layout-engine/measure/cache.js +31 -1
- package/dist/layout-engine/measure/font-metrics.worker.js +3 -7
- package/dist/layout-engine/measure/measureContainer.js +7 -47
- package/dist/layout-engine/measure/measureHelpers.d.ts +6 -1
- package/dist/layout-engine/measure/measureHelpers.js +22 -2
- package/dist/layout-engine/measure/measureWorker.js +2 -3
- package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +6 -7
- package/dist/layout-engine/measure/measureWorkerProtocol.js +1 -2
- package/dist/layout-engine/types.d.ts +6 -0
- package/dist/layout-painter/renderPage.js +1 -0
- package/dist/layout-painter/renderParagraph.js +1 -1
- package/dist/managers/ContextMenuManager.js +5 -2
- package/dist/managers/types.d.ts +3 -0
- package/dist/markdown/fromMarkdown.js +2 -1
- package/dist/markdown/internals.d.ts +11 -1
- package/dist/markdown/internals.js +24 -3
- package/dist/markdown/renderBlock.js +39 -9
- package/dist/markdown/renderParagraph.d.ts +13 -1
- package/dist/markdown/renderParagraph.js +85 -38
- package/dist/markdown/renderRuns.js +3 -10
- package/dist/markdown/renderTable.js +24 -12
- package/dist/markdown/trailers.js +2 -2
- package/dist/markdown/types.d.ts +29 -7
- package/dist/paged-layout/incrementalMeasure.d.ts +1 -2
- package/dist/paged-layout/incrementalMeasure.js +1 -9
- package/dist/paged-layout/transactionDirtyRange.d.ts +1 -3
- package/dist/paged-layout/transactionDirtyRange.js +29 -41
- package/dist/panel-layout.d.ts +83 -0
- package/dist/panel-layout.js +96 -0
- package/dist/prosemirror/anchoredTextBoxes.d.ts +37 -0
- package/dist/prosemirror/anchoredTextBoxes.js +156 -0
- package/dist/prosemirror/attrs/index.d.ts +6 -2
- package/dist/prosemirror/attrs/index.js +172 -1
- package/dist/prosemirror/commands/comments.d.ts +7 -8
- package/dist/prosemirror/commands/comments.js +241 -207
- package/dist/prosemirror/commands/contentControls.d.ts +1 -1
- package/dist/prosemirror/commands/formatPainter.d.ts +1 -1
- package/dist/prosemirror/commands/formatting.d.ts +1 -1
- package/dist/prosemirror/commands/index.d.ts +2 -2
- package/dist/prosemirror/commands/index.js +2 -2
- package/dist/prosemirror/commands/paragraph.d.ts +7 -1
- package/dist/prosemirror/commands/paragraph.js +9 -1
- package/dist/prosemirror/commands/paragraphBookmarkJoin.d.ts +13 -0
- package/dist/prosemirror/commands/paragraphBookmarkJoin.js +22 -0
- package/dist/prosemirror/commands/pastePlainText.d.ts +1 -1
- package/dist/prosemirror/commands/resolveAllTableChanges.d.ts +40 -0
- package/dist/prosemirror/commands/resolveAllTableChanges.js +615 -0
- package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.d.ts +15 -0
- package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.js +46 -0
- package/dist/prosemirror/commands/resolveParagraphProperties.d.ts +16 -0
- package/dist/prosemirror/commands/resolveParagraphProperties.js +59 -0
- package/dist/prosemirror/commands/sectionBreak.js +2 -1
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +8 -2
- package/dist/prosemirror/commands/tableCellMergeResolution.js +1 -1
- package/dist/prosemirror/contentControlRevisions.d.ts +1 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +181 -31
- package/dist/prosemirror/conversion/toProseDoc.js +39 -4
- package/dist/prosemirror/emptyFieldResultRuns.d.ts +18 -0
- package/dist/prosemirror/emptyFieldResultRuns.js +33 -0
- package/dist/prosemirror/extensions/ExtensionManager.d.ts +1 -1
- package/dist/prosemirror/extensions/ExtensionManager.js +3 -1
- package/dist/prosemirror/extensions/StarterKit.js +3 -1
- package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +12 -0
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +3 -0
- package/dist/prosemirror/extensions/features/ListExtension.js +161 -123
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +19 -2
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +33 -20
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +31 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +270 -99
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +3 -2
- package/dist/prosemirror/extensions/features/pasteCleanup.js +2 -1
- package/dist/prosemirror/extensions/features/pastedHtmlLists.d.ts +12 -0
- package/dist/prosemirror/extensions/features/pastedHtmlLists.js +126 -0
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +21 -5
- package/dist/prosemirror/extensions/marks/RunIdentityExtension.d.ts +21 -18
- package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +37 -17
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +7 -2
- package/dist/prosemirror/extensions/marks/markUtils.js +8 -2
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +10 -0
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +35 -0
- package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.js +52 -0
- package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +3 -3
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +50 -15
- package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +4 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.js +19 -18
- package/dist/prosemirror/extensions/types.d.ts +4 -1
- package/dist/prosemirror/findReplaceSelection.js +1 -1
- package/dist/prosemirror/index.d.ts +3 -2
- package/dist/prosemirror/index.js +3 -2
- package/dist/prosemirror/indexedNodeLookup.d.ts +17 -0
- package/dist/prosemirror/indexedNodeLookup.js +103 -0
- package/dist/prosemirror/listAutoformatMarkers.d.ts +19 -0
- package/dist/prosemirror/listAutoformatMarkers.js +98 -0
- package/dist/prosemirror/listInstanceReferences.d.ts +12 -0
- package/dist/prosemirror/listInstanceReferences.js +72 -0
- package/dist/prosemirror/listLabels.d.ts +24 -0
- package/dist/prosemirror/listLabels.js +111 -0
- package/dist/prosemirror/listNumbering.d.ts +110 -0
- package/dist/prosemirror/listNumbering.js +345 -0
- package/dist/prosemirror/listRenderingAttrs.d.ts +11 -2
- package/dist/prosemirror/listRenderingAttrs.js +8 -1
- package/dist/prosemirror/markupViewProjection.d.ts +95 -0
- package/dist/prosemirror/markupViewProjection.js +83 -0
- package/dist/prosemirror/numberingAttr.d.ts +9 -1
- package/dist/prosemirror/numberingAttr.js +9 -1
- package/dist/prosemirror/outlineLevelAttr.js +1 -1
- package/dist/prosemirror/paragraphIndentation.d.ts +10 -1
- package/dist/prosemirror/paragraphIndentation.js +29 -7
- package/dist/prosemirror/paragraphMarkJoin.d.ts +19 -0
- package/dist/prosemirror/paragraphMarkJoin.js +24 -0
- package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +1 -1
- package/dist/prosemirror/plugins/documentNumbering.d.ts +27 -1
- package/dist/prosemirror/plugins/documentNumbering.js +97 -11
- package/dist/prosemirror/plugins/documentStyles.d.ts +10 -1
- package/dist/prosemirror/plugins/documentStyles.js +14 -1
- package/dist/prosemirror/plugins/suggestionMode.d.ts +16 -2
- package/dist/prosemirror/plugins/suggestionMode.js +116 -52
- package/dist/prosemirror/plugins/templateDirectives.d.ts +1 -1
- package/dist/prosemirror/plugins/transactionInvariants.d.ts +32 -0
- package/dist/prosemirror/plugins/transactionInvariants.js +86 -0
- package/dist/prosemirror/positionSweep.d.ts +24 -0
- package/dist/prosemirror/positionSweep.js +222 -0
- package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +1 -1
- package/dist/prosemirror/rejoinRunCarriers.d.ts +1 -1
- package/dist/prosemirror/replacedAnnotations.d.ts +33 -1
- package/dist/prosemirror/replacedAnnotations.js +170 -1
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +1 -1
- package/dist/prosemirror/runStyleFormatting.d.ts +7 -1
- package/dist/prosemirror/runStyleFormatting.js +13 -7
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/index.js +3 -0
- package/dist/prosemirror/schema/nodes.d.ts +42 -11
- package/dist/prosemirror/sectionMarkEdits.d.ts +20 -0
- package/dist/prosemirror/sectionMarkEdits.js +59 -0
- package/dist/prosemirror/storyListNumbering.d.ts +17 -0
- package/dist/prosemirror/storyListNumbering.js +102 -0
- package/dist/prosemirror/tableGridMutation.d.ts +48 -2
- package/dist/prosemirror/tableGridMutation.js +106 -1
- package/dist/prosemirror/validation.js +5 -1
- package/dist/redline.js +93 -9
- package/dist/render-dom/BodySelectionOverlay.d.ts +4 -1
- package/dist/render-dom/BodySelectionOverlay.js +3 -2
- package/dist/shaping/shaper.d.ts +47 -4
- package/dist/shaping/shaper.js +46 -15
- package/dist/text-shaping.d.ts +4 -0
- package/dist/text-shaping.js +4 -0
- package/dist/utils/findReplace.js +2 -2
- package/dist/utils/mergeDocumentContent.js +1 -1
- package/dist/utils/noteReferenceLabels.d.ts +36 -0
- package/dist/utils/noteReferenceLabels.js +32 -0
- package/dist/utils/replaceText.js +1 -1
- package/dist/version-comparison.d.ts +1 -0
- package/dist/version-comparison.js +1 -0
- package/package.json +8 -2
- package/dist/internal/headlessRevisionResolution.d.ts +0 -23
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { deterministicHexId } from "../utils/hexId.js";
|
|
2
2
|
import { isXmlNameBoundary, spliceXml } from "./selectiveXmlPatch.js";
|
|
3
3
|
import { loadDocxArchive } from "./server/boundedArchive.js";
|
|
4
|
+
import { matchCloseTag, matchOpenTag, resolveWordprocessingPrefixes } from "./wordprocessingPrefixes.js";
|
|
5
|
+
import { WORDPROCESSINGML_NAMESPACE_URIS, getChildElements, getLocalName, getNamespaceUri, parseXmlDocument } from "./xmlParser.js";
|
|
4
6
|
import { TaggedError } from "better-result";
|
|
5
7
|
import JSZip from "jszip";
|
|
6
8
|
//#region src/docx/ensureParaIds.ts
|
|
@@ -48,8 +50,15 @@ import JSZip from "jszip";
|
|
|
48
50
|
* declarations and a `mc:Ignorable` listing `w14` when missing — non-Word
|
|
49
51
|
* producers declare neither, and absent `mc:Ignorable` handling is what
|
|
50
52
|
* makes pre-2010 consumers choke on the new attributes.
|
|
53
|
+
* - Prefixes are aliases. Each part is scanned under the prefixes its root
|
|
54
|
+
* binds to WordprocessingML (a default namespace included), `w14` and `mc`
|
|
55
|
+
* (see `resolveWordprocessingPrefixes`), and a minted id is written under
|
|
56
|
+
* the part's own `w14` prefix, which is the one `mc:Ignorable` lists. A
|
|
57
|
+
* part whose bindings a string scan cannot follow (a nested element that
|
|
58
|
+
* rebinds one of them) is refused with {@link EnsureParaIdsError}.
|
|
51
59
|
* - Idempotent: a document that already has full coverage is returned as the
|
|
52
|
-
* original bytes, untouched (`alreadyComplete: true`).
|
|
60
|
+
* original bytes, untouched (`alreadyComplete: true`). A body with
|
|
61
|
+
* paragraphs the scan did not see is refused, never reported complete.
|
|
53
62
|
* - Digitally signed packages are returned untouched when already complete.
|
|
54
63
|
* When normalization would rewrite the package, it fails unless the caller
|
|
55
64
|
* explicitly allows signature invalidation after warning the user.
|
|
@@ -70,9 +79,6 @@ const TARGET_PART_PATTERNS = [
|
|
|
70
79
|
/^word\/footnotes\.xml$/u,
|
|
71
80
|
/^word\/endnotes\.xml$/u
|
|
72
81
|
];
|
|
73
|
-
const PARAGRAPH_OPEN = "<w:p";
|
|
74
|
-
const FALLBACK_OPEN = "<mc:Fallback";
|
|
75
|
-
const FALLBACK_CLOSE = "</mc:Fallback>";
|
|
76
82
|
const OPAQUE_XML_REGIONS = [
|
|
77
83
|
{
|
|
78
84
|
open: "<!--",
|
|
@@ -88,15 +94,30 @@ const OPAQUE_XML_REGIONS = [
|
|
|
88
94
|
}
|
|
89
95
|
];
|
|
90
96
|
/**
|
|
91
|
-
*
|
|
92
|
-
* link replies via `w15:paraId
|
|
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.
|
|
93
101
|
*/
|
|
94
|
-
const ANY_PARA_ID_PATTERN = /\
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
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
|
+
};
|
|
100
121
|
/**
|
|
101
122
|
* Stamp the edits into the part through the splice owner, which refuses a
|
|
102
123
|
* result that would leave a comment range with only one half. These edits
|
|
@@ -141,7 +162,12 @@ const collectExistingParaIds = (xml, into) => {
|
|
|
141
162
|
if (id.length > 0) into.add(id.toUpperCase());
|
|
142
163
|
}
|
|
143
164
|
};
|
|
144
|
-
|
|
165
|
+
/**
|
|
166
|
+
* Every paragraph open tag of a part in document order, under whichever
|
|
167
|
+
* WordprocessingML prefix the part binds, with whether it sits in
|
|
168
|
+
* `mc:Fallback` (under the part's own `mc` prefix).
|
|
169
|
+
*/
|
|
170
|
+
function* paragraphOpenTags(xml, partPath, { prefixes }) {
|
|
145
171
|
let fallbackDepth = 0;
|
|
146
172
|
let pos = 0;
|
|
147
173
|
while (pos < xml.length) {
|
|
@@ -152,27 +178,40 @@ const collectFallbackParaIds = (xml, partPath, into) => {
|
|
|
152
178
|
pos = opaqueEnd;
|
|
153
179
|
continue;
|
|
154
180
|
}
|
|
155
|
-
if (xml
|
|
181
|
+
if (matchOpenTag(xml, tagStart, prefixes.mc, "Fallback") !== -1) {
|
|
156
182
|
const tagEnd = xml.indexOf(">", tagStart);
|
|
157
|
-
if (tagEnd === -1) throw createEnsureParaIdsError(`Unterminated
|
|
183
|
+
if (tagEnd === -1) throw createEnsureParaIdsError(`Unterminated mc:Fallback tag in ${partPath}`);
|
|
158
184
|
if (xml[tagEnd - 1] !== "/") fallbackDepth += 1;
|
|
159
185
|
pos = tagEnd + 1;
|
|
160
186
|
continue;
|
|
161
187
|
}
|
|
162
|
-
|
|
188
|
+
const fallbackClose = matchCloseTag(xml, tagStart, prefixes.mc, "Fallback");
|
|
189
|
+
if (fallbackClose !== -1) {
|
|
163
190
|
fallbackDepth = Math.max(0, fallbackDepth - 1);
|
|
164
|
-
pos = tagStart +
|
|
191
|
+
pos = tagStart + fallbackClose;
|
|
165
192
|
continue;
|
|
166
193
|
}
|
|
167
|
-
|
|
194
|
+
const nameLength = matchOpenTag(xml, tagStart, prefixes.main, "p");
|
|
195
|
+
if (nameLength === -1) {
|
|
168
196
|
pos = tagStart + 1;
|
|
169
197
|
continue;
|
|
170
198
|
}
|
|
171
199
|
const tagEnd = xml.indexOf(">", tagStart);
|
|
172
|
-
if (tagEnd === -1) throw createEnsureParaIdsError(`Unterminated
|
|
173
|
-
const id = OPEN_TAG_PARA_ID_PATTERN.exec(xml.slice(tagStart, tagEnd + 1))?.groups?.["id"];
|
|
174
|
-
if (id) into.add(id.toUpperCase());
|
|
200
|
+
if (tagEnd === -1) throw createEnsureParaIdsError(`Unterminated paragraph tag in ${partPath}`);
|
|
175
201
|
pos = tagEnd + 1;
|
|
202
|
+
yield {
|
|
203
|
+
tagStart,
|
|
204
|
+
tagEnd,
|
|
205
|
+
nameEnd: tagStart + nameLength,
|
|
206
|
+
inFallback: fallbackDepth > 0
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const collectFallbackParaIds = (xml, partPath, spelling, into) => {
|
|
211
|
+
for (const { tagStart, tagEnd, inFallback } of paragraphOpenTags(xml, partPath, spelling)) {
|
|
212
|
+
if (!inFallback) continue;
|
|
213
|
+
const id = spelling.paraId.exec(xml.slice(tagStart, tagEnd + 1))?.groups?.["value"];
|
|
214
|
+
if (id) into.add(id.toUpperCase());
|
|
176
215
|
}
|
|
177
216
|
};
|
|
178
217
|
/**
|
|
@@ -187,74 +226,45 @@ const mintParaId = (context, partPath, ordinal) => {
|
|
|
187
226
|
return id;
|
|
188
227
|
};
|
|
189
228
|
/**
|
|
190
|
-
* One forward scan over a part: every
|
|
191
|
-
* either keeps its paraId (valid, first occurrence) or gets a
|
|
192
|
-
* minting / replacing one. `seen` spans parts so the
|
|
193
|
-
* document-wide across the scan order.
|
|
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.
|
|
194
233
|
*/
|
|
195
|
-
const scanPart = (xml, partPath, context, seen) => {
|
|
234
|
+
const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
196
235
|
const edits = [];
|
|
236
|
+
const w14 = spelling.w14Prefix;
|
|
197
237
|
let assigned = 0;
|
|
198
238
|
let deduplicated = 0;
|
|
199
239
|
let ordinal = 0;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
while (pos < xml.length) {
|
|
203
|
-
const tagStart = xml.indexOf("<", pos);
|
|
204
|
-
if (tagStart === -1) break;
|
|
205
|
-
const opaqueEnd = skipOpaqueXmlRegion(xml, tagStart, partPath);
|
|
206
|
-
if (opaqueEnd !== null) {
|
|
207
|
-
pos = opaqueEnd;
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
if (xml.startsWith(FALLBACK_OPEN, tagStart) && isXmlNameBoundary(xml[tagStart + 12])) {
|
|
211
|
-
const tagEnd = xml.indexOf(">", tagStart);
|
|
212
|
-
if (tagEnd === -1) throw createEnsureParaIdsError(`Unterminated <mc:Fallback> tag in ${partPath}`);
|
|
213
|
-
if (xml[tagEnd - 1] !== "/") fallbackDepth += 1;
|
|
214
|
-
pos = tagEnd + 1;
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
if (xml.startsWith(FALLBACK_CLOSE, tagStart)) {
|
|
218
|
-
fallbackDepth = Math.max(0, fallbackDepth - 1);
|
|
219
|
-
pos = tagStart + 14;
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
if (!xml.startsWith(PARAGRAPH_OPEN, tagStart) || !isXmlNameBoundary(xml[tagStart + 4])) {
|
|
223
|
-
pos = tagStart + 1;
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
const tagEnd = xml.indexOf(">", tagStart);
|
|
227
|
-
if (tagEnd === -1) throw createEnsureParaIdsError(`Unterminated <w:p> tag in ${partPath}`);
|
|
228
|
-
pos = tagEnd + 1;
|
|
229
|
-
if (fallbackDepth > 0) continue;
|
|
240
|
+
for (const { tagStart, tagEnd, nameEnd, inFallback } of paragraphOpenTags(xml, partPath, spelling)) {
|
|
241
|
+
if (inFallback) continue;
|
|
230
242
|
ordinal += 1;
|
|
231
243
|
const openTag = xml.slice(tagStart, tagEnd + 1);
|
|
232
|
-
const paraIdMatch =
|
|
244
|
+
const paraIdMatch = spelling.paraId.exec(openTag);
|
|
233
245
|
if (!paraIdMatch) {
|
|
234
246
|
const id = mintParaId(context, partPath, ordinal);
|
|
235
|
-
const
|
|
236
|
-
const textIdMatch = OPEN_TAG_TEXT_ID_PATTERN.exec(openTag);
|
|
247
|
+
const textIdMatch = spelling.textId.exec(openTag);
|
|
237
248
|
if (textIdMatch) {
|
|
238
249
|
edits.push({
|
|
239
|
-
start:
|
|
240
|
-
end:
|
|
241
|
-
text: ` w14:paraId="${id}"`
|
|
250
|
+
start: nameEnd,
|
|
251
|
+
end: nameEnd,
|
|
252
|
+
text: ` ${w14}:paraId="${id}"`
|
|
242
253
|
});
|
|
243
|
-
const range = attributeValueRange(textIdMatch, tagStart, "id");
|
|
244
254
|
edits.push({
|
|
245
|
-
...
|
|
255
|
+
...attributeValueRange(textIdMatch, tagStart, "value"),
|
|
246
256
|
text: id
|
|
247
257
|
});
|
|
248
258
|
} else edits.push({
|
|
249
|
-
start:
|
|
250
|
-
end:
|
|
251
|
-
text: ` w14:paraId="${id}" w14:textId="${id}"`
|
|
259
|
+
start: nameEnd,
|
|
260
|
+
end: nameEnd,
|
|
261
|
+
text: ` ${w14}:paraId="${id}" ${w14}:textId="${id}"`
|
|
252
262
|
});
|
|
253
263
|
assigned += 1;
|
|
254
264
|
seen.add(id);
|
|
255
265
|
continue;
|
|
256
266
|
}
|
|
257
|
-
const value = paraIdMatch.groups["
|
|
267
|
+
const value = paraIdMatch.groups["value"].toUpperCase();
|
|
258
268
|
const unassigned = RESERVED_ZERO_ID_PATTERN.test(value);
|
|
259
269
|
if (!unassigned && !seen.has(value)) {
|
|
260
270
|
seen.add(value);
|
|
@@ -262,22 +272,19 @@ const scanPart = (xml, partPath, context, seen) => {
|
|
|
262
272
|
}
|
|
263
273
|
const id = mintParaId(context, partPath, ordinal);
|
|
264
274
|
edits.push({
|
|
265
|
-
...attributeValueRange(paraIdMatch, tagStart, "
|
|
275
|
+
...attributeValueRange(paraIdMatch, tagStart, "value"),
|
|
266
276
|
text: id
|
|
267
277
|
});
|
|
268
|
-
const textIdMatch =
|
|
278
|
+
const textIdMatch = spelling.textId.exec(openTag);
|
|
269
279
|
if (textIdMatch) edits.push({
|
|
270
|
-
...attributeValueRange(textIdMatch, tagStart, "
|
|
280
|
+
...attributeValueRange(textIdMatch, tagStart, "value"),
|
|
271
281
|
text: id
|
|
272
282
|
});
|
|
273
|
-
else {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
text: ` w14:textId="${id}"`
|
|
279
|
-
});
|
|
280
|
-
}
|
|
283
|
+
else edits.push({
|
|
284
|
+
start: nameEnd,
|
|
285
|
+
end: nameEnd,
|
|
286
|
+
text: ` ${w14}:textId="${id}"`
|
|
287
|
+
});
|
|
281
288
|
if (unassigned) assigned += 1;
|
|
282
289
|
else deduplicated += 1;
|
|
283
290
|
seen.add(id);
|
|
@@ -285,7 +292,8 @@ const scanPart = (xml, partPath, context, seen) => {
|
|
|
285
292
|
return {
|
|
286
293
|
edits,
|
|
287
294
|
assigned,
|
|
288
|
-
deduplicated
|
|
295
|
+
deduplicated,
|
|
296
|
+
paragraphs: ordinal
|
|
289
297
|
};
|
|
290
298
|
};
|
|
291
299
|
/**
|
|
@@ -293,7 +301,7 @@ const scanPart = (xml, partPath, context, seen) => {
|
|
|
293
301
|
* `xmlns:mc` and lists `w14` in `mc:Ignorable`, so consumers that predate the
|
|
294
302
|
* 2010 extensions skip the new attributes instead of rejecting the part.
|
|
295
303
|
*/
|
|
296
|
-
const ensureRootNamespaces = (xml, partPath) => {
|
|
304
|
+
const ensureRootNamespaces = (xml, partPath, { prefixes, w14Prefix }) => {
|
|
297
305
|
let pos = 0;
|
|
298
306
|
let rootStart = -1;
|
|
299
307
|
while (pos < xml.length) {
|
|
@@ -317,29 +325,26 @@ const ensureRootNamespaces = (xml, partPath) => {
|
|
|
317
325
|
const rootEnd = xml.indexOf(">", rootStart);
|
|
318
326
|
if (rootEnd === -1) throw createEnsureParaIdsError(`Unterminated root element in ${partPath}`);
|
|
319
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}`);
|
|
320
330
|
const edits = [];
|
|
321
331
|
const declarations = [];
|
|
322
|
-
const
|
|
323
|
-
if (
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
const w14Namespace = XMLNS_W14_PATTERN.exec(rootTag);
|
|
327
|
-
if (w14Namespace) {
|
|
328
|
-
if (w14Namespace.groups["value"] !== W14_NAMESPACE_URI) throw createEnsureParaIdsError(`xmlns:w14 has an unexpected namespace URI in ${partPath}`);
|
|
329
|
-
} else declarations.push(` xmlns:w14="${W14_NAMESPACE_URI}"`);
|
|
330
|
-
const ignorable = MC_IGNORABLE_PATTERN.exec(rootTag);
|
|
332
|
+
const mcPrefix = (prefixes.mcDeclared ? prefixes.mc[0] : void 0) ?? "mc";
|
|
333
|
+
if (!prefixes.mcDeclared) declarations.push(` xmlns:mc="${MC_NAMESPACE_URI}"`);
|
|
334
|
+
if (!prefixes.w14Declared) declarations.push(` xmlns:w14="${W14_NAMESPACE_URI}"`);
|
|
335
|
+
const ignorable = prefixedAttributePattern(prefixes.mcDeclared ? prefixes.mc : [mcPrefix], "Ignorable").exec(rootTag);
|
|
331
336
|
if (ignorable) {
|
|
332
337
|
const tokens = ignorable.groups["value"].split(/\s+/u).filter((token) => token.length > 0);
|
|
333
|
-
if (!tokens.includes(
|
|
338
|
+
if (!tokens.includes(w14Prefix)) {
|
|
334
339
|
const { end } = attributeValueRange(ignorable, rootStart, "value");
|
|
335
|
-
const text = tokens.length === 0 ?
|
|
340
|
+
const text = tokens.length === 0 ? w14Prefix : ` ${w14Prefix}`;
|
|
336
341
|
edits.push({
|
|
337
342
|
start: end,
|
|
338
343
|
end,
|
|
339
344
|
text
|
|
340
345
|
});
|
|
341
346
|
}
|
|
342
|
-
} else declarations.push(`
|
|
347
|
+
} else declarations.push(` ${mcPrefix}:Ignorable="${w14Prefix}"`);
|
|
343
348
|
if (declarations.length > 0) {
|
|
344
349
|
let nameEnd = rootStart + 1;
|
|
345
350
|
while (nameEnd < xml.length && !isXmlNameBoundary(xml[nameEnd])) nameEnd += 1;
|
|
@@ -351,6 +356,17 @@ const ensureRootNamespaces = (xml, partPath) => {
|
|
|
351
356
|
}
|
|
352
357
|
return edits;
|
|
353
358
|
};
|
|
359
|
+
const hasWordParagraph = (element) => getLocalName(element.name ?? "") === "p" && WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(element) ?? "") || getChildElements(element).some(hasWordParagraph);
|
|
360
|
+
/**
|
|
361
|
+
* A body the string scan saw no paragraph in is either genuinely empty or
|
|
362
|
+
* spelled in a way the scan missed. Only the namespace-aware parser can tell
|
|
363
|
+
* which, and it only has to be asked in that rare case: `alreadyComplete`
|
|
364
|
+
* must never describe paragraphs nobody looked at.
|
|
365
|
+
*/
|
|
366
|
+
const assertNoUnscannedParagraphs = (xml, partPath) => {
|
|
367
|
+
const root = parseXmlDocument(xml);
|
|
368
|
+
if (root !== null && hasWordParagraph(root)) throw createEnsureParaIdsError(`Cannot normalize paragraph ids in ${partPath}: its paragraphs are not spelled in a way the scan can patch`);
|
|
369
|
+
};
|
|
354
370
|
/** OPC part names are case-insensitive; compare lowercased. */
|
|
355
371
|
const isTargetPart = (path) => TARGET_PART_PATTERNS.some((pattern) => pattern.test(path.toLowerCase()));
|
|
356
372
|
const toUint8Array = (docx) => docx instanceof Uint8Array ? docx : new Uint8Array(docx);
|
|
@@ -380,19 +396,25 @@ const ensureParaIdsInternal = async (docx, options) => {
|
|
|
380
396
|
docKey: deterministicHexId(partTexts.get(documentPartName))
|
|
381
397
|
};
|
|
382
398
|
const targetParts = [documentPartName, ...xmlPartNames.filter((name) => name !== documentPartName && isTargetPart(name)).sort((a, b) => a.localeCompare(b))];
|
|
399
|
+
const spellings = /* @__PURE__ */ new Map();
|
|
383
400
|
const seen = /* @__PURE__ */ new Set();
|
|
384
|
-
for (const [partPath, xml] of partTexts) if (isTargetPart(partPath))
|
|
385
|
-
|
|
401
|
+
for (const [partPath, xml] of partTexts) if (isTargetPart(partPath)) {
|
|
402
|
+
const spelling = partSpelling(xml, partPath);
|
|
403
|
+
spellings.set(partPath, spelling);
|
|
404
|
+
collectFallbackParaIds(xml, partPath, spelling, seen);
|
|
405
|
+
} else collectExistingParaIds(xml, seen);
|
|
386
406
|
const updates = /* @__PURE__ */ new Map();
|
|
387
407
|
let assigned = 0;
|
|
388
408
|
let deduplicated = 0;
|
|
389
409
|
for (const partPath of targetParts) {
|
|
390
410
|
const xml = partTexts.get(partPath);
|
|
391
|
-
const
|
|
411
|
+
const spelling = spellings.get(partPath);
|
|
412
|
+
const scan = scanPart(xml, partPath, spelling, context, seen);
|
|
413
|
+
if (partPath === documentPartName && scan.paragraphs === 0) assertNoUnscannedParagraphs(xml, partPath);
|
|
392
414
|
if (scan.edits.length === 0) continue;
|
|
393
415
|
assigned += scan.assigned;
|
|
394
416
|
deduplicated += scan.deduplicated;
|
|
395
|
-
updates.set(partPath, applySplices(xml, [...scan.edits, ...ensureRootNamespaces(xml, partPath)], partPath));
|
|
417
|
+
updates.set(partPath, applySplices(xml, [...scan.edits, ...ensureRootNamespaces(xml, partPath, spelling)], partPath));
|
|
396
418
|
}
|
|
397
419
|
if (updates.size === 0) return {
|
|
398
420
|
docx: toUint8Array(docx),
|
|
@@ -4,7 +4,8 @@ import { parseParagraph } from "./paragraphParser.js";
|
|
|
4
4
|
import { standalonePreviewLedger } from "./previewBudget.js";
|
|
5
5
|
import { captureSdtSiblingMarkers, parseSdtProperties } from "./sdtProperties.js";
|
|
6
6
|
import { parseTable } from "./tableParser.js";
|
|
7
|
-
import {
|
|
7
|
+
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
8
|
+
import { WORDPROCESSINGML_NAMESPACE_URIS, findChildren, findWordprocessingChild, getAttributes, getChildElements, getLocalName, parseXml } from "./xmlParser.js";
|
|
8
9
|
import { PARSE_WARNING_CODES } from "@stll/docx-core/model";
|
|
9
10
|
//#region src/docx/footnoteParser.ts
|
|
10
11
|
/**
|
|
@@ -49,7 +50,10 @@ function parseNoteBlockContent(element, styles, theme, numbering, rels, media, p
|
|
|
49
50
|
properties,
|
|
50
51
|
content: sdtContent ? parseNoteBlockContent(sdtContent, styles, theme, numbering, rels, media, previews) : []
|
|
51
52
|
});
|
|
52
|
-
}
|
|
53
|
+
} else if (localName === "altChunk" && WORDPROCESSINGML_NAMESPACE_URIS.has(child.namespaceUri ?? "")) blocks.push({
|
|
54
|
+
type: "preservedBlock",
|
|
55
|
+
xml: captureVerbatimXml(child)
|
|
56
|
+
});
|
|
53
57
|
}
|
|
54
58
|
return blocks;
|
|
55
59
|
}
|
|
@@ -79,7 +83,7 @@ function parseFootnotes(footnotesXml, styles = null, theme = null, numbering = n
|
|
|
79
83
|
const byId = /* @__PURE__ */ new Map();
|
|
80
84
|
const footnotes = [];
|
|
81
85
|
if (!footnotesXml) return createFootnoteMap(byId, footnotes);
|
|
82
|
-
const rootElement = parseXml(footnotesXml).elements?.find((el) => el.type === "element" && (el.name
|
|
86
|
+
const rootElement = parseXml(footnotesXml).elements?.find((el) => el.type === "element" && getLocalName(el.name ?? "") === "footnotes");
|
|
83
87
|
if (!rootElement) return createFootnoteMap(byId, footnotes);
|
|
84
88
|
const footnoteElements = findChildren(rootElement, "w", "footnote");
|
|
85
89
|
for (const fnEl of footnoteElements) {
|
|
@@ -148,7 +152,7 @@ function parseEndnotes(endnotesXml, styles = null, theme = null, numbering = nul
|
|
|
148
152
|
const byId = /* @__PURE__ */ new Map();
|
|
149
153
|
const endnotes = [];
|
|
150
154
|
if (!endnotesXml) return createEndnoteMap(byId, endnotes);
|
|
151
|
-
const rootElement = parseXml(endnotesXml).elements?.find((el) => el.type === "element" && (el.name
|
|
155
|
+
const rootElement = parseXml(endnotesXml).elements?.find((el) => el.type === "element" && getLocalName(el.name ?? "") === "endnotes");
|
|
152
156
|
if (!rootElement) return createEndnoteMap(byId, endnotes);
|
|
153
157
|
const endnoteElements = findChildren(rootElement, "w", "endnote");
|
|
154
158
|
for (const enEl of endnoteElements) {
|
|
@@ -5,7 +5,7 @@ import { assignHeaderFooterVerbatimXml } from "./headerFooterVerbatim.js";
|
|
|
5
5
|
import { cloneParagraphWithPropertySource } from "./paragraphPropertySource.js";
|
|
6
6
|
import { standalonePreviewLedger } from "./previewBudget.js";
|
|
7
7
|
import { parseWatermark } from "./watermarkParser.js";
|
|
8
|
-
import { collectXmlnsDeclarations, parseXml } from "./xmlParser.js";
|
|
8
|
+
import { collectXmlnsDeclarations, getLocalName, parseXml } from "./xmlParser.js";
|
|
9
9
|
//#region src/docx/headerFooterParser.ts
|
|
10
10
|
/**
|
|
11
11
|
* Parse a header XML file (word/header*.xml)
|
|
@@ -28,7 +28,7 @@ function parseHeader(headerXml, hdrFtrType = "default", styles = null, theme = n
|
|
|
28
28
|
content: []
|
|
29
29
|
};
|
|
30
30
|
if (!headerXml) return result;
|
|
31
|
-
const rootElement = parseXml(headerXml).elements?.find((el) => el.type === "element" && (el.name
|
|
31
|
+
const rootElement = parseXml(headerXml).elements?.find((el) => el.type === "element" && getLocalName(el.name ?? "") === "hdr");
|
|
32
32
|
if (!rootElement) return result;
|
|
33
33
|
const watermarkResult = parseWatermark(rootElement);
|
|
34
34
|
if (watermarkResult) {
|
|
@@ -72,7 +72,7 @@ function parseFooter(footerXml, hdrFtrType = "default", styles = null, theme = n
|
|
|
72
72
|
content: []
|
|
73
73
|
};
|
|
74
74
|
if (!footerXml) return result;
|
|
75
|
-
const rootElement = parseXml(footerXml).elements?.find((el) => el.type === "element" && (el.name
|
|
75
|
+
const rootElement = parseXml(footerXml).elements?.find((el) => el.type === "element" && getLocalName(el.name ?? "") === "ftr");
|
|
76
76
|
if (!rootElement) return result;
|
|
77
77
|
result.content = parseBlockContent(rootElement, styles, theme, numbering, rels, media, {
|
|
78
78
|
inHeaderFooter: true,
|
|
@@ -13,5 +13,24 @@ type NormalizeHeaderFooterReferencesResult = {
|
|
|
13
13
|
removedDanglingFooterReferences: number;
|
|
14
14
|
};
|
|
15
15
|
declare const normalizeHeaderFooterReferences: ({ documentBody, headers, footers }: NormalizeHeaderFooterReferencesInput) => NormalizeHeaderFooterReferencesResult;
|
|
16
|
+
type AssignHeaderFooterRolesInput = {
|
|
17
|
+
documentBody: document_d_exports.DocumentBody;
|
|
18
|
+
headers?: Map<string, document_d_exports.HeaderFooter>;
|
|
19
|
+
footers?: Map<string, document_d_exports.HeaderFooter>;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Give each header and footer part the role a section reference states for
|
|
23
|
+
* it.
|
|
24
|
+
*
|
|
25
|
+
* A part says nothing about which pages it serves: the `w:type` of the
|
|
26
|
+
* `w:headerReference` / `w:footerReference` naming it does (ECMA-376 Part 1
|
|
27
|
+
* §17.10.5, `ST_HdrFtr`). The parts are read from the document's
|
|
28
|
+
* relationships, before any section is consulted, so each is read as a
|
|
29
|
+
* default part and takes its role here, from the first section that
|
|
30
|
+
* references it. A part no section references stays default; one several
|
|
31
|
+
* sections reference in different roles keeps the first, and a reader that
|
|
32
|
+
* needs every role reads the section references themselves.
|
|
33
|
+
*/
|
|
34
|
+
declare const assignHeaderFooterRoles: ({ documentBody, headers, footers }: AssignHeaderFooterRolesInput) => void;
|
|
16
35
|
//#endregion
|
|
17
|
-
export { DANGLING_FOOTER_REFERENCE_WARNING, DANGLING_HEADER_REFERENCE_WARNING, normalizeHeaderFooterReferences };
|
|
36
|
+
export { DANGLING_FOOTER_REFERENCE_WARNING, DANGLING_HEADER_REFERENCE_WARNING, assignHeaderFooterRoles, normalizeHeaderFooterReferences };
|
|
@@ -4,12 +4,9 @@ import { PARSE_WARNING_CODES } from "@stll/docx-core/model";
|
|
|
4
4
|
const DANGLING_HEADER_REFERENCE_WARNING = PARSE_WARNING_CODES.danglingHeaderReference;
|
|
5
5
|
const DANGLING_FOOTER_REFERENCE_WARNING = PARSE_WARNING_CODES.danglingFooterReference;
|
|
6
6
|
const normalizeHeaderFooterReferences = ({ documentBody, headers, footers }) => {
|
|
7
|
-
const seenSectionProperties = /* @__PURE__ */ new Set();
|
|
8
7
|
let removedDanglingHeaderReferences = 0;
|
|
9
8
|
let removedDanglingFooterReferences = 0;
|
|
10
9
|
const normalizeSectionProperties = (sectionProperties) => {
|
|
11
|
-
if (!sectionProperties || seenSectionProperties.has(sectionProperties)) return;
|
|
12
|
-
seenSectionProperties.add(sectionProperties);
|
|
13
10
|
const headerResult = removeDanglingReferences(sectionProperties.headerReferences, headers);
|
|
14
11
|
if (headerResult.changed) {
|
|
15
12
|
removedDanglingHeaderReferences += headerResult.removed;
|
|
@@ -23,34 +20,62 @@ const normalizeHeaderFooterReferences = ({ documentBody, headers, footers }) =>
|
|
|
23
20
|
else delete sectionProperties.footerReferences;
|
|
24
21
|
}
|
|
25
22
|
};
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
forEachSectionProperties(documentBody, normalizeSectionProperties);
|
|
24
|
+
return {
|
|
25
|
+
removedDanglingHeaderReferences,
|
|
26
|
+
removedDanglingFooterReferences
|
|
28
27
|
};
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Visit every distinct section record once, in section order: the paragraph
|
|
31
|
+
* carriers in document order, then the body's final `w:sectPr`.
|
|
32
|
+
*/
|
|
33
|
+
const forEachSectionProperties = (documentBody, visit) => {
|
|
34
|
+
const seen = /* @__PURE__ */ new Set();
|
|
35
|
+
const visitOnce = (sectionProperties) => {
|
|
36
|
+
if (!sectionProperties || seen.has(sectionProperties)) return;
|
|
37
|
+
seen.add(sectionProperties);
|
|
38
|
+
visit(sectionProperties);
|
|
31
39
|
};
|
|
32
|
-
const
|
|
33
|
-
if (block.type === "paragraph")
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
if (block.type === "table") {
|
|
38
|
-
normalizeTable(block);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
if (block.type !== "blockSdt") return;
|
|
42
|
-
normalizeBlocks(block.content);
|
|
40
|
+
const visitBlocks = (blocks) => {
|
|
41
|
+
for (const block of blocks) if (block.type === "paragraph") visitOnce(block.sectionProperties);
|
|
42
|
+
else if (block.type === "table") visitTable(block);
|
|
43
|
+
else if (block.type === "blockSdt" || block.type === "blockCustomXml") visitBlocks(block.content);
|
|
43
44
|
};
|
|
44
|
-
const
|
|
45
|
-
for (const
|
|
45
|
+
const visitTable = (table) => {
|
|
46
|
+
for (const row of table.rows) for (const cell of row.cells) visitBlocks(cell.content);
|
|
46
47
|
};
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
for (const section of documentBody.sections ?? [])
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
visitBlocks(documentBody.content);
|
|
49
|
+
visitOnce(documentBody.finalSectionProperties);
|
|
50
|
+
for (const section of documentBody.sections ?? []) visitOnce(section.properties);
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Give each header and footer part the role a section reference states for
|
|
54
|
+
* it.
|
|
55
|
+
*
|
|
56
|
+
* A part says nothing about which pages it serves: the `w:type` of the
|
|
57
|
+
* `w:headerReference` / `w:footerReference` naming it does (ECMA-376 Part 1
|
|
58
|
+
* §17.10.5, `ST_HdrFtr`). The parts are read from the document's
|
|
59
|
+
* relationships, before any section is consulted, so each is read as a
|
|
60
|
+
* default part and takes its role here, from the first section that
|
|
61
|
+
* references it. A part no section references stays default; one several
|
|
62
|
+
* sections reference in different roles keeps the first, and a reader that
|
|
63
|
+
* needs every role reads the section references themselves.
|
|
64
|
+
*/
|
|
65
|
+
const assignHeaderFooterRoles = ({ documentBody, headers, footers }) => {
|
|
66
|
+
const assigned = /* @__PURE__ */ new Set();
|
|
67
|
+
const assign = (references, parts) => {
|
|
68
|
+
for (const { type, rId } of references ?? []) {
|
|
69
|
+
const part = parts?.get(rId);
|
|
70
|
+
if (!part || assigned.has(part)) continue;
|
|
71
|
+
assigned.add(part);
|
|
72
|
+
part.hdrFtrType = type;
|
|
73
|
+
}
|
|
53
74
|
};
|
|
75
|
+
forEachSectionProperties(documentBody, (sectionProperties) => {
|
|
76
|
+
assign(sectionProperties.headerReferences, headers);
|
|
77
|
+
assign(sectionProperties.footerReferences, footers);
|
|
78
|
+
});
|
|
54
79
|
};
|
|
55
80
|
const removeDanglingReferences = (references, validParts) => {
|
|
56
81
|
if (!references || !validParts) return {
|
|
@@ -66,4 +91,4 @@ const removeDanglingReferences = (references, validParts) => {
|
|
|
66
91
|
};
|
|
67
92
|
};
|
|
68
93
|
//#endregion
|
|
69
|
-
export { DANGLING_FOOTER_REFERENCE_WARNING, DANGLING_HEADER_REFERENCE_WARNING, normalizeHeaderFooterReferences };
|
|
94
|
+
export { DANGLING_FOOTER_REFERENCE_WARNING, DANGLING_HEADER_REFERENCE_WARNING, assignHeaderFooterRoles, normalizeHeaderFooterReferences };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { XmlElement } from "./xmlParser.js";
|
|
3
|
+
import { ChildReader } from "./containerChildren.js";
|
|
3
4
|
import { PreviewLedger } from "./previewBudget.js";
|
|
4
5
|
import { StyleMap } from "./styleParser.js";
|
|
5
|
-
import { ChildReader } from "./containerChildren.js";
|
|
6
6
|
//#region src/docx/hyperlinkParser.d.ts
|
|
7
7
|
/**
|
|
8
8
|
* Parse a hyperlink element (w:hyperlink)
|
package/dist/docx/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getCachedNumberingMap } from "./numberingParser.js";
|
|
2
|
+
import { NO_NUMBERING_NUM_ID, NO_PARAGRAPH_NUMBERING, ParagraphNumberingOverride, ParagraphNumberingSlots, ResolvedParagraphNumbering, isNumberingReference, mergeParagraphNumbering, paragraphNumberingFromSlots, paragraphNumberingLevel, paragraphNumberingReferenceId, paragraphNumberingSlots, readParagraphNumbering, resolveParagraphNumbering, sameEffectiveParagraphNumbering, sameStatedParagraphNumbering } from "./numberingReference.js";
|
|
2
3
|
import { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError, DocxEncryptionErrorCode, isDocxEncryptionError } from "./encryption/errors.js";
|
|
3
4
|
import { DecryptDocxOptions, DecryptDocxResult, decryptDocxIfNeeded, openDocxBuffer } from "./encryption/openEncryptedDocx.js";
|
|
4
|
-
import { NO_NUMBERING_NUM_ID, NO_PARAGRAPH_NUMBERING, ParagraphNumberingOverride, ParagraphNumberingSlots, ResolvedParagraphNumbering, isNumberingReference, mergeParagraphNumbering, paragraphNumberingFromSlots, paragraphNumberingLevel, paragraphNumberingReferenceId, paragraphNumberingSlots, readParagraphNumbering, resolveParagraphNumbering, sameEffectiveParagraphNumbering, sameStatedParagraphNumbering } from "./numberingReference.js";
|
|
5
5
|
import { FOLIO_DOCUMENT_METADATA_PROPERTIES, FOLIO_DOCUMENT_PRIVACY_TRANSFORMS, FolioDocumentMetadataProperty, FolioDocumentPrivacyArchiveError, FolioDocumentPrivacyOptions, FolioDocumentPrivacyReport, FolioDocumentPrivacyTransform, InvalidFolioDocumentPrivacyOptionsError, RewriteDocxMetadataPrivacyResult, isFolioDocumentPrivacyTransform, rewriteDocxMetadataPrivacy } from "./metadataPrivacy.js";
|
|
6
6
|
export { DOCX_ENCRYPTION_ERROR_CODES, type DecryptDocxOptions, type DecryptDocxResult, DocxEncryptionError, type DocxEncryptionErrorCode, FOLIO_DOCUMENT_METADATA_PROPERTIES, FOLIO_DOCUMENT_PRIVACY_TRANSFORMS, type FolioDocumentMetadataProperty, FolioDocumentPrivacyArchiveError, type FolioDocumentPrivacyOptions, type FolioDocumentPrivacyReport, type FolioDocumentPrivacyTransform, InvalidFolioDocumentPrivacyOptionsError, NO_NUMBERING_NUM_ID, NO_PARAGRAPH_NUMBERING, type ParagraphNumberingOverride, type ParagraphNumberingSlots, type ResolvedParagraphNumbering, type RewriteDocxMetadataPrivacyResult, decryptDocxIfNeeded, getCachedNumberingMap, isDocxEncryptionError, isFolioDocumentPrivacyTransform, isNumberingReference, mergeParagraphNumbering, openDocxBuffer, paragraphNumberingFromSlots, paragraphNumberingLevel, paragraphNumberingReferenceId, paragraphNumberingSlots, readParagraphNumbering, resolveParagraphNumbering, rewriteDocxMetadataPrivacy, sameEffectiveParagraphNumbering, sameStatedParagraphNumbering };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
//#region src/docx/listNumberingInstances.d.ts
|
|
3
|
+
/** The two kinds of list a command can create. */
|
|
4
|
+
declare const LIST_KINDS: readonly ["bullet", "numbered"];
|
|
5
|
+
type ListKind = (typeof LIST_KINDS)[number];
|
|
6
|
+
/** What a numbered list's first level counts in and how its marker reads. */
|
|
7
|
+
type ListLevelFormat = {
|
|
8
|
+
numFmt: document_d_exports.NumberFormat;
|
|
9
|
+
/** `w:lvlText` for level 0, e.g. `%1.` or `%1)`. */
|
|
10
|
+
lvlText: string;
|
|
11
|
+
};
|
|
12
|
+
/** The first-level appearance and start a new list is minted with. */
|
|
13
|
+
type NewListDefinition = {
|
|
14
|
+
kind: ListKind;
|
|
15
|
+
/** Level-0 format of a numbered list; the template's `%1.` decimal otherwise. */
|
|
16
|
+
format?: ListLevelFormat | undefined;
|
|
17
|
+
/** The value the first item shows, stated as a level-0 `w:startOverride`. */
|
|
18
|
+
start?: number | undefined;
|
|
19
|
+
};
|
|
20
|
+
type MintedListInstance = {
|
|
21
|
+
definitions: document_d_exports.NumberingDefinitions;
|
|
22
|
+
numId: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Define a new list: a new `w:abstractNum` of the requested kind and the
|
|
26
|
+
* `w:num` naming it. `definitions` must already hold every instance the
|
|
27
|
+
* document references (see {@link completeListNumbering}), so the new ids
|
|
28
|
+
* collide with none of them.
|
|
29
|
+
*/
|
|
30
|
+
declare const mintListInstance: (definitions: document_d_exports.NumberingDefinitions | null | undefined, { kind, format, start }: NewListDefinition) => MintedListInstance;
|
|
31
|
+
type RestartListInstanceOptions = {
|
|
32
|
+
abstractNumId: number;
|
|
33
|
+
ilvl: number;
|
|
34
|
+
start: number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* A new `w:num` over an existing `w:abstractNum`, with a `w:startOverride` at
|
|
38
|
+
* `ilvl`: *Restart Numbering* and *Set Numbering Value*. The override is what
|
|
39
|
+
* makes a consumer count the instance on its own rather than continue the
|
|
40
|
+
* other instances of the same definition.
|
|
41
|
+
*/
|
|
42
|
+
declare const restartListInstance: (definitions: document_d_exports.NumberingDefinitions | null | undefined, { abstractNumId, ilvl, start }: RestartListInstanceOptions) => MintedListInstance;
|
|
43
|
+
/** One paragraph's numbering reference and the rendering it resolved to. */
|
|
44
|
+
type ListInstanceReference = {
|
|
45
|
+
numId: number;
|
|
46
|
+
ilvl: number;
|
|
47
|
+
rendering: document_d_exports.ListRendering;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Define every instance `references` name that `definitions` does not.
|
|
51
|
+
*
|
|
52
|
+
* A reference the editor resolved carries its rendering, and a rendering is
|
|
53
|
+
* what a minted instance leaves behind; a reference without one (a dangling
|
|
54
|
+
* id read from a file) has no rendering and so is never defined here. Returns
|
|
55
|
+
* `definitions` itself when nothing is missing.
|
|
56
|
+
*/
|
|
57
|
+
declare const completeListNumbering: (definitions: document_d_exports.NumberingDefinitions | undefined, references: Iterable<ListInstanceReference>) => document_d_exports.NumberingDefinitions | undefined;
|
|
58
|
+
//#endregion
|
|
59
|
+
export { LIST_KINDS, ListInstanceReference, ListKind, ListLevelFormat, completeListNumbering, mintListInstance, restartListInstance };
|