@stll/folio-core 0.47.7 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/apply.js +565 -121
- package/dist/ai-edits/minimal-replacement.d.ts +64 -0
- package/dist/ai-edits/minimal-replacement.js +282 -0
- package/dist/controller/fontReadiness.js +3 -3
- package/dist/controller/hyphenationReadiness.d.ts +19 -0
- package/dist/controller/hyphenationReadiness.js +47 -0
- package/dist/controller/layoutPipeline.d.ts +3 -0
- package/dist/controller/layoutPipeline.js +70 -18
- package/dist/display-list/build/buildDisplayList.js +39 -12
- package/dist/display-list/build/pageFurniture.d.ts +7 -1
- package/dist/display-list/build/pageFurniture.js +21 -8
- package/dist/display-list/build/paragraphPrimitives.js +68 -11
- package/dist/display-list/build/tablePrimitives.js +10 -6
- package/dist/display-list/build/textBoxPrimitives.js +1 -1
- package/dist/display-list/build/unsupported.d.ts +2 -2
- package/dist/display-list/build/unsupported.js +0 -0
- package/dist/display-list/dom/renderDisplayListToDom.js +0 -1
- package/dist/docx/attributeRemainder.js +10 -2
- package/dist/docx/blockContentParser.d.ts +2 -0
- package/dist/docx/blockContentParser.js +79 -64
- package/dist/docx/bulletMarkers.d.ts +18 -2
- package/dist/docx/bulletMarkers.js +27 -2
- package/dist/docx/commentParser.d.ts +2 -1
- package/dist/docx/commentParser.js +57 -45
- package/dist/docx/containerChildren.d.ts +21 -8
- package/dist/docx/containerChildren.js +39 -9
- package/dist/docx/documentParser.d.ts +5 -1
- package/dist/docx/documentParser.js +8 -5
- package/dist/docx/documentSectionFacts.d.ts +22 -0
- package/dist/docx/documentSectionFacts.js +43 -0
- package/dist/docx/drawingGroupChildren.d.ts +60 -0
- package/dist/docx/drawingGroupChildren.js +176 -0
- package/dist/docx/drawingUtils.js +3 -0
- package/dist/docx/fieldParser.d.ts +8 -1
- package/dist/docx/fieldParser.js +47 -3
- package/dist/docx/fontTableParser.js +34 -27
- package/dist/docx/footnoteParser.d.ts +3 -2
- package/dist/docx/footnoteParser.js +15 -12
- package/dist/docx/groupDrawingParser.d.ts +9 -2
- package/dist/docx/groupDrawingParser.js +125 -37
- package/dist/docx/headerFooterParser.d.ts +7 -2
- package/dist/docx/headerFooterParser.js +15 -5
- package/dist/docx/hyperlinkParser.d.ts +48 -36
- package/dist/docx/hyperlinkParser.js +97 -82
- package/dist/docx/numberingParser.js +108 -84
- package/dist/docx/packageParts.d.ts +45 -1
- package/dist/docx/packageParts.js +93 -2
- package/dist/docx/paraIdAttribute.d.ts +3 -2
- package/dist/docx/paragraphParser.d.ts +5 -1
- package/dist/docx/paragraphParser.js +468 -365
- package/dist/docx/paragraphProperties.js +123 -109
- package/dist/docx/paragraphPropertySource.d.ts +6 -1
- package/dist/docx/paragraphPropertySource.js +9 -1
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +143 -10
- package/dist/docx/parser.d.ts +7 -1
- package/dist/docx/parser.js +33 -16
- package/dist/docx/previewBudget.d.ts +55 -33
- package/dist/docx/previewBudget.js +59 -65
- package/dist/docx/rezip.js +41 -58
- package/dist/docx/runParser.d.ts +4 -1
- package/dist/docx/runParser.js +182 -184
- package/dist/docx/sdtProperties.js +87 -84
- package/dist/docx/sectionParser.js +169 -160
- package/dist/docx/selectiveSaveFlags.d.ts +10 -3
- package/dist/docx/selectiveSaveFlags.js +1 -1
- package/dist/docx/selectiveXmlPatch.d.ts +56 -34
- package/dist/docx/selectiveXmlPatch.js +222 -115
- package/dist/docx/serializer/groupTextBoxWriteBack.d.ts +12 -0
- package/dist/docx/serializer/groupTextBoxWriteBack.js +101 -0
- package/dist/docx/serializer/paragraphSerializer.js +47 -42
- package/dist/docx/serializer/runSerializer.d.ts +5 -1
- package/dist/docx/serializer/runSerializer.js +4 -3
- package/dist/docx/serializer/settingsSerializer.js +19 -1
- package/dist/docx/settingsParser.js +8 -0
- package/dist/docx/shapeAlternateContent.d.ts +25 -0
- package/dist/docx/shapeAlternateContent.js +52 -0
- package/dist/docx/streamingXmlParser.d.ts +6 -2
- package/dist/docx/streamingXmlParser.js +12 -7
- package/dist/docx/styleParser.js +34 -8
- package/dist/docx/tableParser.d.ts +21 -12
- package/dist/docx/tableParser.js +518 -441
- package/dist/docx/textBoxParser.d.ts +18 -5
- package/dist/docx/textBoxParser.js +12 -6
- package/dist/docx/verbatimCapture.d.ts +10 -8
- package/dist/docx/verbatimCapture.js +45 -2
- package/dist/docx/vmlImageParser.d.ts +15 -13
- package/dist/docx/vmlImageParser.js +75 -60
- package/dist/docx/vmlPreview.d.ts +3 -3
- package/dist/docx/vmlPreview.js +3 -4
- package/dist/docx/xmlNamespaceContext.d.ts +9 -0
- package/dist/docx/xmlNamespaceContext.js +54 -0
- package/dist/docx/xmlParser.d.ts +6 -3
- package/dist/docx/xmlParser.js +11 -41
- package/dist/docx/xmlResourceLimits.d.ts +2 -1
- package/dist/docx/xmlResourceLimits.js +4 -1
- package/dist/fonts/headlessMeasure.js +0 -0
- package/dist/headless-layout.d.ts +11 -2
- package/dist/headless-layout.js +153 -107
- package/dist/internal/headlessRevisionResolution.js +50 -13
- package/dist/internal/paragraphFormattingSerialization.js +3 -3
- package/dist/layout-bridge/convert/endnoteLayout.d.ts +65 -0
- package/dist/layout-bridge/convert/endnoteLayout.js +227 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +19 -3
- package/dist/layout-bridge/convert/footnoteLayout.js +40 -31
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -7
- package/dist/layout-bridge/convert/headerFooterLayout.js +5 -20
- package/dist/layout-bridge/convert/paragraphMarkFormatting.d.ts +10 -0
- package/dist/layout-bridge/convert/paragraphMarkFormatting.js +59 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +29 -1
- package/dist/layout-bridge/convert/toFlowBlocks.js +284 -39
- package/dist/layout-bridge/engine/hitTest.js +5 -3
- package/dist/layout-bridge/engine/selectionRects.js +5 -3
- package/dist/layout-engine/anchorLayoutInCellCompatibility.d.ts +10 -0
- package/dist/layout-engine/anchorLayoutInCellCompatibility.js +22 -0
- package/dist/layout-engine/index.d.ts +2 -2
- package/dist/layout-engine/index.js +102 -14
- package/dist/layout-engine/justificationCompatibility.d.ts +1 -1
- package/dist/layout-engine/justificationCompatibility.js +9 -5
- package/dist/layout-engine/keep-together.d.ts +15 -6
- package/dist/layout-engine/keep-together.js +43 -16
- package/dist/layout-engine/layoutInstrumentation.d.ts +8 -2
- package/dist/layout-engine/layoutInstrumentation.js +8 -1
- package/dist/layout-engine/measure/advanceComposition.js +50 -7
- package/dist/layout-engine/measure/hyphenationDictionaries.d.ts +74 -0
- package/dist/layout-engine/measure/hyphenationDictionaries.js +138 -0
- package/dist/layout-engine/measure/hyphenationPreload.d.ts +11 -0
- package/dist/layout-engine/measure/hyphenationPreload.js +24 -0
- package/dist/layout-engine/measure/lineBreakProvider.d.ts +12 -2
- package/dist/layout-engine/measure/lineBreakProvider.js +30 -30
- package/dist/layout-engine/measure/lineBreaks.d.ts +6 -1
- package/dist/layout-engine/measure/lineBreaks.js +10 -3
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +9 -6
- package/dist/layout-engine/measure/listMarkerWidth.js +20 -22
- package/dist/layout-engine/measure/measureBlocks.js +46 -10
- package/dist/layout-engine/measure/measureContainer.js +51 -8
- package/dist/layout-engine/measure/measureHelpers.d.ts +15 -3
- package/dist/layout-engine/measure/measureHelpers.js +34 -8
- package/dist/layout-engine/measure/measureParagraph.js +143 -101
- package/dist/layout-engine/measure/measureTypes.d.ts +5 -0
- package/dist/layout-engine/measure/smallCapsCasing.d.ts +56 -0
- package/dist/layout-engine/measure/smallCapsCasing.js +88 -0
- package/dist/layout-engine/measure/tabCalculator.d.ts +2 -0
- package/dist/layout-engine/measure/tabCalculator.js +2 -1
- package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
- package/dist/layout-engine/measure/tableCellFloating.js +51 -19
- package/dist/layout-engine/measure/tableCellGrid.d.ts +28 -3
- package/dist/layout-engine/measure/tableCellGrid.js +45 -5
- package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +2 -1
- package/dist/layout-engine/noteAreaFlow.d.ts +19 -0
- package/dist/layout-engine/noteAreaFlow.js +83 -0
- package/dist/layout-engine/paginator.d.ts +12 -1
- package/dist/layout-engine/paginator.js +18 -2
- package/dist/layout-engine/paragraphSpacing.d.ts +4 -2
- package/dist/layout-engine/paragraphSpacing.js +2 -1
- package/dist/layout-engine/tableRowBreak.d.ts +8 -1
- package/dist/layout-engine/tableRowBreak.js +25 -2
- package/dist/layout-engine/types.d.ts +108 -3
- package/dist/layout-engine/types.js +96 -1
- package/dist/layout-painter/index.d.ts +3 -0
- package/dist/layout-painter/renderPage.js +32 -83
- package/dist/layout-painter/renderParagraph.d.ts +6 -1
- package/dist/layout-painter/renderParagraph.js +75 -26
- package/dist/layout-painter/renderTable.js +26 -13
- package/dist/layout-painter/renderTextBox.d.ts +12 -2
- package/dist/layout-painter/renderTextBox.js +26 -2
- package/dist/prosemirror/alternateContentAttrs.d.ts +9 -0
- package/dist/prosemirror/alternateContentAttrs.js +35 -0
- package/dist/prosemirror/attrs/index.d.ts +9 -9
- package/dist/prosemirror/attrs/index.js +133 -11
- package/dist/prosemirror/commands/comments.js +51 -41
- package/dist/prosemirror/contentControlRevisions.d.ts +43 -0
- package/dist/prosemirror/contentControlRevisions.js +84 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +54 -23
- package/dist/prosemirror/conversion/markInterner.d.ts +21 -0
- package/dist/prosemirror/conversion/markInterner.js +90 -0
- package/dist/prosemirror/conversion/toProseDoc.js +117 -58
- package/dist/prosemirror/extensions/core/DocExtension.js +2 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts +7 -7
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +7 -7
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +6 -2
- package/dist/prosemirror/extensions/marks/markUtils.js +39 -51
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +6 -3
- package/dist/prosemirror/extensions/nodes/SdtExtension.js +9 -2
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +1 -0
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +5 -1
- package/dist/prosemirror/listMarker.d.ts +1 -0
- package/dist/prosemirror/listMarker.js +4 -2
- package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
- package/dist/prosemirror/listRenderingAttrs.js +3 -0
- package/dist/prosemirror/paragraphDirection.d.ts +29 -2
- package/dist/prosemirror/paragraphDirection.js +21 -2
- package/dist/prosemirror/plugins/suggestionMode.js +37 -8
- package/dist/prosemirror/rejoinRunCarriers.d.ts +27 -0
- package/dist/prosemirror/rejoinRunCarriers.js +66 -0
- package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +10 -0
- package/dist/prosemirror/runIdentityAcrossRevisions.js +90 -0
- package/dist/prosemirror/schema/nodes.d.ts +50 -6
- package/dist/prosemirror/textBoxHostParagraph.d.ts +13 -0
- package/dist/prosemirror/textBoxHostParagraph.js +21 -0
- package/dist/prosemirror/validation.js +91 -80
- package/dist/types/content.d.ts +2 -2
- package/dist/utils/createDocument.js +10 -1
- package/dist/utils/scriptSegments.d.ts +33 -4
- package/dist/utils/scriptSegments.js +39 -6
- package/dist/utils/textFormattingMerge.d.ts +17 -3
- package/dist/utils/textFormattingMerge.js +18 -12
- package/dist/utils/trailingText.d.ts +22 -0
- package/dist/utils/trailingText.js +32 -0
- package/package.json +2 -2
- package/dist/layout-engine/justifiedLineFit.d.ts +0 -7
- package/dist/layout-engine/justifiedLineFit.js +0 -6
|
@@ -2,6 +2,7 @@ import { document_d_exports } from "../types/document.js";
|
|
|
2
2
|
import { NumberingMap } from "./numberingParser.js";
|
|
3
3
|
import { ParseContext } from "./parseContext.js";
|
|
4
4
|
import { XmlElement } from "./xmlParser.js";
|
|
5
|
+
import { PreviewLedger } from "./previewBudget.js";
|
|
5
6
|
import { StyleMap } from "./styleParser.js";
|
|
6
7
|
//#region src/docx/textBoxParser.d.ts
|
|
7
8
|
/**
|
|
@@ -15,16 +16,20 @@ declare function extractTextBoxContentElements(txbxContent: XmlElement | null):
|
|
|
15
16
|
/**
|
|
16
17
|
* Type for the paragraph parser function to avoid circular imports
|
|
17
18
|
*/
|
|
18
|
-
type ParagraphParserFn = (node: XmlElement, styles: StyleMap | null, theme: document_d_exports.Theme | null, numbering: NumberingMap | null, rels: document_d_exports.RelationshipMap | null, media: Map<string, document_d_exports.MediaFile> | null
|
|
19
|
+
type ParagraphParserFn = (node: XmlElement, styles: StyleMap | null, theme: document_d_exports.Theme | null, numbering: NumberingMap | null, rels: document_d_exports.RelationshipMap | null, media: Map<string, document_d_exports.MediaFile> | null, options: {
|
|
20
|
+
previews: PreviewLedger;
|
|
21
|
+
}) => document_d_exports.Paragraph;
|
|
19
22
|
/**
|
|
20
23
|
* Type for the table parser function to avoid circular imports
|
|
21
24
|
*/
|
|
22
|
-
type TableParserFn = (node: XmlElement, styles: StyleMap | null, theme: document_d_exports.Theme | null, numbering: NumberingMap | null, rels: document_d_exports.RelationshipMap | null, media: Map<string, document_d_exports.MediaFile> | null
|
|
25
|
+
type TableParserFn = (node: XmlElement, styles: StyleMap | null, theme: document_d_exports.Theme | null, numbering: NumberingMap | null, rels: document_d_exports.RelationshipMap | null, media: Map<string, document_d_exports.MediaFile> | null, options: {
|
|
26
|
+
previews: PreviewLedger;
|
|
27
|
+
}) => document_d_exports.Table | undefined;
|
|
23
28
|
/**
|
|
24
29
|
* Parse text box content with provided parser functions
|
|
25
30
|
* This avoids circular dependencies by accepting parser functions as parameters
|
|
26
31
|
*/
|
|
27
|
-
declare function parseTextBoxContent(txbxContent: XmlElement | null, parseParagraph: ParagraphParserFn, parseTable: TableParserFn | null, styles: StyleMap | null, theme: document_d_exports.Theme | null, numbering: NumberingMap | null, rels: document_d_exports.RelationshipMap | null, media: Map<string, document_d_exports.MediaFile> | null): (document_d_exports.Paragraph | document_d_exports.Table)[];
|
|
32
|
+
declare function parseTextBoxContent(txbxContent: XmlElement | null, parseParagraph: ParagraphParserFn, parseTable: TableParserFn | null, styles: StyleMap | null, theme: document_d_exports.Theme | null, numbering: NumberingMap | null, rels: document_d_exports.RelationshipMap | null, media: Map<string, document_d_exports.MediaFile> | null, previews: PreviewLedger): (document_d_exports.Paragraph | document_d_exports.Table)[];
|
|
28
33
|
/**
|
|
29
34
|
* Check if a drawing element contains a text box
|
|
30
35
|
* Text boxes are shapes with wps:txbx content
|
|
@@ -114,8 +119,16 @@ declare function resolveTextBoxOutlineColor(textBox: document_d_exports.TextBox)
|
|
|
114
119
|
* Get outline width in pixels
|
|
115
120
|
*/
|
|
116
121
|
declare function getTextBoxOutlineWidthPx(textBox: document_d_exports.TextBox): number;
|
|
122
|
+
/** A text-box `w:drawing`, with the `mc:AlternateContent` branch it sits in, if any. */
|
|
123
|
+
type TextBoxDrawing = {
|
|
124
|
+
drawing: XmlElement;
|
|
125
|
+
alternateContent?: {
|
|
126
|
+
element: XmlElement;
|
|
127
|
+
branch: XmlElement;
|
|
128
|
+
};
|
|
129
|
+
};
|
|
117
130
|
type TextBoxRunScan = {
|
|
118
|
-
textBoxDrawings:
|
|
131
|
+
textBoxDrawings: TextBoxDrawing[];
|
|
119
132
|
vmlTextBoxes: XmlElement[];
|
|
120
133
|
hasNonTextBoxContent: boolean;
|
|
121
134
|
};
|
|
@@ -138,4 +151,4 @@ type ScanRunForTextBoxDrawingsOptions = {
|
|
|
138
151
|
};
|
|
139
152
|
declare const scanRunForTextBoxDrawings: ({ xmlRun, claimedByRunParser }: ScanRunForTextBoxDrawingsOptions) => TextBoxRunScan;
|
|
140
153
|
//#endregion
|
|
141
|
-
export { ParagraphParserFn, ScanRunForTextBoxDrawingsOptions, TableParserFn, TextBoxRunScan, extractTextBoxContentElements, getTextBoxContentElement, getTextBoxDimensionsPx, getTextBoxHeightPx, getTextBoxMarginsPx, getTextBoxOutlineWidthPx, getTextBoxText, getTextBoxWidthPx, hasTextBoxContent, hasTextBoxFill, hasTextBoxOutline, isFloatingTextBox, isShapeTextBox, isTextBoxDrawing, parseTextBox, parseTextBoxContent, parseTextBoxFromShape, resolveTextBoxFillColor, resolveTextBoxOutlineColor, scanRunForTextBoxDrawings };
|
|
154
|
+
export { ParagraphParserFn, ScanRunForTextBoxDrawingsOptions, TableParserFn, TextBoxDrawing, TextBoxRunScan, extractTextBoxContentElements, getTextBoxContentElement, getTextBoxDimensionsPx, getTextBoxHeightPx, getTextBoxMarginsPx, getTextBoxOutlineWidthPx, getTextBoxText, getTextBoxWidthPx, hasTextBoxContent, hasTextBoxFill, hasTextBoxOutline, isFloatingTextBox, isShapeTextBox, isTextBoxDrawing, parseTextBox, parseTextBoxContent, parseTextBoxFromShape, resolveTextBoxFillColor, resolveTextBoxOutlineColor, scanRunForTextBoxDrawings };
|
|
@@ -80,7 +80,7 @@ function extractTextBoxContentElements(txbxContent) {
|
|
|
80
80
|
* Parse text box content with provided parser functions
|
|
81
81
|
* This avoids circular dependencies by accepting parser functions as parameters
|
|
82
82
|
*/
|
|
83
|
-
function parseTextBoxContent(txbxContent, parseParagraph, parseTable, styles, theme, numbering, rels, media) {
|
|
83
|
+
function parseTextBoxContent(txbxContent, parseParagraph, parseTable, styles, theme, numbering, rels, media, previews) {
|
|
84
84
|
if (!txbxContent) return [];
|
|
85
85
|
const blocks = [];
|
|
86
86
|
const children = getChildElements(txbxContent);
|
|
@@ -89,10 +89,10 @@ function parseTextBoxContent(txbxContent, parseParagraph, parseTable, styles, th
|
|
|
89
89
|
const colonIdx = name.indexOf(":");
|
|
90
90
|
const localName = colonIdx !== -1 ? name.slice(colonIdx + 1) : name;
|
|
91
91
|
if (localName === "p") {
|
|
92
|
-
const paragraph = parseParagraph(child, styles, theme, numbering, rels, media);
|
|
92
|
+
const paragraph = parseParagraph(child, styles, theme, numbering, rels, media, { previews });
|
|
93
93
|
blocks.push(paragraph);
|
|
94
94
|
} else if (localName === "tbl" && parseTable) {
|
|
95
|
-
const table = parseTable(child, styles, theme, numbering, rels, media);
|
|
95
|
+
const table = parseTable(child, styles, theme, numbering, rels, media, { previews });
|
|
96
96
|
if (table) blocks.push(table);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
@@ -328,9 +328,12 @@ const scanRunForTextBoxDrawings = ({ xmlRun, claimedByRunParser }) => {
|
|
|
328
328
|
const textBoxDrawings = [];
|
|
329
329
|
const vmlTextBoxes = [];
|
|
330
330
|
let hasNonTextBoxContent = false;
|
|
331
|
-
const visitDrawing = (drawingEl) => {
|
|
331
|
+
const visitDrawing = (drawingEl, alternateContent) => {
|
|
332
332
|
if (isTextBoxDrawing(drawingEl)) {
|
|
333
|
-
textBoxDrawings.push(
|
|
333
|
+
textBoxDrawings.push({
|
|
334
|
+
drawing: drawingEl,
|
|
335
|
+
...alternateContent === void 0 ? {} : { alternateContent }
|
|
336
|
+
});
|
|
334
337
|
return;
|
|
335
338
|
}
|
|
336
339
|
hasNonTextBoxContent = true;
|
|
@@ -355,7 +358,10 @@ const scanRunForTextBoxDrawings = ({ xmlRun, claimedByRunParser }) => {
|
|
|
355
358
|
if (!branch) return false;
|
|
356
359
|
let found = false;
|
|
357
360
|
for (const innerEl of getChildElements(branch)) if (getLocalName(innerEl.name ?? "") === "drawing") {
|
|
358
|
-
visitDrawing(innerEl
|
|
361
|
+
visitDrawing(innerEl, {
|
|
362
|
+
element: el,
|
|
363
|
+
branch
|
|
364
|
+
});
|
|
359
365
|
found = true;
|
|
360
366
|
}
|
|
361
367
|
return found;
|
|
@@ -18,14 +18,16 @@ type SanitizeCapturedXmlOptions = {
|
|
|
18
18
|
/** Validate a complete XML part immediately before package output. */
|
|
19
19
|
declare const isSafeCapturedXmlDocument: (xml: string) => boolean;
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* {@link sanitizeCapturedXmlElement} over fixed options, memoized by source
|
|
22
|
+
* string.
|
|
22
23
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* Sanitizing is a pure function of the string and the options, and a capture
|
|
25
|
+
* is fixed when it is parsed, so every save after the first would otherwise
|
|
26
|
+
* re-parse and re-validate the same markup. The key is the string rather than
|
|
27
|
+
* the object holding it: editor round-trips copy capture holders, but the
|
|
28
|
+
* string they carry is the one the parser produced. `transform` and `validate`
|
|
29
|
+
* must therefore depend on nothing but the element they are given.
|
|
28
30
|
*/
|
|
29
|
-
declare const
|
|
31
|
+
declare const createCapturedXmlSanitizer: (options: SanitizeCapturedXmlOptions) => ((xml: string | undefined) => string | null);
|
|
30
32
|
//#endregion
|
|
31
|
-
export { UntranslatableStrictNamespaceError, captureVerbatimXml,
|
|
33
|
+
export { UntranslatableStrictNamespaceError, captureVerbatimXml, createCapturedXmlSanitizer, isSafeCapturedXmlDocument };
|
|
@@ -313,12 +313,55 @@ const captureAllowedElement = (element, { allowedLocalNames, allowedNamespaceUri
|
|
|
313
313
|
* owner, and rejects siblings, unexpected roots, and namespace shadowing.
|
|
314
314
|
*/
|
|
315
315
|
const sanitizeCapturedXmlElement = (xml, options) => {
|
|
316
|
-
if (xml === void 0) return null;
|
|
317
316
|
const trimmed = xml.trim();
|
|
318
317
|
if (!trimmed) return null;
|
|
319
318
|
if (!withinXmlResourceLimits(xml) || getDocxXmlSafetyIssue(xml) !== null) return null;
|
|
320
319
|
const root = parsedReplayEnvelope(trimmed, options.inheritedNamespaceScope ?? OOXML_NAMESPACE_SCOPE);
|
|
321
320
|
return root ? captureAllowedElement(root, options) : null;
|
|
322
321
|
};
|
|
322
|
+
/**
|
|
323
|
+
* Characters of source and sanitized XML one sanitizer retains. A paragraph
|
|
324
|
+
* capture is a few hundred characters, so this holds every distinct capture of
|
|
325
|
+
* a large document while bounding what outlives the documents it came from.
|
|
326
|
+
*/
|
|
327
|
+
const CAPTURED_XML_SANITIZER_CACHE_CHARACTERS = 2 * 1024 * 1024;
|
|
328
|
+
/**
|
|
329
|
+
* What one cache entry costs beyond its strings, in characters. Charging it
|
|
330
|
+
* keeps many short keys from holding far more memory than their text, and
|
|
331
|
+
* caps the entry count at the budget divided by this.
|
|
332
|
+
*/
|
|
333
|
+
const CAPTURED_XML_SANITIZER_ENTRY_OVERHEAD_CHARACTERS = 64;
|
|
334
|
+
const capturedXmlCacheCharacters = (xml, sanitized) => CAPTURED_XML_SANITIZER_ENTRY_OVERHEAD_CHARACTERS + xml.length + (sanitized?.length ?? 0);
|
|
335
|
+
/**
|
|
336
|
+
* {@link sanitizeCapturedXmlElement} over fixed options, memoized by source
|
|
337
|
+
* string.
|
|
338
|
+
*
|
|
339
|
+
* Sanitizing is a pure function of the string and the options, and a capture
|
|
340
|
+
* is fixed when it is parsed, so every save after the first would otherwise
|
|
341
|
+
* re-parse and re-validate the same markup. The key is the string rather than
|
|
342
|
+
* the object holding it: editor round-trips copy capture holders, but the
|
|
343
|
+
* string they carry is the one the parser produced. `transform` and `validate`
|
|
344
|
+
* must therefore depend on nothing but the element they are given.
|
|
345
|
+
*/
|
|
346
|
+
const createCapturedXmlSanitizer = (options) => {
|
|
347
|
+
const cache = /* @__PURE__ */ new Map();
|
|
348
|
+
let cachedCharacters = 0;
|
|
349
|
+
return (xml) => {
|
|
350
|
+
if (xml === void 0) return null;
|
|
351
|
+
const cached = cache.get(xml);
|
|
352
|
+
if (cached !== void 0) return cached;
|
|
353
|
+
const sanitized = sanitizeCapturedXmlElement(xml, options);
|
|
354
|
+
const characters = capturedXmlCacheCharacters(xml, sanitized);
|
|
355
|
+
if (characters > CAPTURED_XML_SANITIZER_CACHE_CHARACTERS) return sanitized;
|
|
356
|
+
for (const [oldestXml, oldestSanitized] of cache) {
|
|
357
|
+
if (cachedCharacters + characters <= CAPTURED_XML_SANITIZER_CACHE_CHARACTERS) break;
|
|
358
|
+
cache.delete(oldestXml);
|
|
359
|
+
cachedCharacters -= capturedXmlCacheCharacters(oldestXml, oldestSanitized);
|
|
360
|
+
}
|
|
361
|
+
cache.set(xml, sanitized);
|
|
362
|
+
cachedCharacters += characters;
|
|
363
|
+
return sanitized;
|
|
364
|
+
};
|
|
365
|
+
};
|
|
323
366
|
//#endregion
|
|
324
|
-
export { UntranslatableStrictNamespaceError, captureVerbatimXml,
|
|
367
|
+
export { UntranslatableStrictNamespaceError, captureVerbatimXml, createCapturedXmlSanitizer, isSafeCapturedXmlDocument };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { XmlElement } from "./xmlParser.js";
|
|
3
|
+
import { PreviewLedger } from "./previewBudget.js";
|
|
3
4
|
//#region src/docx/vmlImageParser.d.ts
|
|
4
5
|
/**
|
|
5
6
|
* Whether a `w:pict` that resolved to no image must still be kept verbatim.
|
|
@@ -20,6 +21,16 @@ import { XmlElement } from "./xmlParser.js";
|
|
|
20
21
|
* the paragraph it detached, so this predicate need not guess who claimed what.
|
|
21
22
|
*/
|
|
22
23
|
declare function shouldPreserveRawVmlPict(pictElement: XmlElement): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Parse a `w:pict` element into an inline image, or null when it carries no
|
|
26
|
+
* ordinary VML picture (no resolvable `<v:imagedata>`, or a watermark shape).
|
|
27
|
+
*
|
|
28
|
+
* `rootXmlns` carries the source document/header namespace declarations. They
|
|
29
|
+
* are injected onto the captured VML so the raw replay stays self-contained
|
|
30
|
+
* when the producer bound WordprocessingML / relationship / VML namespaces to
|
|
31
|
+
* non-canonical prefixes the serializer's root does not declare.
|
|
32
|
+
*/
|
|
33
|
+
declare function parseVmlImageContent(pictElement: XmlElement, rels: document_d_exports.RelationshipMap | null, media: Map<string, document_d_exports.MediaFile> | null, previews: PreviewLedger, rootXmlns?: Record<string, string>): document_d_exports.DrawingContent | null;
|
|
23
34
|
/**
|
|
24
35
|
* Whether the run parser turns this `w:pict` into run content of its own.
|
|
25
36
|
*
|
|
@@ -31,20 +42,11 @@ declare function shouldPreserveRawVmlPict(pictElement: XmlElement): boolean;
|
|
|
31
42
|
*
|
|
32
43
|
* The enrichment asks this rather than re-deriving the answer from the markup:
|
|
33
44
|
* its own reading (`v:imagedata` present) named only the picture path, so a
|
|
34
|
-
* `v:group` claimed by the preview path was represented by both owners.
|
|
35
|
-
*
|
|
36
|
-
*
|
|
45
|
+
* `v:group` claimed by the preview path was represented by both owners. It
|
|
46
|
+
* reads the two decisions {@link parseVmlImageContent} makes without building
|
|
47
|
+
* either result: a preview built here would be registered against the package
|
|
48
|
+
* budget for a drawing the model never holds.
|
|
37
49
|
*/
|
|
38
50
|
declare const isVmlPictParsedByRunParser: (pictElement: XmlElement, rels: document_d_exports.RelationshipMap | null, media: Map<string, document_d_exports.MediaFile> | null) => boolean;
|
|
39
|
-
/**
|
|
40
|
-
* Parse a `w:pict` element into an inline image, or null when it carries no
|
|
41
|
-
* ordinary VML picture (no resolvable `<v:imagedata>`, or a watermark shape).
|
|
42
|
-
*
|
|
43
|
-
* `rootXmlns` carries the source document/header namespace declarations. They
|
|
44
|
-
* are injected onto the captured VML so the raw replay stays self-contained
|
|
45
|
-
* when the producer bound WordprocessingML / relationship / VML namespaces to
|
|
46
|
-
* non-canonical prefixes the serializer's root does not declare.
|
|
47
|
-
*/
|
|
48
|
-
declare function parseVmlImageContent(pictElement: XmlElement, rels: document_d_exports.RelationshipMap | null, media: Map<string, document_d_exports.MediaFile> | null, rootXmlns?: Record<string, string>): document_d_exports.DrawingContent | null;
|
|
49
51
|
//#endregion
|
|
50
52
|
export { isVmlPictParsedByRunParser, parseVmlImageContent, shouldPreserveRawVmlPict };
|
|
@@ -2,9 +2,8 @@ import { sanitizeImageSrc } from "../utils/sanitizeImageSrc.js";
|
|
|
2
2
|
import { pixelsToEmu } from "../utils/units.js";
|
|
3
3
|
import { resolveImageData } from "./imageParser.js";
|
|
4
4
|
import { imageRawXmlFingerprint } from "./imageRawXml.js";
|
|
5
|
-
import { PREVIEW_KINDS } from "./previewBudget.js";
|
|
6
5
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
7
|
-
import { isValidVmlPreviewDimension, parseVmlNumber, parseVmlStyle, renderStandaloneVmlPreview, renderVmlGroupPreview, vmlCssLengthToPx
|
|
6
|
+
import { fitsVmlSvgCap, isValidVmlPreviewDimension, parseVmlNumber, parseVmlStyle, renderStandaloneVmlPreview, renderVmlGroupPreview, vmlCssLengthToPx } from "./vmlPreview.js";
|
|
8
7
|
import { isWatermarkShape } from "./watermarkParser.js";
|
|
9
8
|
import { cloneWithXmlnsDeclarations, findAllDeep, findChild, findDeep, getAttribute, getChildElements, getLocalName } from "./xmlParser.js";
|
|
10
9
|
import { DRAWING_RAW_XML_MODES, relationshipIdOf } from "@stll/docx-core/model";
|
|
@@ -93,19 +92,44 @@ const vmlImageLayout = (style) => {
|
|
|
93
92
|
}
|
|
94
93
|
};
|
|
95
94
|
};
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
/** A render the model may retain: one that painted, within the SVG output cap. */
|
|
96
|
+
const retainableRender = (preview) => {
|
|
97
|
+
switch (preview.type) {
|
|
98
|
+
case "rendered": return fitsVmlSvgCap(preview.svg) ? preview : null;
|
|
99
|
+
case "empty":
|
|
100
|
+
case "invalid": return null;
|
|
101
|
+
default: return preview;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* The render folio draws a `w:pict` as when it holds no picture: its first
|
|
106
|
+
* group or standalone shape that renders within the cap, or null.
|
|
107
|
+
*
|
|
108
|
+
* Deciding this builds nothing, so the text-box pass can ask whether the run
|
|
109
|
+
* parser claims a pict without making a second preview of it.
|
|
110
|
+
*/
|
|
111
|
+
const vmlPictPreview = (pictElement) => {
|
|
112
|
+
for (const child of getChildElements(pictElement)) {
|
|
113
|
+
const localName = getLocalName(child.name ?? "");
|
|
114
|
+
if (localName === "group") {
|
|
115
|
+
const preview = retainableRender(renderVmlGroupPreview(child));
|
|
116
|
+
if (preview) return preview;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (localName === "rect" || localName === "roundrect" || localName === "oval") {
|
|
120
|
+
const preview = retainableRender(renderStandaloneVmlPreview(child));
|
|
121
|
+
if (preview) return preview;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
};
|
|
126
|
+
const previewDrawing = (pictElement, { svg, widthPx, heightPx, style }, rootXmlns, previews) => {
|
|
99
127
|
const leftPx = vmlCssLengthToPx(style["margin-left"] ?? style["left"]) ?? 0;
|
|
100
128
|
const topPx = vmlCssLengthToPx(style["margin-top"] ?? style["top"]) ?? 0;
|
|
101
129
|
const horizontalRelative = style["mso-position-horizontal-relative"] === "page";
|
|
102
130
|
const verticalRelative = style["mso-position-vertical-relative"] === "page";
|
|
103
131
|
const zIndex = parseVmlNumber(style["z-index"]);
|
|
104
|
-
const image = {
|
|
105
|
-
type: "image",
|
|
106
|
-
src,
|
|
107
|
-
mimeType: PREVIEW_KINDS.vmlShape.mimeType,
|
|
108
|
-
filename: PREVIEW_KINDS.vmlShape.filename,
|
|
132
|
+
const image = previews.svgImage("vmlShape", svg, {
|
|
109
133
|
size: {
|
|
110
134
|
width: pixelsToEmu(widthPx),
|
|
111
135
|
height: pixelsToEmu(heightPx)
|
|
@@ -121,7 +145,7 @@ const previewImage = (pictElement, svg, widthPx, heightPx, style, rootXmlns) =>
|
|
|
121
145
|
posOffset: pixelsToSafeEmu(topPx) ?? 0
|
|
122
146
|
}
|
|
123
147
|
}
|
|
124
|
-
};
|
|
148
|
+
});
|
|
125
149
|
return {
|
|
126
150
|
type: "drawing",
|
|
127
151
|
image,
|
|
@@ -130,14 +154,6 @@ const previewImage = (pictElement, svg, widthPx, heightPx, style, rootXmlns) =>
|
|
|
130
154
|
rawXmlMode: DRAWING_RAW_XML_MODES.PREVIEW_ONLY
|
|
131
155
|
};
|
|
132
156
|
};
|
|
133
|
-
const previewDrawing = (pictElement, preview, rootXmlns) => {
|
|
134
|
-
switch (preview.type) {
|
|
135
|
-
case "rendered": return previewImage(pictElement, preview.svg, preview.widthPx, preview.heightPx, preview.style, rootXmlns);
|
|
136
|
-
case "empty":
|
|
137
|
-
case "invalid": return null;
|
|
138
|
-
default: return preview;
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
157
|
/** VML elements that paint something, i.e. content a save must not lose. */
|
|
142
158
|
const VML_DRAWABLE_SHAPES = [
|
|
143
159
|
"shape",
|
|
@@ -174,22 +190,6 @@ function shouldPreserveRawVmlPict(pictElement) {
|
|
|
174
190
|
return VML_DRAWABLE_SHAPES.some((localName) => findDeep(pictElement, "v", localName) !== null);
|
|
175
191
|
}
|
|
176
192
|
/**
|
|
177
|
-
* Whether the run parser turns this `w:pict` into run content of its own.
|
|
178
|
-
*
|
|
179
|
-
* Exactly one owner may represent a `w:pict`: the run parser, whose drawing
|
|
180
|
-
* carries the whole element as captured XML, or the text-box enrichment pass,
|
|
181
|
-
* which rebuilds one `v:textbox` as an editable shape. Two owners write the
|
|
182
|
-
* same artwork twice, and when the pict holds text, the saved document says it
|
|
183
|
-
* twice.
|
|
184
|
-
*
|
|
185
|
-
* The enrichment asks this rather than re-deriving the answer from the markup:
|
|
186
|
-
* its own reading (`v:imagedata` present) named only the picture path, so a
|
|
187
|
-
* `v:group` claimed by the preview path was represented by both owners. The
|
|
188
|
-
* result is a pure function of the element, so asking it a second time costs
|
|
189
|
-
* only the work the run parser already did.
|
|
190
|
-
*/
|
|
191
|
-
const isVmlPictParsedByRunParser = (pictElement, rels, media) => parseVmlImageContent(pictElement, rels, media) !== null || shouldPreserveRawVmlPict(pictElement);
|
|
192
|
-
/**
|
|
193
193
|
* Read the relationship id off a `v:imagedata` element. Word writes `r:id`;
|
|
194
194
|
* some legacy / third-party generators use `r:embed` or the office-namespace
|
|
195
195
|
* `o:relid` instead, so fall back through those before the bare `id`.
|
|
@@ -198,15 +198,10 @@ function readImageDataRId(imagedata) {
|
|
|
198
198
|
return getAttribute(imagedata, "r", "id") ?? getAttribute(imagedata, "r", "embed") ?? getAttribute(imagedata, "o", "relid") ?? getAttribute(imagedata, null, "id") ?? void 0;
|
|
199
199
|
}
|
|
200
200
|
/**
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* `rootXmlns` carries the source document/header namespace declarations. They
|
|
205
|
-
* are injected onto the captured VML so the raw replay stays self-contained
|
|
206
|
-
* when the producer bound WordprocessingML / relationship / VML namespaces to
|
|
207
|
-
* non-canonical prefixes the serializer's root does not declare.
|
|
201
|
+
* The picture a `w:pict` carries: the first shape whose `<v:imagedata>`
|
|
202
|
+
* names a relationship, skipping watermarks, or null.
|
|
208
203
|
*/
|
|
209
|
-
|
|
204
|
+
const vmlPictPicture = (pictElement, rels, media) => {
|
|
210
205
|
const shapes = [
|
|
211
206
|
...findAllDeep(pictElement, "v", "shape"),
|
|
212
207
|
...findAllDeep(pictElement, "v", "rect"),
|
|
@@ -239,25 +234,45 @@ function parseVmlImageContent(pictElement, rels, media, rootXmlns = {}) {
|
|
|
239
234
|
if (filename) image.filename = filename;
|
|
240
235
|
const title = getAttribute(imagedata, "o", "title");
|
|
241
236
|
if (title) image.title = title;
|
|
242
|
-
return
|
|
243
|
-
type: "drawing",
|
|
244
|
-
image,
|
|
245
|
-
rawXml: captureVerbatimXml(cloneWithXmlnsDeclarations(pictElement, rootXmlns))
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
for (const child of getChildElements(pictElement)) {
|
|
249
|
-
const localName = getLocalName(child.name ?? "");
|
|
250
|
-
if (localName === "group") {
|
|
251
|
-
const preview = previewDrawing(pictElement, renderVmlGroupPreview(child), rootXmlns);
|
|
252
|
-
if (preview) return preview;
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
if (localName === "rect" || localName === "roundrect" || localName === "oval") {
|
|
256
|
-
const preview = previewDrawing(pictElement, renderStandaloneVmlPreview(child), rootXmlns);
|
|
257
|
-
if (preview) return preview;
|
|
258
|
-
}
|
|
237
|
+
return image;
|
|
259
238
|
}
|
|
260
239
|
return null;
|
|
240
|
+
};
|
|
241
|
+
/**
|
|
242
|
+
* Parse a `w:pict` element into an inline image, or null when it carries no
|
|
243
|
+
* ordinary VML picture (no resolvable `<v:imagedata>`, or a watermark shape).
|
|
244
|
+
*
|
|
245
|
+
* `rootXmlns` carries the source document/header namespace declarations. They
|
|
246
|
+
* are injected onto the captured VML so the raw replay stays self-contained
|
|
247
|
+
* when the producer bound WordprocessingML / relationship / VML namespaces to
|
|
248
|
+
* non-canonical prefixes the serializer's root does not declare.
|
|
249
|
+
*/
|
|
250
|
+
function parseVmlImageContent(pictElement, rels, media, previews, rootXmlns = {}) {
|
|
251
|
+
const image = vmlPictPicture(pictElement, rels, media);
|
|
252
|
+
if (image) return {
|
|
253
|
+
type: "drawing",
|
|
254
|
+
image,
|
|
255
|
+
rawXml: captureVerbatimXml(cloneWithXmlnsDeclarations(pictElement, rootXmlns))
|
|
256
|
+
};
|
|
257
|
+
const preview = vmlPictPreview(pictElement);
|
|
258
|
+
return preview ? previewDrawing(pictElement, preview, rootXmlns, previews) : null;
|
|
261
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Whether the run parser turns this `w:pict` into run content of its own.
|
|
262
|
+
*
|
|
263
|
+
* Exactly one owner may represent a `w:pict`: the run parser, whose drawing
|
|
264
|
+
* carries the whole element as captured XML, or the text-box enrichment pass,
|
|
265
|
+
* which rebuilds one `v:textbox` as an editable shape. Two owners write the
|
|
266
|
+
* same artwork twice, and when the pict holds text, the saved document says it
|
|
267
|
+
* twice.
|
|
268
|
+
*
|
|
269
|
+
* The enrichment asks this rather than re-deriving the answer from the markup:
|
|
270
|
+
* its own reading (`v:imagedata` present) named only the picture path, so a
|
|
271
|
+
* `v:group` claimed by the preview path was represented by both owners. It
|
|
272
|
+
* reads the two decisions {@link parseVmlImageContent} makes without building
|
|
273
|
+
* either result: a preview built here would be registered against the package
|
|
274
|
+
* budget for a drawing the model never holds.
|
|
275
|
+
*/
|
|
276
|
+
const isVmlPictParsedByRunParser = (pictElement, rels, media) => vmlPictPicture(pictElement, rels, media) !== null || vmlPictPreview(pictElement) !== null || shouldPreserveRawVmlPict(pictElement);
|
|
262
277
|
//#endregion
|
|
263
278
|
export { isVmlPictParsedByRunParser, parseVmlImageContent, shouldPreserveRawVmlPict };
|
|
@@ -19,11 +19,11 @@ declare const parseVmlStyle: (style: string | null) => Record<string, string>;
|
|
|
19
19
|
declare const parseVmlNumber: (raw: string | null | undefined) => number | undefined;
|
|
20
20
|
/** Check a physical preview dimension against the retained SVG layout bound. */
|
|
21
21
|
declare const isValidVmlPreviewDimension: (value: number | undefined) => value is number;
|
|
22
|
-
/**
|
|
23
|
-
declare const
|
|
22
|
+
/** Whether a generated, already-sanitized SVG is within the output cap a preview may retain. */
|
|
23
|
+
declare const fitsVmlSvgCap: (svg: string) => boolean;
|
|
24
24
|
/** Render a bounded standalone VML rectangle or oval preview. */
|
|
25
25
|
declare const renderStandaloneVmlPreview: (shape: XmlElement) => VmlPreviewResult;
|
|
26
26
|
/** Render a VML group through bounded, non-clipping local-coordinate transforms. */
|
|
27
27
|
declare const renderVmlGroupPreview: (group: XmlElement) => VmlPreviewResult;
|
|
28
28
|
//#endregion
|
|
29
|
-
export { VmlPreviewResult, isValidVmlPreviewDimension, parseVmlNumber, parseVmlStyle, renderStandaloneVmlPreview, renderVmlGroupPreview, vmlCssLengthToPx
|
|
29
|
+
export { VmlPreviewResult, fitsVmlSvgCap, isValidVmlPreviewDimension, parseVmlNumber, parseVmlStyle, renderStandaloneVmlPreview, renderVmlGroupPreview, vmlCssLengthToPx };
|
package/dist/docx/vmlPreview.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { PREVIEW_KINDS } from "./previewBudget.js";
|
|
2
1
|
import { findChild, findDeep, getAttribute, getChildElements, getLocalName } from "./xmlParser.js";
|
|
3
2
|
//#region src/docx/vmlPreview.ts
|
|
4
3
|
const MAX_VML_PREVIEW_DEPTH = 16;
|
|
@@ -83,8 +82,8 @@ const parseVmlNumber = (raw) => {
|
|
|
83
82
|
};
|
|
84
83
|
/** Check a physical preview dimension against the retained SVG layout bound. */
|
|
85
84
|
const isValidVmlPreviewDimension = (value) => value !== void 0 && value > 0 && value <= MAX_VML_PREVIEW_DIMENSION_PX;
|
|
86
|
-
/**
|
|
87
|
-
const
|
|
85
|
+
/** Whether a generated, already-sanitized SVG is within the output cap a preview may retain. */
|
|
86
|
+
const fitsVmlSvgCap = (svg) => svg.length <= MAX_VML_SVG_CHARACTERS;
|
|
88
87
|
const validCoordinate = (value) => value !== void 0 && Math.abs(value) <= MAX_VML_PREVIEW_COORDINATE;
|
|
89
88
|
const validExtent = (value) => value !== void 0 && value > 0 && value <= MAX_VML_PREVIEW_COORDINATE;
|
|
90
89
|
const coordinatePair = (raw) => {
|
|
@@ -504,4 +503,4 @@ const renderVmlGroupPreview = (group) => {
|
|
|
504
503
|
};
|
|
505
504
|
};
|
|
506
505
|
//#endregion
|
|
507
|
-
export { isValidVmlPreviewDimension, parseVmlNumber, parseVmlStyle, renderStandaloneVmlPreview, renderVmlGroupPreview, vmlCssLengthToPx
|
|
506
|
+
export { fitsVmlSvgCap, isValidVmlPreviewDimension, parseVmlNumber, parseVmlStyle, renderStandaloneVmlPreview, renderVmlGroupPreview, vmlCssLengthToPx };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { XmlElement, XmlNamespaceScope } from "./xmlParser.js";
|
|
2
|
+
//#region src/docx/xmlNamespaceContext.d.ts
|
|
3
|
+
declare const EMPTY_NAMESPACE_SCOPE: XmlNamespaceScope;
|
|
4
|
+
declare const XML_NAMESPACE_URI = "http://www.w3.org/XML/1998/namespace";
|
|
5
|
+
declare const resolveNamespaceUri: (scope: XmlNamespaceScope | undefined, prefix: string) => string | undefined;
|
|
6
|
+
/** Attach the element's resolved namespace metadata from its in-scope declarations. */
|
|
7
|
+
declare const attachXmlNamespaceContext: (element: XmlElement, inheritedNamespaceScope?: XmlNamespaceScope) => XmlNamespaceScope;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { EMPTY_NAMESPACE_SCOPE, XML_NAMESPACE_URI, attachXmlNamespaceContext, resolveNamespaceUri };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//#region src/docx/xmlNamespaceContext.ts
|
|
2
|
+
const EMPTY_NAMESPACE_SCOPE = { bindings: /* @__PURE__ */ new Map() };
|
|
3
|
+
/**
|
|
4
|
+
* Reserved by the XML Namespaces recommendation (§3): every document binds
|
|
5
|
+
* this prefix to this URI whether or not it declares `xmlns:xml`, and no
|
|
6
|
+
* document may bind it to anything else. A source that omits the declaration
|
|
7
|
+
* — the overwhelming majority — must still resolve `xml:space`, `xml:lang`
|
|
8
|
+
* and `xml:id` to it, or a reader mistakes the attribute for an unprefixed
|
|
9
|
+
* one and a save drops its prefix.
|
|
10
|
+
*/
|
|
11
|
+
const XML_RESERVED_PREFIX = "xml";
|
|
12
|
+
const XML_NAMESPACE_URI = "http://www.w3.org/XML/1998/namespace";
|
|
13
|
+
const resolveNamespaceUri = (scope, prefix) => {
|
|
14
|
+
let current = scope;
|
|
15
|
+
while (current) {
|
|
16
|
+
const value = current.bindings.get(prefix);
|
|
17
|
+
if (value !== void 0) return value;
|
|
18
|
+
current = current.parent;
|
|
19
|
+
}
|
|
20
|
+
return prefix === XML_RESERVED_PREFIX ? XML_NAMESPACE_URI : void 0;
|
|
21
|
+
};
|
|
22
|
+
/** Attach the element's resolved namespace metadata from its in-scope declarations. */
|
|
23
|
+
const attachXmlNamespaceContext = (element, inheritedNamespaceScope = EMPTY_NAMESPACE_SCOPE) => {
|
|
24
|
+
let localBindings = null;
|
|
25
|
+
if (element.attributes) for (const [attribute, value] of Object.entries(element.attributes)) {
|
|
26
|
+
if (typeof value !== "string" || attribute !== "xmlns" && !attribute.startsWith("xmlns:")) continue;
|
|
27
|
+
localBindings ??= /* @__PURE__ */ new Map();
|
|
28
|
+
const prefix = attribute === "xmlns" ? "" : attribute.slice(6);
|
|
29
|
+
localBindings.set(prefix, value);
|
|
30
|
+
}
|
|
31
|
+
const namespaceScope = localBindings === null ? inheritedNamespaceScope : {
|
|
32
|
+
bindings: localBindings,
|
|
33
|
+
parent: inheritedNamespaceScope
|
|
34
|
+
};
|
|
35
|
+
Object.defineProperty(element, "namespaceScope", {
|
|
36
|
+
configurable: false,
|
|
37
|
+
enumerable: false,
|
|
38
|
+
value: namespaceScope,
|
|
39
|
+
writable: false
|
|
40
|
+
});
|
|
41
|
+
const name = element.name ?? "";
|
|
42
|
+
const colonIndex = name.indexOf(":");
|
|
43
|
+
const prefix = colonIndex === -1 ? "" : name.slice(0, colonIndex);
|
|
44
|
+
const namespaceUri = resolveNamespaceUri(namespaceScope, prefix);
|
|
45
|
+
if (namespaceUri !== void 0) Object.defineProperty(element, "namespaceUri", {
|
|
46
|
+
configurable: false,
|
|
47
|
+
enumerable: false,
|
|
48
|
+
value: namespaceUri,
|
|
49
|
+
writable: false
|
|
50
|
+
});
|
|
51
|
+
return namespaceScope;
|
|
52
|
+
};
|
|
53
|
+
//#endregion
|
|
54
|
+
export { EMPTY_NAMESPACE_SCOPE, XML_NAMESPACE_URI, attachXmlNamespaceContext, resolveNamespaceUri };
|
package/dist/docx/xmlParser.d.ts
CHANGED
|
@@ -28,8 +28,6 @@ type XmlNamespaceScope = {
|
|
|
28
28
|
bindings: ReadonlyMap<string, string>;
|
|
29
29
|
parent?: XmlNamespaceScope;
|
|
30
30
|
};
|
|
31
|
-
/** Attach the element's resolved namespace metadata from its in-scope declarations. */
|
|
32
|
-
declare const attachXmlNamespaceContext: (element: XmlElement, inheritedNamespaceScope?: XmlNamespaceScope) => XmlNamespaceScope;
|
|
33
31
|
/**
|
|
34
32
|
* Common OOXML namespace URIs — re-exported from @stll/docx-utils.
|
|
35
33
|
*/
|
|
@@ -70,6 +68,11 @@ declare const NAMESPACES: {
|
|
|
70
68
|
*/
|
|
71
69
|
declare const OOXML_NAMESPACE_SCOPE: XmlNamespaceScope;
|
|
72
70
|
declare function parseXml(xml: string, inheritedNamespaceScope?: XmlNamespaceScope): XmlElement;
|
|
71
|
+
/**
|
|
72
|
+
* The general-purpose reader {@link parseXml} falls back to, and the reference
|
|
73
|
+
* the streaming reader's equivalence tests compare against.
|
|
74
|
+
*/
|
|
75
|
+
declare function parseXmlWithFastXmlParser(xml: string, inheritedNamespaceScope?: XmlNamespaceScope): XmlElement;
|
|
73
76
|
/**
|
|
74
77
|
* Serialize an XmlElement back to an XML string.
|
|
75
78
|
*
|
|
@@ -405,4 +408,4 @@ declare function cloneWithXmlnsDeclarations(element: XmlElement, xmlnsDecls: Rec
|
|
|
405
408
|
*/
|
|
406
409
|
declare function cloneElement(element: XmlElement, overrides: Partial<XmlElement>): XmlElement;
|
|
407
410
|
//#endregion
|
|
408
|
-
export { NAMESPACES, OFFICE_RELATIONSHIP_NAMESPACE_URIS, OOXML_NAMESPACE_SCOPE, WORDPROCESSINGML_NAMESPACE_URIS, XmlAttributeMatch, XmlElement, XmlNamespaceScope,
|
|
411
|
+
export { NAMESPACES, OFFICE_RELATIONSHIP_NAMESPACE_URIS, OOXML_NAMESPACE_SCOPE, WORDPROCESSINGML_NAMESPACE_URIS, XmlAttributeMatch, XmlElement, XmlNamespaceScope, cloneElement, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findAttributeByNamespaceUri, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findChildrenByNamespaceUri, findDeep, findWordprocessingChild, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffAttribute, parseOnOffChild, parseOnOffValue, parseTableMeasurementValue, parseXml, parseXmlDocument, parseXmlWithFastXmlParser, resolveAttributeNamespaceUri, selectAlternateContentBranch };
|
package/dist/docx/xmlParser.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { parseStreamingXml } from "./streamingXmlParser.js";
|
|
1
2
|
import { NUMBERS_PER_PERCENT, percentageSpelling, transitionalSlotEncoding } from "./transitionalSpelling.js";
|
|
2
3
|
import { universalMeasureAs } from "./universalMeasure.js";
|
|
4
|
+
import { EMPTY_NAMESPACE_SCOPE, attachXmlNamespaceContext, resolveNamespaceUri } from "./xmlNamespaceContext.js";
|
|
3
5
|
import { escapeXmlAttribute, escapeXmlText } from "@stll/docx-core";
|
|
4
6
|
import { PARSE_WARNING_CODES } from "@stll/docx-core/model";
|
|
5
7
|
import { XMLParser } from "fast-xml-parser";
|
|
@@ -49,46 +51,6 @@ const fxpParserWithStopNodes = new XMLParser(fxpParserOptionsWithStopNodes);
|
|
|
49
51
|
const TEXT_KEY = "#text";
|
|
50
52
|
/** Attribute group key used by fast-xml-parser in preserveOrder mode. */
|
|
51
53
|
const ATTR_KEY = ":@";
|
|
52
|
-
const EMPTY_NAMESPACE_SCOPE = { bindings: /* @__PURE__ */ new Map() };
|
|
53
|
-
const resolveNamespaceUri = (scope, prefix) => {
|
|
54
|
-
let current = scope;
|
|
55
|
-
while (current) {
|
|
56
|
-
const value = current.bindings.get(prefix);
|
|
57
|
-
if (value !== void 0) return value;
|
|
58
|
-
current = current.parent;
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
/** Attach the element's resolved namespace metadata from its in-scope declarations. */
|
|
62
|
-
const attachXmlNamespaceContext = (element, inheritedNamespaceScope = EMPTY_NAMESPACE_SCOPE) => {
|
|
63
|
-
let localBindings = null;
|
|
64
|
-
if (element.attributes) for (const [attribute, value] of Object.entries(element.attributes)) {
|
|
65
|
-
if (typeof value !== "string" || attribute !== "xmlns" && !attribute.startsWith("xmlns:")) continue;
|
|
66
|
-
localBindings ??= /* @__PURE__ */ new Map();
|
|
67
|
-
const prefix = attribute === "xmlns" ? "" : attribute.slice(6);
|
|
68
|
-
localBindings.set(prefix, value);
|
|
69
|
-
}
|
|
70
|
-
const namespaceScope = localBindings === null ? inheritedNamespaceScope : {
|
|
71
|
-
bindings: localBindings,
|
|
72
|
-
parent: inheritedNamespaceScope
|
|
73
|
-
};
|
|
74
|
-
Object.defineProperty(element, "namespaceScope", {
|
|
75
|
-
configurable: false,
|
|
76
|
-
enumerable: false,
|
|
77
|
-
value: namespaceScope,
|
|
78
|
-
writable: false
|
|
79
|
-
});
|
|
80
|
-
const name = element.name ?? "";
|
|
81
|
-
const colonIndex = name.indexOf(":");
|
|
82
|
-
const prefix = colonIndex === -1 ? "" : name.slice(0, colonIndex);
|
|
83
|
-
const namespaceUri = resolveNamespaceUri(namespaceScope, prefix);
|
|
84
|
-
if (namespaceUri !== void 0) Object.defineProperty(element, "namespaceUri", {
|
|
85
|
-
configurable: false,
|
|
86
|
-
enumerable: false,
|
|
87
|
-
value: namespaceUri,
|
|
88
|
-
writable: false
|
|
89
|
-
});
|
|
90
|
-
return namespaceScope;
|
|
91
|
-
};
|
|
92
54
|
/**
|
|
93
55
|
* Convert a fast-xml-parser preserveOrder node into an XmlElement.
|
|
94
56
|
*
|
|
@@ -150,6 +112,14 @@ const NAMESPACES = OOXML_NS;
|
|
|
150
112
|
*/
|
|
151
113
|
const OOXML_NAMESPACE_SCOPE = { bindings: new Map(Object.entries(OOXML_NS)) };
|
|
152
114
|
function parseXml(xml, inheritedNamespaceScope = EMPTY_NAMESPACE_SCOPE) {
|
|
115
|
+
const streamed = parseStreamingXml(xml, inheritedNamespaceScope);
|
|
116
|
+
return streamed.status === "parsed" ? streamed.value : parseXmlWithFastXmlParser(xml, inheritedNamespaceScope);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The general-purpose reader {@link parseXml} falls back to, and the reference
|
|
120
|
+
* the streaming reader's equivalence tests compare against.
|
|
121
|
+
*/
|
|
122
|
+
function parseXmlWithFastXmlParser(xml, inheritedNamespaceScope = EMPTY_NAMESPACE_SCOPE) {
|
|
153
123
|
return fxpToRootElement((xml.includes("binData") ? fxpParserWithStopNodes : fxpParser).parse(xml), inheritedNamespaceScope);
|
|
154
124
|
}
|
|
155
125
|
/**
|
|
@@ -884,4 +854,4 @@ function cloneElement(element, overrides) {
|
|
|
884
854
|
return clone;
|
|
885
855
|
}
|
|
886
856
|
//#endregion
|
|
887
|
-
export { NAMESPACES, OFFICE_RELATIONSHIP_NAMESPACE_URIS, OOXML_NAMESPACE_SCOPE, WORDPROCESSINGML_NAMESPACE_URIS,
|
|
857
|
+
export { NAMESPACES, OFFICE_RELATIONSHIP_NAMESPACE_URIS, OOXML_NAMESPACE_SCOPE, WORDPROCESSINGML_NAMESPACE_URIS, cloneElement, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findAttributeByNamespaceUri, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findChildrenByNamespaceUri, findDeep, findWordprocessingChild, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffAttribute, parseOnOffChild, parseOnOffValue, parseTableMeasurementValue, parseXml, parseXmlDocument, parseXmlWithFastXmlParser, resolveAttributeNamespaceUri, selectAlternateContentBranch };
|