@stll/folio-core 0.52.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/plan.js +10 -5
- package/dist/compare/scenario.js +14 -3
- package/dist/compare/section-boundary-properties.js +1 -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/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/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/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 +3 -2
- package/dist/internal/headlessRevisionResolution.d.ts +0 -23
package/dist/docx/tableParser.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NO_MODELLED_ATTRIBUTES, attributeRemainder } from "./attributeRemainder.js";
|
|
2
|
+
import { blockCustomXmlShell } from "./blockCustomXmlShell.js";
|
|
2
3
|
import { parseBookmarkEnd, parseBookmarkStart } from "./bookmarkParser.js";
|
|
3
4
|
import { parseBorderSpec } from "./borderParser.js";
|
|
4
5
|
import { CAPTURE, dispatchChildrenWithContext, keptUnless, ownedElsewhere, sequencePositions, withPreservedChildren } from "./containerChildren.js";
|
|
@@ -14,6 +15,7 @@ import { parsePropertyChangeInfo, parseTrackedChangeInfo } from "./trackedChange
|
|
|
14
15
|
import { percentageSpelling, transitionalSlotEncoding } from "./transitionalSpelling.js";
|
|
15
16
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
16
17
|
import { WORDPROCESSINGML_NAMESPACE_URIS, cloneElement, findChild, findChildren, findWordprocessingChild, getAttribute, getAttributeByNamespaceUri, getLocalName, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, parseOnOffAttribute, parseTableMeasurementValue, selectAlternateContentBranch } from "./xmlParser.js";
|
|
18
|
+
import { PARSE_WARNING_CODES } from "@stll/docx-core/model";
|
|
17
19
|
//#region src/docx/tableParser.ts
|
|
18
20
|
/**
|
|
19
21
|
* Sanity cap on `w:gridSpan` (and the derived table column count). Word's
|
|
@@ -556,6 +558,12 @@ function parseTableCellVerticalMergeRevisionValue(value) {
|
|
|
556
558
|
if (value === "cont") return "continue";
|
|
557
559
|
if (value === "rest") return "rest";
|
|
558
560
|
}
|
|
561
|
+
function isTableCellMergeRevisionValue(value) {
|
|
562
|
+
return value === "continue" || value === "rest";
|
|
563
|
+
}
|
|
564
|
+
function isTableCellMergeRevisionContinuation(value) {
|
|
565
|
+
return value === "continue";
|
|
566
|
+
}
|
|
559
567
|
const ROW_PROPERTY_HANDLERS = {
|
|
560
568
|
cnfStyle: (child, formatting) => {
|
|
561
569
|
const conditionalFormat = parseConditionalFormatStyle(child);
|
|
@@ -755,13 +763,35 @@ function parseTableCellProperties(tcPrElement) {
|
|
|
755
763
|
if (preserved) formatting.preserved = preserved;
|
|
756
764
|
return withSourceXml(formatting, tcPrElement);
|
|
757
765
|
}
|
|
766
|
+
const createCustomXmlWrapper = (element, identity) => ({
|
|
767
|
+
id: identity.next++,
|
|
768
|
+
...blockCustomXmlShell(element)
|
|
769
|
+
});
|
|
770
|
+
const recordCustomXmlWrapper = (wrapped, wrapper) => {
|
|
771
|
+
for (const item of wrapped) item.carrierStack = [{
|
|
772
|
+
type: "customXml",
|
|
773
|
+
wrapper
|
|
774
|
+
}, ...item.carrierStack ?? []];
|
|
775
|
+
};
|
|
758
776
|
const recordContentControl = (wrapped, carriers, sdtElement) => {
|
|
759
777
|
const properties = parseSdtProperties(findWordprocessingChild(sdtElement, "sdtPr"), findWordprocessingChild(sdtElement, "sdtEndPr"));
|
|
760
778
|
const siblings = captureSdtSiblingMarkers(sdtElement);
|
|
761
779
|
if (siblings.before.length > 0) properties.rawSdtChildrenBeforeContent = siblings.before;
|
|
762
780
|
if (siblings.after.length > 0) properties.rawSdtChildrenAfterContent = siblings.after;
|
|
763
|
-
for (const item of wrapped)
|
|
764
|
-
|
|
781
|
+
for (const item of wrapped) {
|
|
782
|
+
item.contentControls = [properties, ...item.contentControls ?? []];
|
|
783
|
+
item.carrierStack = [{
|
|
784
|
+
type: "sdt",
|
|
785
|
+
properties
|
|
786
|
+
}, ...item.carrierStack ?? []];
|
|
787
|
+
}
|
|
788
|
+
for (const item of carriers) {
|
|
789
|
+
item.contentControls = [properties, ...item.contentControls ?? []];
|
|
790
|
+
item.carrierStack = [{
|
|
791
|
+
type: "sdt",
|
|
792
|
+
properties
|
|
793
|
+
}, ...item.carrierStack ?? []];
|
|
794
|
+
}
|
|
765
795
|
};
|
|
766
796
|
/**
|
|
767
797
|
* Accumulate a table container's own `xmlns:*` onto the inherited in-scope set,
|
|
@@ -779,7 +809,7 @@ const findLastFlowBlock = (blocks) => {
|
|
|
779
809
|
for (let index = blocks.length - 1; index >= 0; index -= 1) {
|
|
780
810
|
const block = blocks[index];
|
|
781
811
|
if (block?.type === "paragraph" || block?.type === "table") return block;
|
|
782
|
-
if (block?.type === "blockSdt") {
|
|
812
|
+
if (block?.type === "blockSdt" || block?.type === "blockCustomXml") {
|
|
783
813
|
const nested = findLastFlowBlock(block.content);
|
|
784
814
|
if (nested) return nested;
|
|
785
815
|
}
|
|
@@ -825,6 +855,19 @@ const CELL_CONTENT_HANDLERS = {
|
|
|
825
855
|
})
|
|
826
856
|
});
|
|
827
857
|
},
|
|
858
|
+
customXml: (child, { resources, options, modelled }) => {
|
|
859
|
+
const customXml = {
|
|
860
|
+
type: "blockCustomXml",
|
|
861
|
+
...blockCustomXmlShell(child),
|
|
862
|
+
content: parseCellChildren({
|
|
863
|
+
element: child,
|
|
864
|
+
resources,
|
|
865
|
+
options: withContainerXmlns(options, child),
|
|
866
|
+
requireTrailingParagraph: false
|
|
867
|
+
})
|
|
868
|
+
};
|
|
869
|
+
modelled.push(customXml);
|
|
870
|
+
},
|
|
828
871
|
bookmarkStart: (child, { modelled }) => {
|
|
829
872
|
modelled.push(parseBookmarkStart(child));
|
|
830
873
|
},
|
|
@@ -836,7 +879,6 @@ const CELL_CONTENT_HANDLERS = {
|
|
|
836
879
|
altChunk: CAPTURE,
|
|
837
880
|
commentRangeEnd: CAPTURE,
|
|
838
881
|
commentRangeStart: CAPTURE,
|
|
839
|
-
customXml: CAPTURE,
|
|
840
882
|
customXmlDelRangeEnd: CAPTURE,
|
|
841
883
|
customXmlDelRangeStart: CAPTURE,
|
|
842
884
|
customXmlInsRangeEnd: CAPTURE,
|
|
@@ -959,6 +1001,21 @@ const ROW_CONTENT_HANDLERS = {
|
|
|
959
1001
|
}
|
|
960
1002
|
recordContentControl(wrapped, [...preservedChildren.slice(firstCaptured), ...bookmarks.slice(firstBookmark)], child);
|
|
961
1003
|
},
|
|
1004
|
+
customXml: (child, walk) => {
|
|
1005
|
+
const firstWrapped = walk.row.cells.length;
|
|
1006
|
+
const firstCaptured = walk.preservedChildren.length;
|
|
1007
|
+
const firstBookmark = walk.bookmarks.length;
|
|
1008
|
+
dispatchRowChildren(child, walk);
|
|
1009
|
+
if (walk.row.cells.length === firstWrapped) {
|
|
1010
|
+
walk.preservedChildren.splice(firstCaptured);
|
|
1011
|
+
walk.bookmarks.splice(firstBookmark);
|
|
1012
|
+
return CAPTURE;
|
|
1013
|
+
}
|
|
1014
|
+
const wrapper = createCustomXmlWrapper(child, walk.customXmlIdentity);
|
|
1015
|
+
recordCustomXmlWrapper(walk.row.cells.slice(firstWrapped), wrapper);
|
|
1016
|
+
recordCustomXmlWrapper(walk.preservedChildren.slice(firstCaptured), wrapper);
|
|
1017
|
+
recordCustomXmlWrapper(walk.bookmarks.slice(firstBookmark), wrapper);
|
|
1018
|
+
},
|
|
962
1019
|
bookmarkStart: (child, { row, bookmarks }) => {
|
|
963
1020
|
bookmarks.push({
|
|
964
1021
|
index: row.cells.length,
|
|
@@ -974,7 +1031,7 @@ const ROW_CONTENT_HANDLERS = {
|
|
|
974
1031
|
...ROW_CHILD_OWNERS,
|
|
975
1032
|
commentRangeEnd: CAPTURE,
|
|
976
1033
|
commentRangeStart: CAPTURE,
|
|
977
|
-
|
|
1034
|
+
customXmlPr: CAPTURE,
|
|
978
1035
|
customXmlDelRangeEnd: CAPTURE,
|
|
979
1036
|
customXmlDelRangeStart: CAPTURE,
|
|
980
1037
|
customXmlInsRangeEnd: CAPTURE,
|
|
@@ -994,7 +1051,13 @@ const ROW_CONTENT_HANDLERS = {
|
|
|
994
1051
|
permEnd: CAPTURE,
|
|
995
1052
|
permStart: CAPTURE,
|
|
996
1053
|
proofErr: CAPTURE,
|
|
997
|
-
tr:
|
|
1054
|
+
tr: (child, { options }) => {
|
|
1055
|
+
options.context?.warn({
|
|
1056
|
+
code: PARSE_WARNING_CODES.nestedRowOpaque,
|
|
1057
|
+
element: child.name ?? "w:tr"
|
|
1058
|
+
});
|
|
1059
|
+
return CAPTURE;
|
|
1060
|
+
}
|
|
998
1061
|
};
|
|
999
1062
|
/**
|
|
1000
1063
|
* One row's children, or a cell-level content control's.
|
|
@@ -1055,7 +1118,8 @@ function parseTableRow(trElement, styles, theme, numbering, rels, media, options
|
|
|
1055
1118
|
options: withContainerXmlns(options, trElement),
|
|
1056
1119
|
row,
|
|
1057
1120
|
bookmarks,
|
|
1058
|
-
preservedChildren
|
|
1121
|
+
preservedChildren,
|
|
1122
|
+
customXmlIdentity: { next: 0 }
|
|
1059
1123
|
});
|
|
1060
1124
|
if (preservedChildren.length > 0) row.preserved = { children: preservedChildren };
|
|
1061
1125
|
if (bookmarks.length > 0) row.bookmarks = bookmarks;
|
|
@@ -1167,6 +1231,21 @@ const TABLE_CONTENT_HANDLERS = {
|
|
|
1167
1231
|
}
|
|
1168
1232
|
recordContentControl(wrapped, [...preservedChildren.slice(firstCaptured), ...bookmarks.slice(firstBookmark)], child);
|
|
1169
1233
|
},
|
|
1234
|
+
customXml: (child, walk) => {
|
|
1235
|
+
const firstWrapped = walk.table.rows.length;
|
|
1236
|
+
const firstCaptured = walk.preservedChildren.length;
|
|
1237
|
+
const firstBookmark = walk.bookmarks.length;
|
|
1238
|
+
dispatchTableChildren(child, walk);
|
|
1239
|
+
if (walk.table.rows.length === firstWrapped) {
|
|
1240
|
+
walk.preservedChildren.splice(firstCaptured);
|
|
1241
|
+
walk.bookmarks.splice(firstBookmark);
|
|
1242
|
+
return CAPTURE;
|
|
1243
|
+
}
|
|
1244
|
+
const wrapper = createCustomXmlWrapper(child, walk.customXmlIdentity);
|
|
1245
|
+
recordCustomXmlWrapper(walk.table.rows.slice(firstWrapped), wrapper);
|
|
1246
|
+
recordCustomXmlWrapper(walk.preservedChildren.slice(firstCaptured), wrapper);
|
|
1247
|
+
recordCustomXmlWrapper(walk.bookmarks.slice(firstBookmark), wrapper);
|
|
1248
|
+
},
|
|
1170
1249
|
...TABLE_CHILD_OWNERS,
|
|
1171
1250
|
bookmarkStart: (child, { table, bookmarks }) => {
|
|
1172
1251
|
bookmarks.push({
|
|
@@ -1182,7 +1261,6 @@ const TABLE_CONTENT_HANDLERS = {
|
|
|
1182
1261
|
},
|
|
1183
1262
|
commentRangeEnd: CAPTURE,
|
|
1184
1263
|
commentRangeStart: CAPTURE,
|
|
1185
|
-
customXml: CAPTURE,
|
|
1186
1264
|
customXmlPr: CAPTURE,
|
|
1187
1265
|
customXmlDelRangeEnd: CAPTURE,
|
|
1188
1266
|
customXmlDelRangeStart: CAPTURE,
|
|
@@ -1269,7 +1347,8 @@ function parseTable(tblElement, styles, theme, numbering, rels, media, options)
|
|
|
1269
1347
|
table,
|
|
1270
1348
|
rowsWithGridOffsets,
|
|
1271
1349
|
bookmarks,
|
|
1272
|
-
preservedChildren
|
|
1350
|
+
preservedChildren,
|
|
1351
|
+
customXmlIdentity: { next: 0 }
|
|
1273
1352
|
});
|
|
1274
1353
|
if (table.rows.length === 0) return;
|
|
1275
1354
|
if (preservedChildren.length > 0) table.preserved = { children: preservedChildren };
|
|
@@ -1382,4 +1461,4 @@ function isFloatingTable(table) {
|
|
|
1382
1461
|
return table.formatting?.floating !== void 0;
|
|
1383
1462
|
}
|
|
1384
1463
|
//#endregion
|
|
1385
|
-
export { MAX_TABLE_COLUMNS, getHeaderRows, getTableColumnCount, getTableRowCount, getTableText, hasHeaderRow, isCellHorizontallyMerged, isCellMergeContinuation, isCellMergeStart, isFloatingTable, parseCellMargins, parseConditionalFormatStyle, parseFloatingTableProperties, parseTable, parseTableBorders, parseTableCell, parseTableCellProperties, parseTableGrid, parseTableLook, parseTableMeasurement, parseTableProperties, parseTablePropertyExceptions, parseTableRow, parseTableRowProperties };
|
|
1464
|
+
export { CELL_CONTENT_HANDLERS, MAX_TABLE_COLUMNS, ROW_CONTENT_HANDLERS, TABLE_CONTENT_HANDLERS, getHeaderRows, getTableColumnCount, getTableRowCount, getTableText, hasHeaderRow, isCellHorizontallyMerged, isCellMergeContinuation, isCellMergeStart, isFloatingTable, isTableCellMergeRevisionContinuation, isTableCellMergeRevisionValue, parseCellMargins, parseConditionalFormatStyle, parseFloatingTableProperties, parseTable, parseTableBorders, parseTableCell, parseTableCellProperties, parseTableGrid, parseTableLook, parseTableMeasurement, parseTableProperties, parseTablePropertyExceptions, parseTableRow, parseTableRowProperties };
|
|
@@ -300,7 +300,7 @@ const getTextBoxBlockText = (block) => {
|
|
|
300
300
|
return runTexts.join("");
|
|
301
301
|
}
|
|
302
302
|
if (block.type === "table") return block.rows.map((row) => row.cells.map((cell) => cell.content.map(getTextBoxBlockText).join("\n")).join(" ")).join("\n");
|
|
303
|
-
if (block.type !== "blockSdt") return "";
|
|
303
|
+
if (block.type !== "blockSdt" && block.type !== "blockCustomXml") return "";
|
|
304
304
|
return block.content.map(getTextBoxBlockText).join("\n");
|
|
305
305
|
};
|
|
306
306
|
/**
|
package/dist/docx/unzip.d.ts
CHANGED
|
@@ -82,6 +82,7 @@ type RawDocxContent = {
|
|
|
82
82
|
* @returns Promise resolving to extracted content
|
|
83
83
|
*/
|
|
84
84
|
declare function unzipDocx(buffer: ArrayBuffer, options?: DocxUnzipOptions): Promise<RawDocxContent>;
|
|
85
|
+
declare function getEntryUncompressedSize(file: JSZip.JSZipObject): number | null;
|
|
85
86
|
/**
|
|
86
87
|
* Get a list of all files in the DOCX
|
|
87
88
|
*
|
|
@@ -142,4 +143,4 @@ declare function getContentSummary(content: RawDocxContent): {
|
|
|
142
143
|
totalFiles: number;
|
|
143
144
|
};
|
|
144
145
|
//#endregion
|
|
145
|
-
export { DocxSecurityError, DocxUnzipLimits, DocxUnzipOptions, RawDocxContent, extractFile, getContentSummary, getFileList, getMediaMimeType, hasFile, mediaToDataUrl, unzipDocx };
|
|
146
|
+
export { DocxSecurityError, DocxUnzipLimits, DocxUnzipOptions, RawDocxContent, extractFile, getContentSummary, getEntryUncompressedSize, getFileList, getMediaMimeType, hasFile, mediaToDataUrl, unzipDocx };
|
package/dist/docx/unzip.js
CHANGED
|
@@ -494,4 +494,4 @@ function getContentSummary(content) {
|
|
|
494
494
|
};
|
|
495
495
|
}
|
|
496
496
|
//#endregion
|
|
497
|
-
export { DocxSecurityError, extractFile, getContentSummary, getFileList, getMediaMimeType, hasFile, mediaToDataUrl, unzipDocx };
|
|
497
|
+
export { DocxSecurityError, extractFile, getContentSummary, getEntryUncompressedSize, getFileList, getMediaMimeType, hasFile, mediaToDataUrl, unzipDocx };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
//#region src/docx/wordprocessingPrefixes.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Which prefixes a WordprocessingML part spells its namespaces with.
|
|
4
|
+
*
|
|
5
|
+
* A prefix is an alias: `<w:p>`, `<x:p>` and an unprefixed `<p>` under a
|
|
6
|
+
* default namespace are the same paragraph when the prefix (or the default)
|
|
7
|
+
* is bound to the WordprocessingML URI. The parser resolves names by URI, so
|
|
8
|
+
* every such package opens. The patchers that work on part XML as a string
|
|
9
|
+
* (`ensureParaIds`, the selective-save splices) find elements by their
|
|
10
|
+
* literal tags instead, and a literal `<w:p` finds nothing in a part that
|
|
11
|
+
* spells it `<x:p`: the patch reports success having touched nothing.
|
|
12
|
+
*
|
|
13
|
+
* {@link resolveWordprocessingPrefixes} is the one place those patchers ask
|
|
14
|
+
* how a part spells WordprocessingML, the `w14` extensions and
|
|
15
|
+
* markup compatibility (`mc`). A patcher either scans with every prefix the
|
|
16
|
+
* part binds, or checks the names its literal scan needs in their namespace
|
|
17
|
+
* scope. A prefix conflict on one of those names is refused.
|
|
18
|
+
*/
|
|
19
|
+
/** How one part spells the namespaces a string-level patcher scans for. */
|
|
20
|
+
type WordprocessingPrefixes = {
|
|
21
|
+
/**
|
|
22
|
+
* Prefixes bound to WordprocessingML (Transitional or Strict), `""` for the
|
|
23
|
+
* default namespace. Never empty: an undeclared part reads as `w`.
|
|
24
|
+
*/
|
|
25
|
+
main: readonly string[];
|
|
26
|
+
/** Prefixes bound to the `w14` extensions namespace. */
|
|
27
|
+
w14: readonly string[];
|
|
28
|
+
/** Whether a `w14` prefix is declared on the root (else `w14` is assumed). */
|
|
29
|
+
w14Declared: boolean;
|
|
30
|
+
/** Prefixes bound to markup compatibility (`mc`). */
|
|
31
|
+
mc: readonly string[];
|
|
32
|
+
/** Whether an `mc` prefix is declared on the root (else `mc` is assumed). */
|
|
33
|
+
mcDeclared: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The spelling folio's serializer writes: WordprocessingML only as `w`,
|
|
36
|
+
* `w14` and `mc` under their conventional prefixes. Splicing serializer
|
|
37
|
+
* output into a part is only sound when this holds.
|
|
38
|
+
*/
|
|
39
|
+
canonical: boolean;
|
|
40
|
+
};
|
|
41
|
+
type WordprocessingPrefixResolution = {
|
|
42
|
+
type: "resolved";
|
|
43
|
+
prefixes: WordprocessingPrefixes;
|
|
44
|
+
} | {
|
|
45
|
+
type: "unsupported";
|
|
46
|
+
reason: string;
|
|
47
|
+
};
|
|
48
|
+
declare const resolveWordprocessingPrefixes: (xml: string) => WordprocessingPrefixResolution;
|
|
49
|
+
/** Root spelling for a selective scan whose relevant nested names are checked separately. */
|
|
50
|
+
declare const resolveSelectiveScanPrefixes: (xml: string) => WordprocessingPrefixResolution;
|
|
51
|
+
/**
|
|
52
|
+
* Whether a patcher that reads `<w:…>` literally sees every element it looks
|
|
53
|
+
* for in `xml`, and may splice the serializer's `w:` markup into it.
|
|
54
|
+
*
|
|
55
|
+
* True for the canonical spelling. Also true when the part binds
|
|
56
|
+
* WordprocessingML under an extra prefix besides `w` but never spells the
|
|
57
|
+
* names the patcher scans under that alias, or binds markup compatibility
|
|
58
|
+
* under another prefix. Relevant nested names are checked in their own scope.
|
|
59
|
+
*/
|
|
60
|
+
declare const splicesAsCanonical: (xml: string, localNames: readonly string[]) => boolean;
|
|
61
|
+
/** The names the paragraph splices scan for: paragraphs, their containers and ids. */
|
|
62
|
+
declare const PARAGRAPH_SCAN_NAMES: readonly ["p", "tc", "txbxContent", "paraId", "textId"];
|
|
63
|
+
/** Whether `xml` spells WordprocessingML the way folio's serializer does. */
|
|
64
|
+
declare const hasCanonicalWordprocessingPrefixes: (xml: string) => boolean;
|
|
65
|
+
/** `<p`, `<w:p`, … — the open-tag literal of `localName` under `prefix`. */
|
|
66
|
+
declare const openTagLiteral: (prefix: string, localName: string) => string;
|
|
67
|
+
/** `</p>`, `</w:p>`, … — the close-tag literal of `localName` under `prefix`. */
|
|
68
|
+
declare const closeTagLiteral: (prefix: string, localName: string) => string;
|
|
69
|
+
/**
|
|
70
|
+
* Whether `xml` opens `localName` under one of `prefixes` at `start`, and the
|
|
71
|
+
* length of the literal that matched (`-1` when none did). The character after
|
|
72
|
+
* the name must end it, so `<w:pPr` is not `<w:p`.
|
|
73
|
+
*/
|
|
74
|
+
declare const matchOpenTag: (xml: string, start: number, prefixes: readonly string[], localName: string) => number;
|
|
75
|
+
/** The length of the close tag of `localName` under one of `prefixes` at `start`, or -1. */
|
|
76
|
+
declare const matchCloseTag: (xml: string, start: number, prefixes: readonly string[], localName: string) => number;
|
|
77
|
+
//#endregion
|
|
78
|
+
export { PARAGRAPH_SCAN_NAMES, WordprocessingPrefixResolution, WordprocessingPrefixes, closeTagLiteral, hasCanonicalWordprocessingPrefixes, matchCloseTag, matchOpenTag, openTagLiteral, resolveSelectiveScanPrefixes, resolveWordprocessingPrefixes, splicesAsCanonical };
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { NAMESPACES, WORDPROCESSINGML_NAMESPACE_URIS, getLocalName, getNamespacePrefix, getNamespaceUri, parseXmlDocument, resolveAttributeNamespaceUri } from "./xmlParser.js";
|
|
2
|
+
//#region src/docx/wordprocessingPrefixes.ts
|
|
3
|
+
/**
|
|
4
|
+
* Which prefixes a WordprocessingML part spells its namespaces with.
|
|
5
|
+
*
|
|
6
|
+
* A prefix is an alias: `<w:p>`, `<x:p>` and an unprefixed `<p>` under a
|
|
7
|
+
* default namespace are the same paragraph when the prefix (or the default)
|
|
8
|
+
* is bound to the WordprocessingML URI. The parser resolves names by URI, so
|
|
9
|
+
* every such package opens. The patchers that work on part XML as a string
|
|
10
|
+
* (`ensureParaIds`, the selective-save splices) find elements by their
|
|
11
|
+
* literal tags instead, and a literal `<w:p` finds nothing in a part that
|
|
12
|
+
* spells it `<x:p`: the patch reports success having touched nothing.
|
|
13
|
+
*
|
|
14
|
+
* {@link resolveWordprocessingPrefixes} is the one place those patchers ask
|
|
15
|
+
* how a part spells WordprocessingML, the `w14` extensions and
|
|
16
|
+
* markup compatibility (`mc`). A patcher either scans with every prefix the
|
|
17
|
+
* part binds, or checks the names its literal scan needs in their namespace
|
|
18
|
+
* scope. A prefix conflict on one of those names is refused.
|
|
19
|
+
*/
|
|
20
|
+
const XMLNS = "xmlns";
|
|
21
|
+
const isWhitespace = (character) => character === " " || character === " " || character === "\n" || character === "\r";
|
|
22
|
+
/** End offset (exclusive) of the tag opening at `start`, skipping quoted values. */
|
|
23
|
+
const tagEnd = (xml, start) => {
|
|
24
|
+
let quote = null;
|
|
25
|
+
for (let index = start + 1; index < xml.length; index += 1) {
|
|
26
|
+
const character = xml[index];
|
|
27
|
+
if (quote !== null) {
|
|
28
|
+
if (character === quote) quote = null;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (character === "\"" || character === "'") quote = character;
|
|
32
|
+
else if (character === ">") return index + 1;
|
|
33
|
+
}
|
|
34
|
+
return -1;
|
|
35
|
+
};
|
|
36
|
+
/** The `xmlns` / `xmlns:*` declarations written in one start tag. */
|
|
37
|
+
const declarationsIn = (tag) => {
|
|
38
|
+
const declarations = [];
|
|
39
|
+
for (const match of tag.matchAll(/\sxmlns(?::(?<prefix>[^\s=/>]+))?\s*=\s*(?<quote>["'])(?<uri>[\s\S]*?)\k<quote>/gu)) declarations.push({
|
|
40
|
+
prefix: match.groups?.["prefix"] ?? "",
|
|
41
|
+
uri: match.groups?.["uri"] ?? ""
|
|
42
|
+
});
|
|
43
|
+
return declarations;
|
|
44
|
+
};
|
|
45
|
+
const OPAQUE_REGIONS = [
|
|
46
|
+
{
|
|
47
|
+
open: "<!--",
|
|
48
|
+
close: "-->"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
open: "<![CDATA[",
|
|
52
|
+
close: "]]>"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
open: "<?",
|
|
56
|
+
close: "?>"
|
|
57
|
+
}
|
|
58
|
+
];
|
|
59
|
+
/** Visit every start tag of `xml` that declares a namespace, root first. */
|
|
60
|
+
const visitDeclaringStartTags = (xml, visit) => {
|
|
61
|
+
let seenRoot = false;
|
|
62
|
+
let pos = 0;
|
|
63
|
+
scan: while (pos < xml.length) {
|
|
64
|
+
const start = xml.indexOf("<", pos);
|
|
65
|
+
if (start === -1) break;
|
|
66
|
+
for (const { open, close } of OPAQUE_REGIONS) if (xml.startsWith(open, start)) {
|
|
67
|
+
const closeStart = xml.indexOf(close, start + open.length);
|
|
68
|
+
if (closeStart === -1) return false;
|
|
69
|
+
pos = closeStart + close.length;
|
|
70
|
+
continue scan;
|
|
71
|
+
}
|
|
72
|
+
const next = xml[start + 1];
|
|
73
|
+
if (next === "!" || next === "/") {
|
|
74
|
+
const end = xml.indexOf(">", start);
|
|
75
|
+
if (end === -1) return false;
|
|
76
|
+
pos = end + 1;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const end = tagEnd(xml, start);
|
|
80
|
+
if (end === -1) return false;
|
|
81
|
+
const isRoot = !seenRoot;
|
|
82
|
+
seenRoot = true;
|
|
83
|
+
const tag = xml.slice(start, end);
|
|
84
|
+
if (isRoot || tag.includes(XMLNS)) visit(tag, isRoot);
|
|
85
|
+
pos = end;
|
|
86
|
+
}
|
|
87
|
+
return seenRoot;
|
|
88
|
+
};
|
|
89
|
+
const MAIN_URIS = WORDPROCESSINGML_NAMESPACE_URIS;
|
|
90
|
+
const W14_URI = NAMESPACES.w14;
|
|
91
|
+
const MC_URI = NAMESPACES.mc;
|
|
92
|
+
const NESTED_BINDINGS = {
|
|
93
|
+
strict: "strict",
|
|
94
|
+
scoped: "scoped"
|
|
95
|
+
};
|
|
96
|
+
const slotOf = (uri) => {
|
|
97
|
+
if (MAIN_URIS.has(uri)) return "main";
|
|
98
|
+
if (uri === W14_URI) return "w14";
|
|
99
|
+
if (uri === MC_URI) return "mc";
|
|
100
|
+
return null;
|
|
101
|
+
};
|
|
102
|
+
const CONVENTIONAL_PREFIX = {
|
|
103
|
+
main: "w",
|
|
104
|
+
w14: "w14",
|
|
105
|
+
mc: "mc"
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Resolve the prefixes `xml` binds to WordprocessingML, `w14` and `mc`.
|
|
109
|
+
*
|
|
110
|
+
* Root declarations decide. A namespace the root never declares is read under
|
|
111
|
+
* its conventional prefix, the tolerant reading of a malformed part. A declaration on
|
|
112
|
+
* a nested element is accepted only when it repeats a binding the scan already
|
|
113
|
+
* uses; anything else changes what a literal tag means partway through the
|
|
114
|
+
* part and is reported as unsupported.
|
|
115
|
+
*/
|
|
116
|
+
const resolvePrefixes = (xml, nestedBindings) => {
|
|
117
|
+
const bound = {
|
|
118
|
+
main: [],
|
|
119
|
+
w14: [],
|
|
120
|
+
mc: []
|
|
121
|
+
};
|
|
122
|
+
const rootPrefixes = /* @__PURE__ */ new Map();
|
|
123
|
+
const nested = [];
|
|
124
|
+
if (!visitDeclaringStartTags(xml, (tag, isRoot) => {
|
|
125
|
+
for (const declaration of declarationsIn(tag)) {
|
|
126
|
+
if (!isRoot) {
|
|
127
|
+
nested.push(declaration);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
rootPrefixes.set(declaration.prefix, declaration.uri);
|
|
131
|
+
const slot = slotOf(declaration.uri);
|
|
132
|
+
if (slot !== null) bound[slot].push(declaration.prefix);
|
|
133
|
+
}
|
|
134
|
+
})) return {
|
|
135
|
+
type: "unsupported",
|
|
136
|
+
reason: "no root element or unterminated markup"
|
|
137
|
+
};
|
|
138
|
+
const declared = {
|
|
139
|
+
main: bound.main.length > 0,
|
|
140
|
+
w14: bound.w14.length > 0,
|
|
141
|
+
mc: bound.mc.length > 0
|
|
142
|
+
};
|
|
143
|
+
for (const slot of [
|
|
144
|
+
"main",
|
|
145
|
+
"w14",
|
|
146
|
+
"mc"
|
|
147
|
+
]) {
|
|
148
|
+
if (declared[slot]) {
|
|
149
|
+
if (slot !== "main" && bound[slot].includes("")) return {
|
|
150
|
+
type: "unsupported",
|
|
151
|
+
reason: `${slot} is bound only as the default namespace`
|
|
152
|
+
};
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const conventional = CONVENTIONAL_PREFIX[slot];
|
|
156
|
+
if (rootPrefixes.has(conventional)) {
|
|
157
|
+
if (slot === "main") return {
|
|
158
|
+
type: "unsupported",
|
|
159
|
+
reason: `no WordprocessingML binding; ${conventional} is bound to another namespace`
|
|
160
|
+
};
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
bound[slot].push(conventional);
|
|
164
|
+
}
|
|
165
|
+
const inUse = /* @__PURE__ */ new Map();
|
|
166
|
+
for (const slot of [
|
|
167
|
+
"main",
|
|
168
|
+
"w14",
|
|
169
|
+
"mc"
|
|
170
|
+
]) for (const prefix of bound[slot]) inUse.set(prefix, slot);
|
|
171
|
+
for (const { prefix, uri } of nestedBindings === NESTED_BINDINGS.strict ? nested : []) {
|
|
172
|
+
const slot = slotOf(uri);
|
|
173
|
+
const spelling = prefix === "" ? "the default namespace" : `prefix ${prefix}`;
|
|
174
|
+
if (slot !== null) {
|
|
175
|
+
if (!bound[slot].includes(prefix)) return {
|
|
176
|
+
type: "unsupported",
|
|
177
|
+
reason: `a nested element binds ${slot} under ${spelling}`
|
|
178
|
+
};
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (inUse.has(prefix)) return {
|
|
182
|
+
type: "unsupported",
|
|
183
|
+
reason: `a nested element rebinds ${spelling} to ${uri === "" ? "no namespace" : uri}`
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const only = (prefixes, prefix) => prefixes.length === 1 && prefixes[0] === prefix;
|
|
187
|
+
const canonical = only(bound.main, "w") && only(bound.w14, "w14") && only(bound.mc, "mc");
|
|
188
|
+
return {
|
|
189
|
+
type: "resolved",
|
|
190
|
+
prefixes: {
|
|
191
|
+
main: bound.main,
|
|
192
|
+
w14: bound.w14,
|
|
193
|
+
w14Declared: declared.w14,
|
|
194
|
+
mc: bound.mc,
|
|
195
|
+
mcDeclared: declared.mc,
|
|
196
|
+
canonical
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
const resolveWordprocessingPrefixes = (xml) => resolvePrefixes(xml, NESTED_BINDINGS.strict);
|
|
201
|
+
/** Root spelling for a selective scan whose relevant nested names are checked separately. */
|
|
202
|
+
const resolveSelectiveScanPrefixes = (xml) => resolvePrefixes(xml, NESTED_BINDINGS.scoped);
|
|
203
|
+
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
204
|
+
/**
|
|
205
|
+
* Whether a patcher that reads `<w:…>` literally sees every element it looks
|
|
206
|
+
* for in `xml`, and may splice the serializer's `w:` markup into it.
|
|
207
|
+
*
|
|
208
|
+
* True for the canonical spelling. Also true when the part binds
|
|
209
|
+
* WordprocessingML under an extra prefix besides `w` but never spells the
|
|
210
|
+
* names the patcher scans under that alias, or binds markup compatibility
|
|
211
|
+
* under another prefix. Relevant nested names are checked in their own scope.
|
|
212
|
+
*/
|
|
213
|
+
const splicesAsCanonical = (xml, localNames) => {
|
|
214
|
+
const resolution = resolveWordprocessingPrefixes(xml);
|
|
215
|
+
if (resolution.type === "resolved" && resolution.prefixes.canonical) return true;
|
|
216
|
+
const relaxed = resolveSelectiveScanPrefixes(xml);
|
|
217
|
+
if (relaxed.type !== "resolved") return false;
|
|
218
|
+
const { prefixes } = relaxed;
|
|
219
|
+
const only = (list, prefix) => list.length === 1 && list[0] === prefix;
|
|
220
|
+
const aliases = prefixes.main.filter((prefix) => prefix !== "w");
|
|
221
|
+
if (!prefixes.main.includes("w") || aliases.includes("") || !only(prefixes.w14, "w14") || prefixes.mc.length === 0) return false;
|
|
222
|
+
const names = localNames.map(escapeRegExp).join("|");
|
|
223
|
+
if (!aliases.every((alias) => !new RegExp(`[<\\s/]${escapeRegExp(alias)}:(?:${names})[\\s/>=]`, "u").test(xml))) return false;
|
|
224
|
+
const root = parseXmlDocument(xml);
|
|
225
|
+
if (!root) return false;
|
|
226
|
+
const mainNames = new Set(localNames.filter((name) => name !== "paraId" && name !== "textId"));
|
|
227
|
+
const idNames = /* @__PURE__ */ new Set(["paraId", "textId"]);
|
|
228
|
+
const mcNames = /* @__PURE__ */ new Set(["Fallback", "AlternateContent"]);
|
|
229
|
+
const visit = (element) => {
|
|
230
|
+
if (element.type !== "element") return true;
|
|
231
|
+
const name = element.name ?? "";
|
|
232
|
+
const local = getLocalName(name);
|
|
233
|
+
const prefix = getNamespacePrefix(name) ?? "";
|
|
234
|
+
const uri = getNamespaceUri(element) ?? "";
|
|
235
|
+
if (prefix === "mc" && uri !== MC_URI || mainNames.has(local) && WORDPROCESSINGML_NAMESPACE_URIS.has(uri) !== (prefix === "w") || mcNames.has(local) && uri === MC_URI !== prefixes.mc.includes(prefix)) return false;
|
|
236
|
+
for (const attribute of Object.keys(element.attributes ?? {})) {
|
|
237
|
+
const attributePrefix = getNamespacePrefix(attribute);
|
|
238
|
+
if (attributePrefix === "mc" && resolveAttributeNamespaceUri(element, attribute) !== MC_URI) return false;
|
|
239
|
+
if (!idNames.has(getLocalName(attribute))) continue;
|
|
240
|
+
if (resolveAttributeNamespaceUri(element, attribute) === W14_URI !== (attributePrefix === "w14")) return false;
|
|
241
|
+
}
|
|
242
|
+
return (element.elements ?? []).every(visit);
|
|
243
|
+
};
|
|
244
|
+
return visit(root);
|
|
245
|
+
};
|
|
246
|
+
/** The names the paragraph splices scan for: paragraphs, their containers and ids. */
|
|
247
|
+
const PARAGRAPH_SCAN_NAMES = [
|
|
248
|
+
"p",
|
|
249
|
+
"tc",
|
|
250
|
+
"txbxContent",
|
|
251
|
+
"paraId",
|
|
252
|
+
"textId"
|
|
253
|
+
];
|
|
254
|
+
/** Whether `xml` spells WordprocessingML the way folio's serializer does. */
|
|
255
|
+
const hasCanonicalWordprocessingPrefixes = (xml) => {
|
|
256
|
+
const resolution = resolveWordprocessingPrefixes(xml);
|
|
257
|
+
return resolution.type === "resolved" && resolution.prefixes.canonical;
|
|
258
|
+
};
|
|
259
|
+
/** `<p`, `<w:p`, … — the open-tag literal of `localName` under `prefix`. */
|
|
260
|
+
const openTagLiteral = (prefix, localName) => prefix === "" ? `<${localName}` : `<${prefix}:${localName}`;
|
|
261
|
+
/** `</p>`, `</w:p>`, … — the close-tag literal of `localName` under `prefix`. */
|
|
262
|
+
const closeTagLiteral = (prefix, localName) => prefix === "" ? `</${localName}>` : `</${prefix}:${localName}>`;
|
|
263
|
+
/**
|
|
264
|
+
* Whether `xml` opens `localName` under one of `prefixes` at `start`, and the
|
|
265
|
+
* length of the literal that matched (`-1` when none did). The character after
|
|
266
|
+
* the name must end it, so `<w:pPr` is not `<w:p`.
|
|
267
|
+
*/
|
|
268
|
+
const matchOpenTag = (xml, start, prefixes, localName) => {
|
|
269
|
+
for (const prefix of prefixes) {
|
|
270
|
+
const literal = openTagLiteral(prefix, localName);
|
|
271
|
+
if (!xml.startsWith(literal, start)) continue;
|
|
272
|
+
const after = xml[start + literal.length];
|
|
273
|
+
if (after === ">" || after === "/" || isWhitespace(after)) return literal.length;
|
|
274
|
+
}
|
|
275
|
+
return -1;
|
|
276
|
+
};
|
|
277
|
+
/** The length of the close tag of `localName` under one of `prefixes` at `start`, or -1. */
|
|
278
|
+
const matchCloseTag = (xml, start, prefixes, localName) => {
|
|
279
|
+
for (const prefix of prefixes) {
|
|
280
|
+
const literal = closeTagLiteral(prefix, localName);
|
|
281
|
+
if (xml.startsWith(literal, start)) return literal.length;
|
|
282
|
+
}
|
|
283
|
+
return -1;
|
|
284
|
+
};
|
|
285
|
+
//#endregion
|
|
286
|
+
export { PARAGRAPH_SCAN_NAMES, closeTagLiteral, hasCanonicalWordprocessingPrefixes, matchCloseTag, matchOpenTag, openTagLiteral, resolveSelectiveScanPrefixes, resolveWordprocessingPrefixes, splicesAsCanonical };
|