@stll/folio-core 0.43.0 → 0.45.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/__fixtures__/paragraphs.js +2 -2
- package/dist/ai-edits/headless.js +7 -5
- package/dist/ai-edits/index.d.ts +2 -2
- package/dist/ai-edits/index.js +2 -2
- package/dist/ai-edits/snapshot.js +13 -9
- package/dist/compare/content-alignment.js +94 -54
- package/dist/compare/inline-atoms.js +34 -20
- package/dist/compare/style-resources.js +6 -0
- package/dist/content-controls/mutateContentControls.js +4 -2
- package/dist/display-list/dom/renderDisplayListToDom.js +8 -8
- package/dist/document-operations.js +14 -3
- package/dist/docx/appVersionNormalization.d.ts +0 -18
- package/dist/docx/blockContentParser.js +8 -0
- package/dist/docx/blockRangeMarkers.d.ts +36 -0
- package/dist/docx/blockRangeMarkers.js +59 -0
- package/dist/docx/bookmarkParser.d.ts +2 -20
- package/dist/docx/bookmarkParser.js +6 -30
- package/dist/docx/borderParser.d.ts +13 -0
- package/dist/docx/borderParser.js +71 -0
- package/dist/docx/builtInStyles.d.ts +165 -0
- package/dist/docx/builtInStyles.js +239 -0
- package/dist/docx/commentIdNormalization.d.ts +3 -1
- package/dist/docx/commentIdNormalization.js +18 -1
- package/dist/docx/commentParser.d.ts +2 -1
- package/dist/docx/commentParser.js +80 -42
- package/dist/docx/commentReferenceNormalization.d.ts +4 -1
- package/dist/docx/commentReferenceNormalization.js +23 -14
- package/dist/docx/commentThreadKey.d.ts +18 -0
- package/dist/docx/commentThreadKey.js +22 -0
- package/dist/docx/danglingRelationshipReferences.d.ts +15 -0
- package/dist/docx/danglingRelationshipReferences.js +30 -0
- package/dist/docx/defaultParagraphStyle.d.ts +18 -1
- package/dist/docx/defaultParagraphStyle.js +23 -1
- package/dist/docx/diagramPreview.js +87 -27
- package/dist/docx/documentParser.d.ts +2 -1
- package/dist/docx/documentParser.js +2 -2
- package/dist/docx/drawingUtils.d.ts +8 -1
- package/dist/docx/drawingUtils.js +12 -3
- package/dist/docx/fieldParser.js +3 -5
- package/dist/docx/footnoteParser.d.ts +3 -2
- package/dist/docx/footnoteParser.js +19 -4
- package/dist/docx/groupDrawingParser.js +4 -4
- package/dist/docx/headerFooterRefParser.d.ts +4 -3
- package/dist/docx/headerFooterRefParser.js +42 -12
- package/dist/docx/headerFooterReferenceNormalization.d.ts +4 -1
- package/dist/docx/headerFooterReferenceNormalization.js +5 -1
- package/dist/docx/hyperlinkParser.js +13 -17
- package/dist/docx/imageParser.d.ts +10 -2
- package/dist/docx/imageParser.js +80 -30
- package/dist/docx/imageRawXml.d.ts +14 -1
- package/dist/docx/imageRawXml.js +35 -11
- package/dist/docx/markupRangeMarker.d.ts +15 -0
- package/dist/docx/markupRangeMarker.js +44 -0
- package/dist/docx/mathToMathml.js +12 -14
- package/dist/docx/nonVisualDrawingProps.d.ts +34 -0
- package/dist/docx/nonVisualDrawingProps.js +46 -0
- package/dist/docx/noteReferenceStyles.d.ts +29 -0
- package/dist/docx/noteReferenceStyles.js +70 -0
- package/dist/docx/numberingReferenceNormalization.d.ts +4 -1
- package/dist/docx/numberingReferenceNormalization.js +20 -1
- package/dist/docx/paraIdRangeNormalization.d.ts +0 -19
- package/dist/docx/paragraphParser.js +66 -99
- package/dist/docx/paragraphPropertySource.js +1 -0
- package/dist/docx/paragraphTextBoxEnrichment.js +3 -0
- package/dist/docx/paragraphTraversal.d.ts +37 -1
- package/dist/docx/paragraphTraversal.js +84 -1
- package/dist/docx/parseContext.d.ts +37 -0
- package/dist/docx/parseContext.js +67 -0
- package/dist/docx/parseWarningMessage.d.ts +6 -0
- package/dist/docx/parseWarningMessage.js +44 -0
- package/dist/docx/parser.js +83 -29
- package/dist/docx/previewBudget.d.ts +64 -0
- package/dist/docx/previewBudget.js +88 -0
- package/dist/docx/relsParser.d.ts +28 -11
- package/dist/docx/relsParser.js +26 -13
- package/dist/docx/revisionIdNormalization.js +96 -10
- package/dist/docx/rezip.js +80 -40
- package/dist/docx/runConsolidator.js +1 -2
- package/dist/docx/runParser.d.ts +8 -1
- package/dist/docx/runParser.js +30 -48
- package/dist/docx/sdtPropertiesPatch.js +24 -18
- package/dist/docx/sectionParser.d.ts +2 -1
- package/dist/docx/sectionParser.js +21 -65
- package/dist/docx/sectionReferenceHistory.js +2 -2
- package/dist/docx/selectiveSave.js +6 -6
- package/dist/docx/serializer/blockSdtSerializer.js +38 -26
- package/dist/docx/serializer/borderSerializer.d.ts +2 -3
- package/dist/docx/serializer/borderSerializer.js +13 -12
- package/dist/docx/serializer/commentSerializer.d.ts +41 -16
- package/dist/docx/serializer/commentSerializer.js +82 -72
- package/dist/docx/serializer/documentSerializer.d.ts +1 -5
- package/dist/docx/serializer/documentSerializer.js +6 -16
- package/dist/docx/serializer/fontTableSerializer.js +6 -6
- package/dist/docx/serializer/headerFooterSerializer.js +10 -5
- package/dist/docx/serializer/markupRangeAttributes.d.ts +8 -0
- package/dist/docx/serializer/markupRangeAttributes.js +24 -0
- package/dist/docx/serializer/noteSerializer.js +5 -0
- package/dist/docx/serializer/numberingSerializer.js +7 -6
- package/dist/docx/serializer/paragraphSerializer.d.ts +1 -5
- package/dist/docx/serializer/paragraphSerializer.js +47 -52
- package/dist/docx/serializer/partNamespaces.js +2 -2
- package/dist/docx/serializer/runSerializer.js +57 -31
- package/dist/docx/serializer/sectionPropertiesSerializer.js +11 -10
- package/dist/docx/serializer/settingsSerializer.js +4 -3
- package/dist/docx/serializer/stylesSerializer.js +6 -6
- package/dist/docx/serializer/tableSerializer.js +37 -21
- package/dist/docx/serializer/textFormattingSerializer.d.ts +2 -3
- package/dist/docx/serializer/textFormattingSerializer.js +29 -28
- package/dist/docx/serializer/themeSerializer.js +6 -6
- package/dist/docx/serializer/trackedChangeAttributes.js +2 -2
- package/dist/docx/serializer/xmlUtils.d.ts +1 -2
- package/dist/docx/serializer/xmlUtils.js +1 -13
- package/dist/docx/server/boundedArchive.d.ts +12 -0
- package/dist/docx/server/boundedArchive.js +20 -1
- package/dist/docx/server/build.js +8 -1
- package/dist/docx/server/createBilingualDocument.js +10 -18
- package/dist/docx/server/extractDocxText.js +3 -4
- package/dist/docx/server/validateDocxConformance.js +22 -1
- package/dist/docx/shadingParser.d.ts +6 -0
- package/dist/docx/shadingParser.js +32 -0
- package/dist/docx/shapeParser.js +10 -8
- package/dist/docx/styleParser.js +13 -87
- package/dist/docx/styleReferenceResolution.d.ts +36 -0
- package/dist/docx/styleReferenceResolution.js +51 -0
- package/dist/docx/tableLook.d.ts +57 -0
- package/dist/docx/tableLook.js +63 -0
- package/dist/docx/tableParser.d.ts +7 -9
- package/dist/docx/tableParser.js +64 -110
- package/dist/docx/textBoxParser.js +11 -6
- package/dist/docx/trackedMoveRangeNormalization.d.ts +3 -1
- package/dist/docx/trackedMoveRangeNormalization.js +11 -21
- package/dist/docx/transitionalSpelling.d.ts +13 -2
- package/dist/docx/transitionalSpelling.js +23 -1
- package/dist/docx/unzip.d.ts +23 -0
- package/dist/docx/unzip.js +32 -22
- package/dist/docx/verbatimCapture.js +5 -12
- package/dist/docx/vmlImageParser.js +5 -4
- package/dist/docx/vmlPreview.d.ts +1 -3
- package/dist/docx/vmlPreview.js +2 -30
- package/dist/docx/watermarkParser.js +2 -2
- package/dist/docx/xmlParser.d.ts +38 -33
- package/dist/docx/xmlParser.js +92 -47
- package/dist/docx/xmlResourceLimits.d.ts +89 -9
- package/dist/docx/xmlResourceLimits.js +105 -24
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
- package/dist/internal/paragraphFormattingSerialization.d.ts +2 -3
- package/dist/internal/paragraphFormattingSerialization.js +29 -8
- package/dist/layout-bridge/convert/footnoteLayout.js +2 -7
- package/dist/layout-engine/index.d.ts +2 -2
- package/dist/layout-engine/index.js +2 -2
- package/dist/layout-engine/measure/measureBlocks.js +1 -6
- package/dist/layout-engine/types.d.ts +8 -2
- package/dist/layout-engine/types.js +35 -2
- package/dist/layout-painter/renderImage.js +4 -3
- package/dist/layout-painter/renderParagraph.js +4 -3
- package/dist/managers/autoSaveCodec.js +2 -8
- package/dist/markdown/images.js +1 -4
- package/dist/markdown/index.js +1 -1
- package/dist/markdown/internals.d.ts +6 -1
- package/dist/markdown/internals.js +14 -1
- package/dist/markdown/renderBlock.js +35 -21
- package/dist/markdown/renderParagraph.js +14 -5
- package/dist/markdown/renderRuns.js +4 -3
- package/dist/markdown/renderTable.js +4 -3
- package/dist/markdown/trailers.js +41 -7
- package/dist/markdown/types.d.ts +3 -7
- package/dist/prosemirror/attrs/index.js +71 -5
- package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +11 -1
- package/dist/prosemirror/bookmarkBoundaryAttrs.js +18 -3
- package/dist/prosemirror/commands/image.js +1 -0
- package/dist/prosemirror/commands/index.d.ts +3 -3
- package/dist/prosemirror/commands/index.js +2 -2
- package/dist/prosemirror/commands/paragraph.d.ts +3 -3
- package/dist/prosemirror/commands/paragraph.js +2 -2
- package/dist/prosemirror/commentIdAllocator.js +2 -7
- package/dist/prosemirror/conversion/fromProseDoc.js +197 -68
- package/dist/prosemirror/conversion/toProseDoc.d.ts +1 -14
- package/dist/prosemirror/conversion/toProseDoc.js +458 -335
- package/dist/prosemirror/extensions/core/ParagraphExtension.d.ts +14 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +11 -6
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +3 -3
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.d.ts +4 -1
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +6 -2
- package/dist/prosemirror/extensions/features/pastedHeadingStyles.d.ts +7 -0
- package/dist/prosemirror/extensions/features/pastedHeadingStyles.js +74 -0
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +2 -3
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +11 -3
- package/dist/prosemirror/extensions/marks/markUtils.js +98 -19
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +7 -3
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +6 -1
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +8 -2
- package/dist/prosemirror/extensions/nodes/TableExtension.js +15 -1
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +8 -4
- package/dist/prosemirror/extensions/types.d.ts +2 -2
- package/dist/prosemirror/index.d.ts +3 -3
- package/dist/prosemirror/index.js +3 -3
- package/dist/prosemirror/insertOperations.d.ts +9 -2
- package/dist/prosemirror/insertOperations.js +9 -4
- package/dist/prosemirror/paragraphFormattingProvenance.d.ts +162 -0
- package/dist/prosemirror/paragraphFormattingProvenance.js +115 -0
- package/dist/prosemirror/plugins/documentStyles.d.ts +9 -1
- package/dist/prosemirror/plugins/documentStyles.js +11 -1
- package/dist/prosemirror/plugins/index.d.ts +2 -2
- package/dist/prosemirror/plugins/index.js +2 -2
- package/dist/prosemirror/plugins/revisionIds.d.ts +11 -2
- package/dist/prosemirror/plugins/revisionIds.js +21 -6
- package/dist/prosemirror/runFormattingReconciliation.js +3 -2
- package/dist/prosemirror/runStyleFormatting.d.ts +1 -1
- package/dist/prosemirror/schema/nodes.d.ts +81 -1
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +2 -0
- package/dist/prosemirror/styles/styleResolver.d.ts +9 -0
- package/dist/prosemirror/styles/styleResolver.js +12 -0
- package/dist/style-engine/styleEngine.d.ts +3 -0
- package/dist/style-engine/styleEngine.js +3 -0
- package/dist/style-sets/extract.js +1 -23
- package/dist/style-sets/stellaStyle.js +46 -39
- package/dist/style-sets/styleSetNormalization.d.ts +19 -0
- package/dist/style-sets/styleSetNormalization.js +99 -0
- package/dist/types/content.d.ts +2 -2
- package/dist/utils/base64.d.ts +36 -0
- package/dist/utils/base64.js +40 -0
- package/dist/utils/clipboard.js +2 -1
- package/dist/utils/createDocument.js +145 -20
- package/dist/utils/headingCollector.d.ts +8 -5
- package/dist/utils/headingCollector.js +23 -25
- package/dist/utils/tableOfContentsStyle.js +9 -2
- package/dist/utils/units.d.ts +10 -1
- package/dist/utils/units.js +12 -1
- package/dist/utils/urlSecurity.d.ts +8 -2
- package/dist/utils/urlSecurity.js +21 -3
- package/package.json +2 -2
- package/dist/docx/textWhitespace.d.ts +0 -4
- package/dist/docx/textWhitespace.js +0 -4
- package/dist/layout-bridge/engine/tableWidthUtils.d.ts +0 -6
- package/dist/layout-bridge/engine/tableWidthUtils.js +0 -25
- package/dist/markdown/headings.d.ts +0 -13
- package/dist/markdown/headings.js +0 -20
package/dist/docx/tableParser.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import { attachPendingRangeMarkers, attachTrailingRangeMarkers, isBlockRangeMarker } from "./blockRangeMarkers.js";
|
|
1
2
|
import { parseBookmarkEnd, parseBookmarkStart } from "./bookmarkParser.js";
|
|
2
3
|
import { appendBookmarkMarkerToLastParagraphInBlocks, appendBookmarkMarkerToLastParagraphInCells, prependBookmarkMarkersToFirstParagraphInBlocks, prependBookmarkMarkersToFirstParagraphInCell } from "./bookmarkPlacement.js";
|
|
4
|
+
import { parseBorderSpec } from "./borderParser.js";
|
|
3
5
|
import { parseParagraph } from "./paragraphParser.js";
|
|
4
6
|
import { enrichParagraphTextBoxes } from "./paragraphTextBoxEnrichment.js";
|
|
5
|
-
import {
|
|
7
|
+
import { FloatingTableXSpecSchema, FloatingTableYSpecSchema, TableCellTextDirectionSchema, narrowEnum } from "./parserEnums.js";
|
|
8
|
+
import { parseShading } from "./shadingParser.js";
|
|
9
|
+
import { TABLE_LOOK_FLAGS } from "./tableLook.js";
|
|
6
10
|
import { parsePropertyChangeInfo, parseTrackedChangeInfo } from "./trackedChangeInfo.js";
|
|
11
|
+
import { percentageSpelling, transitionalSlotEncoding } from "./transitionalSpelling.js";
|
|
7
12
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
8
|
-
import { cloneElement, findChild, findChildByLocalName, findChildren, getAttribute, getChildElements, getLocalName, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, parseTableMeasurementValue, selectAlternateContentBranch } from "./xmlParser.js";
|
|
13
|
+
import { cloneElement, findChild, findChildByLocalName, findChildren, getAttribute, getChildElements, getLocalName, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, parseOnOffAttribute, parseTableMeasurementValue, selectAlternateContentBranch } from "./xmlParser.js";
|
|
9
14
|
//#region src/docx/tableParser.ts
|
|
10
15
|
/**
|
|
11
16
|
* Sanity cap on `w:gridSpan` (and the derived table column count). Word's
|
|
@@ -22,15 +27,32 @@ const MAX_TABLE_COLUMNS = 63;
|
|
|
22
27
|
*/
|
|
23
28
|
function parseTableMeasurement(element) {
|
|
24
29
|
if (!element) return;
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
if (typeStr === "auto" || typeStr === "dxa" || typeStr === "nil" || typeStr === "pct") type = typeStr;
|
|
30
|
+
const declared = getAttribute(element, "w", "type");
|
|
31
|
+
const type = tableWidthType(element, declared === "auto" || declared === "dxa" || declared === "nil" || declared === "pct" ? declared : void 0);
|
|
28
32
|
return {
|
|
29
33
|
value: parseTableMeasurementValue(element, type) ?? 0,
|
|
30
34
|
type
|
|
31
35
|
};
|
|
32
36
|
}
|
|
33
37
|
/**
|
|
38
|
+
* What unit `w:w` counts in.
|
|
39
|
+
*
|
|
40
|
+
* `w:type` is optional on `CT_TblWidth` and the schema gives it no default, so
|
|
41
|
+
* reading an absent one as `dxa` turned `w:w="50%"` into 50 twips: a
|
|
42
|
+
* full-width table became a hairline. `w:w` is `ST_MeasurementOrPercent`, so a
|
|
43
|
+
* value spelled with a `%` is a percentage whatever `w:type` says — the
|
|
44
|
+
* spelling carries its own unit, and no number of twips is ever written that
|
|
45
|
+
* way. `auto` and `nil` are left alone: neither reads `w:w` as a width at all.
|
|
46
|
+
*/
|
|
47
|
+
const tableWidthType = (element, declared) => {
|
|
48
|
+
if (declared === "auto" || declared === "nil") return declared;
|
|
49
|
+
const raw = getAttribute(element, "w", "w");
|
|
50
|
+
const spelledAsPercent = raw !== null && percentageSpelling(raw) !== void 0;
|
|
51
|
+
const slotTakesPercent = transitionalSlotEncoding(element.namespaceUri, getLocalName(element.name), "w")?.percent !== void 0;
|
|
52
|
+
if (spelledAsPercent && slotTakesPercent) return "pct";
|
|
53
|
+
return declared ?? "dxa";
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
34
56
|
* Parse width from an element (shorthand for common case)
|
|
35
57
|
*/
|
|
36
58
|
function parseWidth(element) {
|
|
@@ -42,34 +64,6 @@ function parseWidth(element) {
|
|
|
42
64
|
* @param element - Border element (w:top, w:bottom, etc.)
|
|
43
65
|
* @returns Parsed border or undefined
|
|
44
66
|
*/
|
|
45
|
-
function parseBorderSpec(element) {
|
|
46
|
-
if (!element) return;
|
|
47
|
-
const rawStyle = getAttribute(element, "w", "val") ?? "none";
|
|
48
|
-
const border = { style: narrowEnum(rawStyle, BorderStyleSchema) ?? rawStyle };
|
|
49
|
-
const sz = parseNumericAttribute(element, "w", "sz");
|
|
50
|
-
if (sz !== void 0) border.size = sz;
|
|
51
|
-
const space = parseNumericAttribute(element, "w", "space");
|
|
52
|
-
if (space !== void 0) border.space = space;
|
|
53
|
-
const color = getAttribute(element, "w", "color");
|
|
54
|
-
const themeColor = getAttribute(element, "w", "themeColor");
|
|
55
|
-
const themeTint = getAttribute(element, "w", "themeTint");
|
|
56
|
-
const themeShade = getAttribute(element, "w", "themeShade");
|
|
57
|
-
if (color || themeColor || themeTint || themeShade) {
|
|
58
|
-
const colorVal = {};
|
|
59
|
-
if (color === "auto") colorVal.auto = true;
|
|
60
|
-
else if (color !== null) colorVal.rgb = color;
|
|
61
|
-
const validatedThemeColor = narrowEnum(themeColor, ThemeColorSlotSchema);
|
|
62
|
-
if (validatedThemeColor) colorVal.themeColor = validatedThemeColor;
|
|
63
|
-
if (themeTint !== null) colorVal.themeTint = themeTint;
|
|
64
|
-
if (themeShade !== null) colorVal.themeShade = themeShade;
|
|
65
|
-
border.color = colorVal;
|
|
66
|
-
}
|
|
67
|
-
const shadow = getAttribute(element, "w", "shadow");
|
|
68
|
-
if (shadow === "1" || shadow === "true") border.shadow = true;
|
|
69
|
-
const frame = getAttribute(element, "w", "frame");
|
|
70
|
-
if (frame === "1" || frame === "true") border.frame = true;
|
|
71
|
-
return border;
|
|
72
|
-
}
|
|
73
67
|
/**
|
|
74
68
|
* Parse table borders (w:tblBorders or w:tcBorders)
|
|
75
69
|
*
|
|
@@ -125,33 +119,13 @@ function parseCellMargins(marginsElement) {
|
|
|
125
119
|
return margins;
|
|
126
120
|
}
|
|
127
121
|
/**
|
|
128
|
-
*
|
|
122
|
+
* Read a `w:tblLook` (the only reader; `styleParser` calls this one).
|
|
129
123
|
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const shading = {};
|
|
136
|
-
const fillStr = getAttribute(shdElement, "w", "fill");
|
|
137
|
-
if (fillStr && fillStr !== "auto") shading.fill = { rgb: fillStr };
|
|
138
|
-
const themeFill = narrowEnum(getAttribute(shdElement, "w", "themeFill"), ThemeColorSlotSchema);
|
|
139
|
-
if (themeFill) {
|
|
140
|
-
shading.fill = { themeColor: themeFill };
|
|
141
|
-
const themeFillTint = getAttribute(shdElement, "w", "themeFillTint");
|
|
142
|
-
if (themeFillTint) shading.fill.themeTint = themeFillTint;
|
|
143
|
-
const themeFillShade = getAttribute(shdElement, "w", "themeFillShade");
|
|
144
|
-
if (themeFillShade) shading.fill.themeShade = themeFillShade;
|
|
145
|
-
}
|
|
146
|
-
const colorStr = getAttribute(shdElement, "w", "color");
|
|
147
|
-
if (colorStr && colorStr !== "auto") shading.color = { rgb: colorStr };
|
|
148
|
-
const pattern = narrowEnum(getAttribute(shdElement, "w", "val"), ShadingPatternSchema);
|
|
149
|
-
if (pattern) shading.pattern = pattern;
|
|
150
|
-
if (Object.keys(shading).length === 0) return;
|
|
151
|
-
return shading;
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Parse table look flags (w:tblLook)
|
|
124
|
+
* What the author wrote, and nothing more: `w:val` verbatim and each flag as
|
|
125
|
+
* stated, absent, `false` or `true`. Folding `w:val`'s bits into the flags here
|
|
126
|
+
* would forget which of the two the document said, and writing the result back
|
|
127
|
+
* would invent attributes the author never had. `resolveTableLook` owns the
|
|
128
|
+
* other direction.
|
|
155
129
|
*
|
|
156
130
|
* @param lookElement - The w:tblLook element
|
|
157
131
|
* @returns Parsed table look or undefined
|
|
@@ -159,29 +133,11 @@ function parseShading(shdElement) {
|
|
|
159
133
|
function parseTableLook(lookElement) {
|
|
160
134
|
if (!lookElement) return;
|
|
161
135
|
const look = {};
|
|
162
|
-
const firstRow = getAttribute(lookElement, "w", "firstRow");
|
|
163
|
-
if (firstRow === "1" || firstRow === "true") look.firstRow = true;
|
|
164
|
-
const lastRow = getAttribute(lookElement, "w", "lastRow");
|
|
165
|
-
if (lastRow === "1" || lastRow === "true") look.lastRow = true;
|
|
166
|
-
const firstColumn = getAttribute(lookElement, "w", "firstColumn");
|
|
167
|
-
if (firstColumn === "1" || firstColumn === "true") look.firstColumn = true;
|
|
168
|
-
const lastColumn = getAttribute(lookElement, "w", "lastColumn");
|
|
169
|
-
if (lastColumn === "1" || lastColumn === "true") look.lastColumn = true;
|
|
170
|
-
const noHBand = getAttribute(lookElement, "w", "noHBand");
|
|
171
|
-
if (noHBand === "1" || noHBand === "true") look.noHBand = true;
|
|
172
|
-
const noVBand = getAttribute(lookElement, "w", "noVBand");
|
|
173
|
-
if (noVBand === "1" || noVBand === "true") look.noVBand = true;
|
|
174
136
|
const val = getAttribute(lookElement, "w", "val");
|
|
175
|
-
if (val)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (flags & 64) look.lastRow = true;
|
|
180
|
-
if (flags & 128) look.firstColumn = true;
|
|
181
|
-
if (flags & 256) look.lastColumn = true;
|
|
182
|
-
if (flags & 512) look.noHBand = true;
|
|
183
|
-
if (flags & 1024) look.noVBand = true;
|
|
184
|
-
}
|
|
137
|
+
if (val !== null && val !== "") look.val = val;
|
|
138
|
+
for (const flag of TABLE_LOOK_FLAGS) {
|
|
139
|
+
const stated = parseOnOffAttribute(lookElement, "w", flag);
|
|
140
|
+
if (stated !== void 0) look[flag] = stated;
|
|
185
141
|
}
|
|
186
142
|
if (Object.keys(look).length === 0) return;
|
|
187
143
|
return look;
|
|
@@ -430,30 +386,18 @@ function parseTableRowProperties(trPrElement) {
|
|
|
430
386
|
function parseConditionalFormatStyle(cnfElement) {
|
|
431
387
|
if (!cnfElement) return;
|
|
432
388
|
const style = {};
|
|
433
|
-
|
|
434
|
-
if (
|
|
435
|
-
|
|
436
|
-
if (
|
|
437
|
-
|
|
438
|
-
if (
|
|
439
|
-
|
|
440
|
-
if (
|
|
441
|
-
|
|
442
|
-
if (
|
|
443
|
-
|
|
444
|
-
if (
|
|
445
|
-
const oddVBand = getAttribute(cnfElement, "w", "oddVBand");
|
|
446
|
-
if (oddVBand === "1" || oddVBand === "true") style.oddVBand = true;
|
|
447
|
-
const evenVBand = getAttribute(cnfElement, "w", "evenVBand");
|
|
448
|
-
if (evenVBand === "1" || evenVBand === "true") style.evenVBand = true;
|
|
449
|
-
const nwCell = getAttribute(cnfElement, "w", "firstRowFirstColumn");
|
|
450
|
-
if (nwCell === "1" || nwCell === "true") style.nwCell = true;
|
|
451
|
-
const neCell = getAttribute(cnfElement, "w", "firstRowLastColumn");
|
|
452
|
-
if (neCell === "1" || neCell === "true") style.neCell = true;
|
|
453
|
-
const swCell = getAttribute(cnfElement, "w", "lastRowFirstColumn");
|
|
454
|
-
if (swCell === "1" || swCell === "true") style.swCell = true;
|
|
455
|
-
const seCell = getAttribute(cnfElement, "w", "lastRowLastColumn");
|
|
456
|
-
if (seCell === "1" || seCell === "true") style.seCell = true;
|
|
389
|
+
if (parseOnOffAttribute(cnfElement, "w", "firstRow") === true) style.firstRow = true;
|
|
390
|
+
if (parseOnOffAttribute(cnfElement, "w", "lastRow") === true) style.lastRow = true;
|
|
391
|
+
if (parseOnOffAttribute(cnfElement, "w", "firstColumn") === true) style.firstColumn = true;
|
|
392
|
+
if (parseOnOffAttribute(cnfElement, "w", "lastColumn") === true) style.lastColumn = true;
|
|
393
|
+
if (parseOnOffAttribute(cnfElement, "w", "oddHBand") === true) style.oddHBand = true;
|
|
394
|
+
if (parseOnOffAttribute(cnfElement, "w", "evenHBand") === true) style.evenHBand = true;
|
|
395
|
+
if (parseOnOffAttribute(cnfElement, "w", "oddVBand") === true) style.oddVBand = true;
|
|
396
|
+
if (parseOnOffAttribute(cnfElement, "w", "evenVBand") === true) style.evenVBand = true;
|
|
397
|
+
if (parseOnOffAttribute(cnfElement, "w", "firstRowFirstColumn") === true) style.nwCell = true;
|
|
398
|
+
if (parseOnOffAttribute(cnfElement, "w", "firstRowLastColumn") === true) style.neCell = true;
|
|
399
|
+
if (parseOnOffAttribute(cnfElement, "w", "lastRowFirstColumn") === true) style.swCell = true;
|
|
400
|
+
if (parseOnOffAttribute(cnfElement, "w", "lastRowLastColumn") === true) style.seCell = true;
|
|
457
401
|
const val = getAttribute(cnfElement, "w", "val");
|
|
458
402
|
if (val && val.length === 12) {
|
|
459
403
|
if (val[0] === "1") style.firstRow = true;
|
|
@@ -530,6 +474,7 @@ function withContainerXmlns(options, element) {
|
|
|
530
474
|
function parseCellContent(tcElement, styles, theme, numbering, rels, media, options) {
|
|
531
475
|
const content = [];
|
|
532
476
|
const pendingBookmarkMarkers = [];
|
|
477
|
+
const pendingRangeMarkers = [];
|
|
533
478
|
const elements = getChildElements(tcElement);
|
|
534
479
|
const parseCellChild = (child, childOptions = options) => {
|
|
535
480
|
if (!child.name) return;
|
|
@@ -538,6 +483,7 @@ function parseCellContent(tcElement, styles, theme, numbering, rels, media, opti
|
|
|
538
483
|
const para = parseParagraph(child, styles, theme, numbering, rels, media, childOptions);
|
|
539
484
|
enrichParagraphTextBoxes(para, child, styles, theme, numbering, rels, media, parseTable);
|
|
540
485
|
prependPendingBookmarkMarkers(para, pendingBookmarkMarkers);
|
|
486
|
+
attachPendingRangeMarkers(para, pendingRangeMarkers);
|
|
541
487
|
content.push(para);
|
|
542
488
|
return;
|
|
543
489
|
}
|
|
@@ -545,6 +491,7 @@ function parseCellContent(tcElement, styles, theme, numbering, rels, media, opti
|
|
|
545
491
|
const table = parseTable(child, styles, theme, numbering, rels, media, childOptions);
|
|
546
492
|
if (!table) return;
|
|
547
493
|
if (prependBookmarkMarkersToFirstParagraphInBlocks([table], pendingBookmarkMarkers)) pendingBookmarkMarkers.length = 0;
|
|
494
|
+
attachPendingRangeMarkers(table, pendingRangeMarkers);
|
|
548
495
|
content.push(table);
|
|
549
496
|
return;
|
|
550
497
|
}
|
|
@@ -565,7 +512,9 @@ function parseCellContent(tcElement, styles, theme, numbering, rels, media, opti
|
|
|
565
512
|
if (localName === "bookmarkStart" || localName === "bookmarkEnd") {
|
|
566
513
|
const marker = parseBookmarkMarker(child, localName);
|
|
567
514
|
if (!appendBookmarkMarkerToLastParagraphInBlocks(content, marker)) pendingBookmarkMarkers.push(marker);
|
|
515
|
+
return;
|
|
568
516
|
}
|
|
517
|
+
if (isBlockRangeMarker(localName)) pendingRangeMarkers.push(captureVerbatimXml(child));
|
|
569
518
|
};
|
|
570
519
|
for (const child of elements) parseCellChild(child);
|
|
571
520
|
if (content.length === 0) content.push({
|
|
@@ -573,6 +522,7 @@ function parseCellContent(tcElement, styles, theme, numbering, rels, media, opti
|
|
|
573
522
|
content: [...pendingBookmarkMarkers]
|
|
574
523
|
});
|
|
575
524
|
else if (pendingBookmarkMarkers.length > 0) appendBookmarkMarkersToLastParagraphInBlocks(content, pendingBookmarkMarkers);
|
|
525
|
+
attachTrailingRangeMarkers(content, pendingRangeMarkers);
|
|
576
526
|
return content;
|
|
577
527
|
}
|
|
578
528
|
/**
|
|
@@ -747,10 +697,14 @@ function parseTable(tblElement, styles, theme, numbering, rels, media, options)
|
|
|
747
697
|
const gridElement = findChild(tblElement, "w", "tblGrid");
|
|
748
698
|
const columnWidths = parseTableGrid(gridElement);
|
|
749
699
|
if (columnWidths) table.columnWidths = columnWidths;
|
|
750
|
-
if (gridElement)
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
700
|
+
if (gridElement) {
|
|
701
|
+
const gridChange = findChild(gridElement, "w", "tblGridChange");
|
|
702
|
+
table.formatting = {
|
|
703
|
+
...table.formatting,
|
|
704
|
+
gridSourceXml: captureVerbatimXml(gridElement),
|
|
705
|
+
...gridChange ? { gridChangeXml: captureVerbatimXml(gridChange) } : {}
|
|
706
|
+
};
|
|
707
|
+
}
|
|
754
708
|
const tableOptions = withContainerXmlns(options, tblElement);
|
|
755
709
|
const rowsWithGridOffsets = /* @__PURE__ */ new Set();
|
|
756
710
|
const parseTableChild = (child, childOptions = tableOptions) => {
|
|
@@ -878,4 +832,4 @@ function isFloatingTable(table) {
|
|
|
878
832
|
return table.formatting?.floating !== void 0;
|
|
879
833
|
}
|
|
880
834
|
//#endregion
|
|
881
|
-
export { MAX_TABLE_COLUMNS, getHeaderRows, getTableColumnCount, getTableRowCount, getTableText, hasHeaderRow, isCellHorizontallyMerged, isCellMergeContinuation, isCellMergeStart, isFloatingTable,
|
|
835
|
+
export { MAX_TABLE_COLUMNS, getHeaderRows, getTableColumnCount, getTableRowCount, getTableText, hasHeaderRow, isCellHorizontallyMerged, isCellMergeContinuation, isCellMergeStart, isFloatingTable, parseCellMargins, parseConditionalFormatStyle, parseFloatingTableProperties, parseTable, parseTableBorders, parseTableCell, parseTableCellProperties, parseTableGrid, parseTableLook, parseTableMeasurement, parseTableProperties, parseTableRow, parseTableRowProperties };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { emuToPixels } from "../utils/units.js";
|
|
2
2
|
import { parseAnchorPosition, parseAnchorWrap, parseFill, parseOutline, resolveColorValueToHex } from "./drawingUtils.js";
|
|
3
|
-
import {
|
|
3
|
+
import { parseNonVisualDrawingNames } from "./nonVisualDrawingProps.js";
|
|
4
|
+
import { findByFullName, findChildByLocalName, findChildByNamespaceUri, findChildrenByLocalName, findChildrenByNamespaceUri, getAttribute, getChildElements, parseNumericAttribute, parseOnOffAttribute } from "./xmlParser.js";
|
|
4
5
|
//#region src/docx/textBoxParser.ts
|
|
5
6
|
const DRAWINGML_MAIN_NAMESPACE_URIS = /* @__PURE__ */ new Set(["http://schemas.openxmlformats.org/drawingml/2006/main", "http://purl.oclc.org/ooxml/drawingml/main"]);
|
|
6
7
|
const WORDPROCESSING_SHAPE_NAMESPACE_URIS = /* @__PURE__ */ new Set(["http://schemas.microsoft.com/office/word/2010/wordprocessingShape"]);
|
|
@@ -30,9 +31,9 @@ function parseBodyProperties(bodyPr) {
|
|
|
30
31
|
else if (findChildByLocalName(bodyPr, "noAutofit")) result.autoFit = "none";
|
|
31
32
|
const verticalAlign = parseTextBoxVerticalAlign(getAttribute(bodyPr, null, "anchor"));
|
|
32
33
|
if (verticalAlign) result.verticalAlign = verticalAlign;
|
|
33
|
-
const fromWordArt =
|
|
34
|
+
const fromWordArt = parseOnOffAttribute(bodyPr, null, "fromWordArt");
|
|
34
35
|
const warp = findChildByNamespaceUri(bodyPr, DRAWINGML_MAIN_NAMESPACE_URIS, "prstTxWarp");
|
|
35
|
-
if (fromWordArt !==
|
|
36
|
+
if (fromWordArt !== void 0 || warp) {
|
|
36
37
|
const adjustments = warp ? findChildrenByNamespaceUri(findChildByNamespaceUri(warp, DRAWINGML_MAIN_NAMESPACE_URIS, "avLst"), DRAWINGML_MAIN_NAMESPACE_URIS, "gd").map((adjustment) => {
|
|
37
38
|
const name = getAttribute(adjustment, null, "name");
|
|
38
39
|
const formula = getAttribute(adjustment, null, "fmla");
|
|
@@ -43,7 +44,7 @@ function parseBodyProperties(bodyPr) {
|
|
|
43
44
|
}).filter((adjustment) => adjustment !== void 0) : [];
|
|
44
45
|
const preset = warp ? getAttribute(warp, null, "prst") : null;
|
|
45
46
|
result.wordArt = {
|
|
46
|
-
...fromWordArt !==
|
|
47
|
+
...fromWordArt !== void 0 ? { fromWordArt } : {},
|
|
47
48
|
...preset !== null ? { preset } : {},
|
|
48
49
|
...adjustments.length > 0 ? { adjustments } : {}
|
|
49
50
|
};
|
|
@@ -148,13 +149,15 @@ function parseTextBox(drawingEl) {
|
|
|
148
149
|
};
|
|
149
150
|
const docPr = findByFullName(container, "wp:docPr");
|
|
150
151
|
const id = docPr ? getAttribute(docPr, null, "id") ?? void 0 : void 0;
|
|
152
|
+
const names = parseNonVisualDrawingNames(docPr);
|
|
151
153
|
const fill = parseFill(spPr ?? null);
|
|
152
154
|
const outline = parseOutline(spPr ?? null);
|
|
153
155
|
const bodyProps = parseBodyProperties(bodyPr ?? null);
|
|
154
156
|
const textBox = {
|
|
155
157
|
type: "textBox",
|
|
156
158
|
size,
|
|
157
|
-
content: []
|
|
159
|
+
content: [],
|
|
160
|
+
...names
|
|
158
161
|
};
|
|
159
162
|
if (id) textBox.id = id;
|
|
160
163
|
if (fill) textBox.fill = fill;
|
|
@@ -193,13 +196,15 @@ function parseTextBoxFromShape(wsp, size, position, wrap) {
|
|
|
193
196
|
const bodyPr = findChildByNamespaceUri(wsp, WORDPROCESSING_SHAPE_NAMESPACE_URIS, "bodyPr");
|
|
194
197
|
const cNvPr = wspChildren.find((el) => el.name === "wps:cNvPr");
|
|
195
198
|
const id = cNvPr ? getAttribute(cNvPr, null, "id") ?? void 0 : void 0;
|
|
199
|
+
const names = parseNonVisualDrawingNames(cNvPr);
|
|
196
200
|
const fill = parseFill(spPr ?? null);
|
|
197
201
|
const outline = parseOutline(spPr ?? null);
|
|
198
202
|
const bodyProps = parseBodyProperties(bodyPr ?? null);
|
|
199
203
|
const textBox = {
|
|
200
204
|
type: "textBox",
|
|
201
205
|
size,
|
|
202
|
-
content: []
|
|
206
|
+
content: [],
|
|
207
|
+
...names
|
|
203
208
|
};
|
|
204
209
|
if (id) textBox.id = id;
|
|
205
210
|
if (fill) textBox.fill = fill;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
//#region src/docx/trackedMoveRangeNormalization.d.ts
|
|
3
|
+
/** The code this normalisation is reported under, owned here, not at the caller. */
|
|
4
|
+
declare const UNBALANCED_MOVE_RANGE_WARNING: "unbalanced-move-range";
|
|
3
5
|
type NormalizeTrackedMoveRangesInput = {
|
|
4
6
|
documentBody: document_d_exports.DocumentBody;
|
|
5
7
|
headers?: Map<string, document_d_exports.HeaderFooter>;
|
|
@@ -12,4 +14,4 @@ type NormalizeTrackedMoveRangesResult = {
|
|
|
12
14
|
};
|
|
13
15
|
declare const normalizeTrackedMoveRanges: ({ documentBody, headers, footers, footnotes, endnotes }: NormalizeTrackedMoveRangesInput) => NormalizeTrackedMoveRangesResult;
|
|
14
16
|
//#endregion
|
|
15
|
-
export { normalizeTrackedMoveRanges };
|
|
17
|
+
export { UNBALANCED_MOVE_RANGE_WARNING, normalizeTrackedMoveRanges };
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { visitDocxParagraphs } from "./paragraphTraversal.js";
|
|
1
|
+
import { InlineContentRemovals, visitDocxParagraphs, visitInlineContentSlots } from "./paragraphTraversal.js";
|
|
2
|
+
import { PARSE_WARNING_CODES } from "@stll/docx-core/model";
|
|
2
3
|
//#region src/docx/trackedMoveRangeNormalization.ts
|
|
4
|
+
/** The code this normalisation is reported under, owned here, not at the caller. */
|
|
5
|
+
const UNBALANCED_MOVE_RANGE_WARNING = PARSE_WARNING_CODES.unbalancedMoveRange;
|
|
3
6
|
const normalizeTrackedMoveRanges = ({ documentBody, headers, footers, footnotes, endnotes }) => {
|
|
4
7
|
const rangeMarkers = [];
|
|
5
8
|
visitDocxParagraphs({
|
|
@@ -9,10 +12,10 @@ const normalizeTrackedMoveRanges = ({ documentBody, headers, footers, footnotes,
|
|
|
9
12
|
footnotes,
|
|
10
13
|
endnotes
|
|
11
14
|
}, (paragraph) => {
|
|
12
|
-
|
|
13
|
-
const marker = toMoveRangeMarkerRef(
|
|
15
|
+
visitInlineContentSlots(paragraph, ({ content, index, item }) => {
|
|
16
|
+
const marker = toMoveRangeMarkerRef(content, index, item);
|
|
14
17
|
if (marker) rangeMarkers.push(marker);
|
|
15
|
-
}
|
|
18
|
+
});
|
|
16
19
|
});
|
|
17
20
|
return { removedUnbalancedMoveRangeMarkers: removeUnbalancedMoveRangeMarkers(rangeMarkers) };
|
|
18
21
|
};
|
|
@@ -27,14 +30,9 @@ const removeUnbalancedMoveRangeMarkers = (rangeMarkers) => {
|
|
|
27
30
|
}
|
|
28
31
|
byRange.set(key, [marker]);
|
|
29
32
|
}
|
|
30
|
-
const removals =
|
|
33
|
+
const removals = new InlineContentRemovals();
|
|
31
34
|
const markForRemoval = (marker) => {
|
|
32
|
-
|
|
33
|
-
if (indexes) {
|
|
34
|
-
indexes.add(marker.index);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
removals.set(marker.content, /* @__PURE__ */ new Set([marker.index]));
|
|
35
|
+
removals.mark(marker);
|
|
38
36
|
};
|
|
39
37
|
for (const markers of byRange.values()) {
|
|
40
38
|
let openStart = null;
|
|
@@ -55,15 +53,7 @@ const removeUnbalancedMoveRangeMarkers = (rangeMarkers) => {
|
|
|
55
53
|
}
|
|
56
54
|
if (openStart) markForRemoval(openStart);
|
|
57
55
|
}
|
|
58
|
-
|
|
59
|
-
for (const [content, indexes] of removals.entries()) {
|
|
60
|
-
if (indexes.size === 0) continue;
|
|
61
|
-
const nextContent = content.filter((_, index) => !indexes.has(index));
|
|
62
|
-
removedCount += content.length - nextContent.length;
|
|
63
|
-
content.length = 0;
|
|
64
|
-
content.push(...nextContent);
|
|
65
|
-
}
|
|
66
|
-
return removedCount;
|
|
56
|
+
return removals.apply();
|
|
67
57
|
};
|
|
68
58
|
const toMoveRangeMarkerRef = (content, index, marker) => {
|
|
69
59
|
if (marker.type === "moveFromRangeStart") return {
|
|
@@ -97,4 +87,4 @@ const toMoveRangeMarkerRef = (content, index, marker) => {
|
|
|
97
87
|
return null;
|
|
98
88
|
};
|
|
99
89
|
//#endregion
|
|
100
|
-
export { normalizeTrackedMoveRanges };
|
|
90
|
+
export { UNBALANCED_MOVE_RANGE_WARNING, normalizeTrackedMoveRanges };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SlotEncoding } from "./strictValueEncodings.gen.js";
|
|
1
|
+
import { PercentUnit, SlotEncoding } from "./strictValueEncodings.gen.js";
|
|
2
2
|
//#region src/docx/transitionalSpelling.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* The Transitional URI a Strict one stands for, or the URI unchanged.
|
|
@@ -12,6 +12,17 @@ declare const toTransitionalNamespaceUri: (uri: string) => string;
|
|
|
12
12
|
declare const isStrictNamespaceUri: (uri: string) => boolean;
|
|
13
13
|
/** Namespaces ECMA-376 Part 4 republished; nothing else starts with this. */
|
|
14
14
|
declare const STRICT_URI_PREFIX = "http://purl.oclc.org/ooxml/";
|
|
15
|
+
/** How many of a unit's numbers make one whole percent. */
|
|
16
|
+
declare const NUMBERS_PER_PERCENT: Readonly<Record<PercentUnit, number>>;
|
|
17
|
+
/**
|
|
18
|
+
* The percent a value spells out, or undefined when it is not spelled as one.
|
|
19
|
+
*
|
|
20
|
+
* A value written `50%` says what it is; a value written `2500` needs its
|
|
21
|
+
* slot's unit to be read. Callers that must tell the two apart — the
|
|
22
|
+
* verbatim-capture conversion, and `CT_TblWidth`, whose `w:type` is optional —
|
|
23
|
+
* ask here rather than testing for a `%` themselves.
|
|
24
|
+
*/
|
|
25
|
+
declare const percentageSpelling: (value: string) => number | undefined;
|
|
15
26
|
/**
|
|
16
27
|
* How one slot's Transitional type spells its value as a number.
|
|
17
28
|
*
|
|
@@ -20,4 +31,4 @@ declare const STRICT_URI_PREFIX = "http://purl.oclc.org/ooxml/";
|
|
|
20
31
|
*/
|
|
21
32
|
declare const transitionalSlotEncoding: (namespaceUri: string | undefined, elementLocalName: string, attributeLocalName?: string) => SlotEncoding | undefined;
|
|
22
33
|
//#endregion
|
|
23
|
-
export { STRICT_URI_PREFIX, isStrictNamespaceUri, toTransitionalNamespaceUri, transitionalSlotEncoding };
|
|
34
|
+
export { NUMBERS_PER_PERCENT, STRICT_URI_PREFIX, isStrictNamespaceUri, percentageSpelling, toTransitionalNamespaceUri, transitionalSlotEncoding };
|
|
@@ -21,6 +21,28 @@ const toTransitionalNamespaceUri = (uri) => TRANSITIONAL_NAMESPACE_BY_STRICT_URI
|
|
|
21
21
|
const isStrictNamespaceUri = (uri) => TRANSITIONAL_NAMESPACE_BY_STRICT_URI.has(uri);
|
|
22
22
|
/** Namespaces ECMA-376 Part 4 republished; nothing else starts with this. */
|
|
23
23
|
const STRICT_URI_PREFIX = "http://purl.oclc.org/ooxml/";
|
|
24
|
+
/** `-?12.5%`: the one shape ECMA-376 gives a percentage that carries its unit. */
|
|
25
|
+
const PERCENTAGE = /^(-?[0-9]+(?:\.[0-9]+)?)%$/u;
|
|
26
|
+
/** How many of a unit's numbers make one whole percent. */
|
|
27
|
+
const NUMBERS_PER_PERCENT = {
|
|
28
|
+
fiftiethPercent: 50,
|
|
29
|
+
thousandthPercent: 1e3,
|
|
30
|
+
wholePercent: 1
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The percent a value spells out, or undefined when it is not spelled as one.
|
|
34
|
+
*
|
|
35
|
+
* A value written `50%` says what it is; a value written `2500` needs its
|
|
36
|
+
* slot's unit to be read. Callers that must tell the two apart — the
|
|
37
|
+
* verbatim-capture conversion, and `CT_TblWidth`, whose `w:type` is optional —
|
|
38
|
+
* ask here rather than testing for a `%` themselves.
|
|
39
|
+
*/
|
|
40
|
+
const percentageSpelling = (value) => {
|
|
41
|
+
const match = PERCENTAGE.exec(value.trim());
|
|
42
|
+
if (match === null) return;
|
|
43
|
+
const percent = Number(match[1]);
|
|
44
|
+
return Number.isNaN(percent) ? void 0 : percent;
|
|
45
|
+
};
|
|
24
46
|
/**
|
|
25
47
|
* How one slot's Transitional type spells its value as a number.
|
|
26
48
|
*
|
|
@@ -33,4 +55,4 @@ const transitionalSlotEncoding = (namespaceUri, elementLocalName, attributeLocal
|
|
|
33
55
|
return TRANSITIONAL_SLOT_ENCODINGS.get(attributeLocalName === void 0 ? slot : `${slot} @${attributeLocalName}`);
|
|
34
56
|
};
|
|
35
57
|
//#endregion
|
|
36
|
-
export { STRICT_URI_PREFIX, isStrictNamespaceUri, toTransitionalNamespaceUri, transitionalSlotEncoding };
|
|
58
|
+
export { NUMBERS_PER_PERCENT, STRICT_URI_PREFIX, isStrictNamespaceUri, percentageSpelling, toTransitionalNamespaceUri, transitionalSlotEncoding };
|
package/dist/docx/unzip.d.ts
CHANGED
|
@@ -6,10 +6,33 @@ declare class DocxSecurityError extends Error {
|
|
|
6
6
|
type DocxUnzipLimits = {
|
|
7
7
|
maxInputBytes: number;
|
|
8
8
|
maxFiles: number;
|
|
9
|
+
/**
|
|
10
|
+
* Inflated bytes allowed in one XML part.
|
|
11
|
+
*
|
|
12
|
+
* A byte ceiling bounds the markup, not what parsing it allocates: a parsed
|
|
13
|
+
* tree costs 3x to 15x the part's bytes, and the cheapest element to write
|
|
14
|
+
* is the most expensive per byte. Use `maxXmlElementsPerPart` and the
|
|
15
|
+
* package bounds to cap memory; this one caps text-dominated parts, where
|
|
16
|
+
* bytes and cost do track each other.
|
|
17
|
+
*/
|
|
9
18
|
maxXmlBytes: number;
|
|
10
19
|
maxMediaBytes: number;
|
|
11
20
|
maxFontBytes: number;
|
|
21
|
+
/**
|
|
22
|
+
* Inflated bytes allowed across every entry in the package.
|
|
23
|
+
*
|
|
24
|
+
* Same caveat as `maxXmlBytes`: this is a ceiling on what passes through
|
|
25
|
+
* memory as bytes, not on what the parsed structure retains.
|
|
26
|
+
*/
|
|
12
27
|
maxTotalUncompressedBytes: number;
|
|
28
|
+
/** Elements allowed in one XML part, counted before any tree is built. */
|
|
29
|
+
maxXmlElementsPerPart: number;
|
|
30
|
+
/** Attributes allowed in one XML part, counted before any tree is built. */
|
|
31
|
+
maxXmlAttributesPerPart: number;
|
|
32
|
+
/** Elements allowed across every XML part in the package. */
|
|
33
|
+
maxXmlElementsPerPackage: number;
|
|
34
|
+
/** Attributes allowed across every XML part in the package. */
|
|
35
|
+
maxXmlAttributesPerPackage: number;
|
|
13
36
|
allowedMediaMimeTypes: ReadonlySet<string>;
|
|
14
37
|
};
|
|
15
38
|
type DocxUnzipOptions = Partial<Omit<DocxUnzipLimits, "allowedMediaMimeTypes">> & {
|
package/dist/docx/unzip.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { bytesToDataUrl } from "../utils/base64.js";
|
|
1
2
|
import { DOCX_CONTAINER_TYPES, detectDocxContainerType } from "./encryption/containerFormat.js";
|
|
2
3
|
import { openDocxBuffer } from "./encryption/openEncryptedDocx.js";
|
|
3
|
-
import { FOLIO_XML_RESOURCE_LIMITS, assertXmlResourceLimits } from "./xmlResourceLimits.js";
|
|
4
|
+
import { FOLIO_XML_RESOURCE_LIMITS, assertXmlResourceLimits, createXmlPackageBudget } from "./xmlResourceLimits.js";
|
|
4
5
|
import JSZip from "jszip";
|
|
5
6
|
//#region src/docx/unzip.ts
|
|
6
7
|
/**
|
|
@@ -58,8 +59,21 @@ const DEFAULT_UNZIP_LIMITS = {
|
|
|
58
59
|
maxMediaBytes: 25 * MEBIBYTE,
|
|
59
60
|
maxFontBytes: 10 * MEBIBYTE,
|
|
60
61
|
maxTotalUncompressedBytes: 250 * MEBIBYTE,
|
|
62
|
+
maxXmlElementsPerPart: FOLIO_XML_RESOURCE_LIMITS.maxElementsPerPart,
|
|
63
|
+
maxXmlAttributesPerPart: FOLIO_XML_RESOURCE_LIMITS.maxAttributesPerPart,
|
|
64
|
+
maxXmlElementsPerPackage: FOLIO_XML_RESOURCE_LIMITS.maxElementsPerPackage,
|
|
65
|
+
maxXmlAttributesPerPackage: FOLIO_XML_RESOURCE_LIMITS.maxAttributesPerPackage,
|
|
61
66
|
allowedMediaMimeTypes: DEFAULT_ALLOWED_MEDIA_MIME_TYPES
|
|
62
67
|
};
|
|
68
|
+
/** The XML bounds an unzip enforces, in the shape the preflight takes. */
|
|
69
|
+
const xmlResourceLimitsFor = (limits) => ({
|
|
70
|
+
maxBytes: limits.maxXmlBytes,
|
|
71
|
+
maxDepth: FOLIO_XML_RESOURCE_LIMITS.maxDepth,
|
|
72
|
+
maxElementsPerPart: limits.maxXmlElementsPerPart,
|
|
73
|
+
maxAttributesPerPart: limits.maxXmlAttributesPerPart,
|
|
74
|
+
maxElementsPerPackage: limits.maxXmlElementsPerPackage,
|
|
75
|
+
maxAttributesPerPackage: limits.maxXmlAttributesPerPackage
|
|
76
|
+
});
|
|
63
77
|
const PARSED_XML_PARTS = /* @__PURE__ */ new Set([
|
|
64
78
|
"[content_types].xml",
|
|
65
79
|
"_rels/.rels",
|
|
@@ -188,10 +202,11 @@ async function unzipDocx(buffer, options = {}) {
|
|
|
188
202
|
}));
|
|
189
203
|
}
|
|
190
204
|
}
|
|
205
|
+
const xmlBudget = createXmlPackageBudget();
|
|
191
206
|
for (const extracted of await Promise.all(extractionTasks.map((extract) => extract()))) {
|
|
192
207
|
if (!extracted) continue;
|
|
193
208
|
if (extracted.type === "xml") {
|
|
194
|
-
assignXmlContent(content, extracted, limits);
|
|
209
|
+
assignXmlContent(content, extracted, limits, xmlBudget);
|
|
195
210
|
continue;
|
|
196
211
|
}
|
|
197
212
|
if (extracted.type === "media") {
|
|
@@ -203,19 +218,21 @@ async function unzipDocx(buffer, options = {}) {
|
|
|
203
218
|
return content;
|
|
204
219
|
}
|
|
205
220
|
/**
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
221
|
+
* Preflight every XML part the unzip retains, not a named few.
|
|
222
|
+
*
|
|
223
|
+
* Naming the parts to bound is the bug: `word/document.xml`, `word/styles.xml`
|
|
224
|
+
* and `word/numbering.xml` were counted, and headers, footers, footnotes,
|
|
225
|
+
* endnotes, comments and every other `word/*.xml` part were parsed into trees
|
|
226
|
+
* unbounded. Bounding the reader instead of the part list means a part added
|
|
227
|
+
* later is bounded by construction, and the package budget makes the ceiling
|
|
228
|
+
* the package's rather than each part's.
|
|
209
229
|
*/
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (PREFLIGHT_XML_PARTS.has(lowerPath)) assertXmlResourceLimits(xmlContent, {
|
|
217
|
-
...FOLIO_XML_RESOURCE_LIMITS,
|
|
218
|
-
maxBytes: limits.maxXmlBytes
|
|
230
|
+
function assignXmlContent(content, { path, lowerPath, content: xmlContent }, limits, budget) {
|
|
231
|
+
assertXmlResourceLimits({
|
|
232
|
+
xml: xmlContent,
|
|
233
|
+
limits: xmlResourceLimitsFor(limits),
|
|
234
|
+
partPath: path,
|
|
235
|
+
budget
|
|
219
236
|
});
|
|
220
237
|
content.allXml.set(path, xmlContent);
|
|
221
238
|
if (lowerPath === "word/document.xml") content.documentXml = xmlContent;
|
|
@@ -426,14 +443,7 @@ function getMediaMimeType(path) {
|
|
|
426
443
|
* @returns Data URL string
|
|
427
444
|
*/
|
|
428
445
|
function mediaToDataUrl(data, mimeType) {
|
|
429
|
-
|
|
430
|
-
const chunks = [];
|
|
431
|
-
const chunkSize = 32768;
|
|
432
|
-
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
433
|
-
const chunk = bytes.subarray(offset, offset + chunkSize);
|
|
434
|
-
chunks.push(String.fromCodePoint(...chunk));
|
|
435
|
-
}
|
|
436
|
-
return `data:${mimeType};base64,${btoa(chunks.join(""))}`;
|
|
446
|
+
return bytesToDataUrl(new Uint8Array(data), mimeType);
|
|
437
447
|
}
|
|
438
448
|
/**
|
|
439
449
|
* Extract a specific file from the original ZIP
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TRANSITIONAL_NAMESPACE_BY_STRICT_URI } from "./strictValueEncodings.gen.js";
|
|
2
|
-
import { isStrictNamespaceUri, toTransitionalNamespaceUri, transitionalSlotEncoding } from "./transitionalSpelling.js";
|
|
2
|
+
import { NUMBERS_PER_PERCENT, isStrictNamespaceUri, percentageSpelling, toTransitionalNamespaceUri, transitionalSlotEncoding } from "./transitionalSpelling.js";
|
|
3
3
|
import { roundHalfAwayFromZero, universalMeasureAs } from "./universalMeasure.js";
|
|
4
4
|
import { NAMESPACES, OOXML_NAMESPACE_SCOPE, cloneElement, elementToXml, getChildElements, getLocalName, getNamespacePrefix, parseXml } from "./xmlParser.js";
|
|
5
5
|
import { assertXmlResourceLimits } from "./xmlResourceLimits.js";
|
|
@@ -52,13 +52,6 @@ const DRAWING_EXTENSION_MARKERS = [
|
|
|
52
52
|
"/wordprocessingShape"
|
|
53
53
|
];
|
|
54
54
|
const isUnschemadDrawingNamespace = (uri) => !TRANSITIONAL_URIS.has(uri) && DRAWING_EXTENSION_MARKERS.some((marker) => uri.includes(marker));
|
|
55
|
-
/** `-?12.5%`: the one shape ECMA-376 gives a percentage that carries its unit. */
|
|
56
|
-
const PERCENTAGE = /^(-?[0-9]+(?:\.[0-9]+)?)%$/u;
|
|
57
|
-
const NUMBERS_PER_PERCENT = {
|
|
58
|
-
fiftiethPercent: 50,
|
|
59
|
-
thousandthPercent: 1e3,
|
|
60
|
-
wholePercent: 1
|
|
61
|
-
};
|
|
62
55
|
/** The Transitional spelling of one Strict-produced value, or the value unchanged. */
|
|
63
56
|
const transitionalValue = (value, namespaceUri, encoding) => {
|
|
64
57
|
const measureUnit = encoding?.measure;
|
|
@@ -66,11 +59,11 @@ const transitionalValue = (value, namespaceUri, encoding) => {
|
|
|
66
59
|
const measure = universalMeasureAs(value, measureUnit);
|
|
67
60
|
if (measure !== void 0) return String(measure);
|
|
68
61
|
}
|
|
69
|
-
const percentage =
|
|
70
|
-
if (percentage ===
|
|
62
|
+
const percentage = percentageSpelling(value);
|
|
63
|
+
if (percentage === void 0) return value;
|
|
71
64
|
const percentUnit = encoding?.percent ?? (isUnschemadDrawingNamespace(namespaceUri) ? "thousandthPercent" : void 0);
|
|
72
65
|
if (percentUnit === void 0) return value;
|
|
73
|
-
return String(roundHalfAwayFromZero(
|
|
66
|
+
return String(roundHalfAwayFromZero(percentage * NUMBERS_PER_PERCENT[percentUnit]));
|
|
74
67
|
};
|
|
75
68
|
const originOf = (element, inherited) => {
|
|
76
69
|
const uri = element.namespaceUri;
|
|
@@ -277,7 +270,7 @@ const parsedReplayEnvelope = (xml, inheritedNamespaceScope) => {
|
|
|
277
270
|
};
|
|
278
271
|
const withinXmlResourceLimits = (xml) => {
|
|
279
272
|
try {
|
|
280
|
-
assertXmlResourceLimits(xml);
|
|
273
|
+
assertXmlResourceLimits({ xml });
|
|
281
274
|
return true;
|
|
282
275
|
} catch {
|
|
283
276
|
return false;
|