@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
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { canonicalJson } from "../../utils/canonicalJson.js";
|
|
2
2
|
import { isValidHexColor } from "../../utils/colorResolver.js";
|
|
3
|
+
import { withBlockRangeMarkers } from "../blockRangeMarkers.js";
|
|
4
|
+
import { TABLE_LOOK_FLAGS } from "../tableLook.js";
|
|
3
5
|
import { parseTableCellProperties, parseTableGrid, parseTableProperties, parseTableRowProperties } from "../tableParser.js";
|
|
4
|
-
import {
|
|
6
|
+
import { sanitizeCapturedXmlElement } from "../verbatimCapture.js";
|
|
7
|
+
import { NAMESPACES, OOXML_NAMESPACE_SCOPE, parseXml } from "../xmlParser.js";
|
|
5
8
|
import { serializeBorder } from "./borderSerializer.js";
|
|
6
9
|
import { serializeTrackedChangeAttributes } from "./trackedChangeAttributes.js";
|
|
7
|
-
import {
|
|
10
|
+
import { intAttr } from "./xmlUtils.js";
|
|
11
|
+
import { escapeXmlAttribute } from "@stll/docx-core";
|
|
8
12
|
//#region src/docx/serializer/tableSerializer.ts
|
|
9
13
|
/**
|
|
10
14
|
* The captured element with the revision children written back into it.
|
|
@@ -127,30 +131,35 @@ function serializeCellMargins(margins, elementName) {
|
|
|
127
131
|
function serializeShading(shading) {
|
|
128
132
|
if (!shading) return "";
|
|
129
133
|
const attrs = [];
|
|
130
|
-
if (shading.pattern) attrs.push(`w:val="${
|
|
134
|
+
if (shading.pattern) attrs.push(`w:val="${escapeXmlAttribute(shading.pattern)}"`);
|
|
131
135
|
else attrs.push("w:val=\"clear\"");
|
|
132
|
-
if (shading.color?.rgb && isValidHexColor(shading.color.rgb)) attrs.push(`w:color="${
|
|
136
|
+
if (shading.color?.rgb && isValidHexColor(shading.color.rgb)) attrs.push(`w:color="${escapeXmlAttribute(shading.color.rgb)}"`);
|
|
133
137
|
else if (shading.color?.auto) attrs.push("w:color=\"auto\"");
|
|
134
|
-
if (shading.fill?.rgb && isValidHexColor(shading.fill.rgb)) attrs.push(`w:fill="${
|
|
138
|
+
if (shading.fill?.rgb && isValidHexColor(shading.fill.rgb)) attrs.push(`w:fill="${escapeXmlAttribute(shading.fill.rgb)}"`);
|
|
135
139
|
else if (shading.fill?.auto) attrs.push("w:fill=\"auto\"");
|
|
136
|
-
if (shading.fill?.themeColor) attrs.push(`w:themeFill="${
|
|
137
|
-
if (shading.fill?.themeTint) attrs.push(`w:themeFillTint="${
|
|
138
|
-
if (shading.fill?.themeShade) attrs.push(`w:themeFillShade="${
|
|
140
|
+
if (shading.fill?.themeColor) attrs.push(`w:themeFill="${escapeXmlAttribute(shading.fill.themeColor)}"`);
|
|
141
|
+
if (shading.fill?.themeTint) attrs.push(`w:themeFillTint="${escapeXmlAttribute(shading.fill.themeTint)}"`);
|
|
142
|
+
if (shading.fill?.themeShade) attrs.push(`w:themeFillShade="${escapeXmlAttribute(shading.fill.themeShade)}"`);
|
|
139
143
|
if (attrs.length === 0) return "";
|
|
140
144
|
return `<w:shd ${attrs.join(" ")}/>`;
|
|
141
145
|
}
|
|
142
146
|
/**
|
|
143
|
-
* Serialize
|
|
147
|
+
* Serialize a `w:tblLook`: what the author wrote, in the order they wrote it.
|
|
148
|
+
*
|
|
149
|
+
* A flag is written when it was authored, true or false. Writing only the true
|
|
150
|
+
* ones drops an explicit `w:lastRow="0"`, and an absent flag is not an off one:
|
|
151
|
+
* it falls back to `w:val`'s bit, so the two spell different tables under the
|
|
152
|
+
* same table style. `w:val` leads, then the flags in `CT_TblLook` order, which
|
|
153
|
+
* is the order Word writes them.
|
|
144
154
|
*/
|
|
145
155
|
function serializeTableLook(look) {
|
|
146
156
|
if (!look) return "";
|
|
147
157
|
const attrs = [];
|
|
148
|
-
if (look.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (look.noVBand) attrs.push("w:noVBand=\"1\"");
|
|
158
|
+
if (look.val !== void 0) attrs.push(`w:val="${escapeXmlAttribute(look.val)}"`);
|
|
159
|
+
for (const flag of TABLE_LOOK_FLAGS) {
|
|
160
|
+
const stated = look[flag];
|
|
161
|
+
if (stated !== void 0) attrs.push(`w:${flag}="${stated ? "1" : "0"}"`);
|
|
162
|
+
}
|
|
154
163
|
if (attrs.length === 0) return "";
|
|
155
164
|
return `<w:tblLook ${attrs.join(" ")}/>`;
|
|
156
165
|
}
|
|
@@ -181,7 +190,7 @@ function serializeTableFormatting(formatting, propertyChanges) {
|
|
|
181
190
|
if (tableSource !== null) return withRevisionChildren(tableSource, (propertyChanges ?? []).map((change) => serializeTablePropertyChange(change)));
|
|
182
191
|
const parts = [];
|
|
183
192
|
if (formatting) {
|
|
184
|
-
if (formatting.styleId) parts.push(`<w:tblStyle w:val="${
|
|
193
|
+
if (formatting.styleId) parts.push(`<w:tblStyle w:val="${escapeXmlAttribute(formatting.styleId)}"/>`);
|
|
185
194
|
const floatingXml = serializeFloatingTableProperties(formatting.floating);
|
|
186
195
|
if (floatingXml) parts.push(floatingXml);
|
|
187
196
|
if (formatting.overlap) parts.push(`<w:tblOverlap w:val="${formatting.overlap}"/>`);
|
|
@@ -344,17 +353,24 @@ function serializeTableGrid(table) {
|
|
|
344
353
|
if (gridSourceXml !== void 0) {
|
|
345
354
|
if (canonicalJson(parseTableGrid(parseXml(gridSourceXml, OOXML_NAMESPACE_SCOPE).elements?.[0] ?? null)) === canonicalJson(columnWidths)) return gridSourceXml;
|
|
346
355
|
}
|
|
347
|
-
|
|
356
|
+
const gridChange = replayableGridChangeXml(table.formatting?.gridChangeXml) ?? "";
|
|
357
|
+
if (columnWidths && columnWidths.length > 0) return `<w:tblGrid>${columnWidths.map((w) => `<w:gridCol w:w="${intAttr(w)}"/>`).join("")}${gridChange}</w:tblGrid>`;
|
|
348
358
|
const columns = gridColumnCount(table);
|
|
349
|
-
return columns === 0 ? "<w:tblGrid/>" : `<w:tblGrid>${"<w:gridCol/>".repeat(columns)}</w:tblGrid>`;
|
|
350
|
-
}
|
|
359
|
+
return columns === 0 && gridChange === "" ? "<w:tblGrid/>" : `<w:tblGrid>${"<w:gridCol/>".repeat(columns)}${gridChange}</w:tblGrid>`;
|
|
360
|
+
}
|
|
361
|
+
const GRID_CHANGE_ROOT_NAME = /* @__PURE__ */ new Set(["tblGridChange"]);
|
|
362
|
+
const WORDPROCESSINGML_NAMESPACE = /* @__PURE__ */ new Set([NAMESPACES.w]);
|
|
363
|
+
const replayableGridChangeXml = (gridChangeXml) => sanitizeCapturedXmlElement(gridChangeXml, {
|
|
364
|
+
allowedLocalNames: GRID_CHANGE_ROOT_NAME,
|
|
365
|
+
allowedNamespaceUris: WORDPROCESSINGML_NAMESPACE,
|
|
366
|
+
inheritedNamespaceScope: OOXML_NAMESPACE_SCOPE
|
|
367
|
+
});
|
|
351
368
|
/**
|
|
352
369
|
* Serialize cell content (paragraphs, nested tables)
|
|
353
370
|
*/
|
|
354
371
|
function serializeCellContent(content, serializeParagraph) {
|
|
355
372
|
const parts = [];
|
|
356
|
-
for (const item of content)
|
|
357
|
-
else parts.push(serializeTable(item, serializeParagraph));
|
|
373
|
+
for (const item of content) parts.push(withBlockRangeMarkers(item, item.type === "paragraph" ? serializeParagraph(item) : serializeTable(item, serializeParagraph)));
|
|
358
374
|
if (parts.length === 0) parts.push("<w:p/>");
|
|
359
375
|
return parts.join("");
|
|
360
376
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { document_d_exports } from "../../types/document.js";
|
|
2
2
|
//#region src/docx/serializer/textFormattingSerializer.d.ts
|
|
3
|
-
type ExhaustiveFields<Source, Classified extends keyof Source> = Exclude<keyof Source, Classified> extends never ? Source : never;
|
|
4
3
|
type ClassifiedShadingField = "color" | "fill" | "pattern";
|
|
5
|
-
type ExhaustiveShadingProperties = ExhaustiveFields<document_d_exports.ShadingProperties, ClassifiedShadingField>;
|
|
4
|
+
type ExhaustiveShadingProperties = document_d_exports.ExhaustiveFields<document_d_exports.ShadingProperties, ClassifiedShadingField>;
|
|
6
5
|
type ClassifiedTextFormattingField = "bold" | "boldCs" | "italic" | "italicCs" | "underline" | "strike" | "doubleStrike" | "vertAlign" | "smallCaps" | "allCaps" | "hidden" | "noProof" | "color" | "highlight" | "shading" | "fontSize" | "fontSizeCs" | "fontFamily" | "language" | "spacing" | "position" | "scale" | "kerning" | "effect" | "emphasisMark" | "emboss" | "imprint" | "outline" | "shadow" | "rtl" | "cs" | "styleId";
|
|
7
|
-
type ExhaustiveTextFormatting = ExhaustiveFields<document_d_exports.TextFormatting, ClassifiedTextFormattingField>;
|
|
6
|
+
type ExhaustiveTextFormatting = document_d_exports.ExhaustiveFields<document_d_exports.TextFormatting, ClassifiedTextFormattingField>;
|
|
8
7
|
/**
|
|
9
8
|
* Serialize shading properties (w:shd)
|
|
10
9
|
*/
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { HIGHLIGHT_COLOR_VALUES } from "../../types/documentEnumValues.js";
|
|
2
2
|
import { isValidHexColor } from "../../utils/colorResolver.js";
|
|
3
3
|
import { roundHorizontalScalePercentForSerialization } from "../../utils/horizontalScale.js";
|
|
4
|
-
import {
|
|
4
|
+
import { intAttr } from "./xmlUtils.js";
|
|
5
|
+
import { escapeXmlAttribute } from "@stll/docx-core";
|
|
5
6
|
//#region src/docx/serializer/textFormattingSerializer.ts
|
|
6
7
|
const VALID_HIGHLIGHT_COLORS = new Set(HIGHLIGHT_COLOR_VALUES);
|
|
7
8
|
/**
|
|
@@ -12,10 +13,10 @@ function serializeColorElement(color) {
|
|
|
12
13
|
const { auto, rgb, themeColor, themeTint, themeShade } = color;
|
|
13
14
|
const attrs = [];
|
|
14
15
|
if (auto) attrs.push("w:val=\"auto\"");
|
|
15
|
-
else if (rgb && isValidHexColor(rgb)) attrs.push(`w:val="${
|
|
16
|
-
if (themeColor) attrs.push(`w:themeColor="${
|
|
17
|
-
if (themeTint) attrs.push(`w:themeTint="${
|
|
18
|
-
if (themeShade) attrs.push(`w:themeShade="${
|
|
16
|
+
else if (rgb && isValidHexColor(rgb)) attrs.push(`w:val="${escapeXmlAttribute(rgb)}"`);
|
|
17
|
+
if (themeColor) attrs.push(`w:themeColor="${escapeXmlAttribute(themeColor)}"`);
|
|
18
|
+
if (themeTint) attrs.push(`w:themeTint="${escapeXmlAttribute(themeTint)}"`);
|
|
19
|
+
if (themeShade) attrs.push(`w:themeShade="${escapeXmlAttribute(themeShade)}"`);
|
|
19
20
|
return attrs.length === 0 ? "" : `<w:color ${attrs.join(" ")}/>`;
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
@@ -25,20 +26,20 @@ function serializeShading(shading) {
|
|
|
25
26
|
if (!shading) return "";
|
|
26
27
|
const { pattern, color, fill } = shading;
|
|
27
28
|
const attrs = [];
|
|
28
|
-
if (pattern) attrs.push(`w:val="${
|
|
29
|
+
if (pattern) attrs.push(`w:val="${escapeXmlAttribute(pattern)}"`);
|
|
29
30
|
else attrs.push("w:val=\"clear\"");
|
|
30
31
|
if (color) {
|
|
31
32
|
const { rgb, auto, themeColor: _themeColor, themeTint: _themeTint, themeShade: _themeShade } = color;
|
|
32
|
-
if (rgb && isValidHexColor(rgb)) attrs.push(`w:color="${
|
|
33
|
+
if (rgb && isValidHexColor(rgb)) attrs.push(`w:color="${escapeXmlAttribute(rgb)}"`);
|
|
33
34
|
else if (auto) attrs.push("w:color=\"auto\"");
|
|
34
35
|
}
|
|
35
36
|
if (fill) {
|
|
36
37
|
const { rgb, auto, themeColor, themeTint, themeShade } = fill;
|
|
37
|
-
if (rgb && isValidHexColor(rgb)) attrs.push(`w:fill="${
|
|
38
|
+
if (rgb && isValidHexColor(rgb)) attrs.push(`w:fill="${escapeXmlAttribute(rgb)}"`);
|
|
38
39
|
else if (auto) attrs.push("w:fill=\"auto\"");
|
|
39
|
-
if (themeColor) attrs.push(`w:themeFill="${
|
|
40
|
-
if (themeTint) attrs.push(`w:themeFillTint="${
|
|
41
|
-
if (themeShade) attrs.push(`w:themeFillShade="${
|
|
40
|
+
if (themeColor) attrs.push(`w:themeFill="${escapeXmlAttribute(themeColor)}"`);
|
|
41
|
+
if (themeTint) attrs.push(`w:themeFillTint="${escapeXmlAttribute(themeTint)}"`);
|
|
42
|
+
if (themeShade) attrs.push(`w:themeFillShade="${escapeXmlAttribute(themeShade)}"`);
|
|
42
43
|
}
|
|
43
44
|
return attrs.length === 0 ? "" : `<w:shd ${attrs.join(" ")}/>`;
|
|
44
45
|
}
|
|
@@ -49,19 +50,19 @@ function serializeTextFormatting(input) {
|
|
|
49
50
|
if (!input) return "";
|
|
50
51
|
const { bold, boldCs, italic, italicCs, underline, strike, doubleStrike, vertAlign, smallCaps, allCaps, hidden, noProof, color, highlight, shading, fontSize, fontSizeCs, fontFamily, language, spacing, position, scale, kerning, effect, emphasisMark, emboss, imprint, outline, shadow, rtl, cs, styleId } = input;
|
|
51
52
|
const parts = [];
|
|
52
|
-
if (styleId) parts.push(`<w:rStyle w:val="${
|
|
53
|
+
if (styleId) parts.push(`<w:rStyle w:val="${escapeXmlAttribute(styleId)}"/>`);
|
|
53
54
|
if (fontFamily) {
|
|
54
55
|
const { ascii, hAnsi, eastAsia, cs: complexScript, hint, asciiTheme, hAnsiTheme, eastAsiaTheme, csTheme } = fontFamily;
|
|
55
56
|
const fontAttrs = [];
|
|
56
|
-
if (ascii) fontAttrs.push(`w:ascii="${
|
|
57
|
-
if (hAnsi) fontAttrs.push(`w:hAnsi="${
|
|
58
|
-
if (eastAsia) fontAttrs.push(`w:eastAsia="${
|
|
59
|
-
if (complexScript) fontAttrs.push(`w:cs="${
|
|
60
|
-
if (hint) fontAttrs.push(`w:hint="${
|
|
61
|
-
if (asciiTheme) fontAttrs.push(`w:asciiTheme="${
|
|
62
|
-
if (hAnsiTheme) fontAttrs.push(`w:hAnsiTheme="${
|
|
63
|
-
if (eastAsiaTheme) fontAttrs.push(`w:eastAsiaTheme="${
|
|
64
|
-
if (csTheme) fontAttrs.push(`w:cstheme="${
|
|
57
|
+
if (ascii) fontAttrs.push(`w:ascii="${escapeXmlAttribute(ascii)}"`);
|
|
58
|
+
if (hAnsi) fontAttrs.push(`w:hAnsi="${escapeXmlAttribute(hAnsi)}"`);
|
|
59
|
+
if (eastAsia) fontAttrs.push(`w:eastAsia="${escapeXmlAttribute(eastAsia)}"`);
|
|
60
|
+
if (complexScript) fontAttrs.push(`w:cs="${escapeXmlAttribute(complexScript)}"`);
|
|
61
|
+
if (hint) fontAttrs.push(`w:hint="${escapeXmlAttribute(hint)}"`);
|
|
62
|
+
if (asciiTheme) fontAttrs.push(`w:asciiTheme="${escapeXmlAttribute(asciiTheme)}"`);
|
|
63
|
+
if (hAnsiTheme) fontAttrs.push(`w:hAnsiTheme="${escapeXmlAttribute(hAnsiTheme)}"`);
|
|
64
|
+
if (eastAsiaTheme) fontAttrs.push(`w:eastAsiaTheme="${escapeXmlAttribute(eastAsiaTheme)}"`);
|
|
65
|
+
if (csTheme) fontAttrs.push(`w:cstheme="${escapeXmlAttribute(csTheme)}"`);
|
|
65
66
|
if (fontAttrs.length > 0) parts.push(`<w:rFonts ${fontAttrs.join(" ")}/>`);
|
|
66
67
|
}
|
|
67
68
|
if (bold === true) parts.push("<w:b/>");
|
|
@@ -114,10 +115,10 @@ function serializeTextFormatting(input) {
|
|
|
114
115
|
const uAttrs = [`w:val="${style}"`];
|
|
115
116
|
if (underlineColor) {
|
|
116
117
|
const { rgb, themeColor, themeTint, themeShade, auto: _auto } = underlineColor;
|
|
117
|
-
if (rgb && isValidHexColor(rgb)) uAttrs.push(`w:color="${
|
|
118
|
-
if (themeColor) uAttrs.push(`w:themeColor="${
|
|
119
|
-
if (themeTint) uAttrs.push(`w:themeTint="${
|
|
120
|
-
if (themeShade) uAttrs.push(`w:themeShade="${
|
|
118
|
+
if (rgb && isValidHexColor(rgb)) uAttrs.push(`w:color="${escapeXmlAttribute(rgb)}"`);
|
|
119
|
+
if (themeColor) uAttrs.push(`w:themeColor="${escapeXmlAttribute(themeColor)}"`);
|
|
120
|
+
if (themeTint) uAttrs.push(`w:themeTint="${escapeXmlAttribute(themeTint)}"`);
|
|
121
|
+
if (themeShade) uAttrs.push(`w:themeShade="${escapeXmlAttribute(themeShade)}"`);
|
|
121
122
|
}
|
|
122
123
|
parts.push(`<w:u ${uAttrs.join(" ")}/>`);
|
|
123
124
|
}
|
|
@@ -133,9 +134,9 @@ function serializeTextFormatting(input) {
|
|
|
133
134
|
if (language) {
|
|
134
135
|
const { val, eastAsia, bidi } = language;
|
|
135
136
|
const languageAttrs = [];
|
|
136
|
-
if (val) languageAttrs.push(`w:val="${
|
|
137
|
-
if (eastAsia) languageAttrs.push(`w:eastAsia="${
|
|
138
|
-
if (bidi) languageAttrs.push(`w:bidi="${
|
|
137
|
+
if (val) languageAttrs.push(`w:val="${escapeXmlAttribute(val)}"`);
|
|
138
|
+
if (eastAsia) languageAttrs.push(`w:eastAsia="${escapeXmlAttribute(eastAsia)}"`);
|
|
139
|
+
if (bidi) languageAttrs.push(`w:bidi="${escapeXmlAttribute(bidi)}"`);
|
|
139
140
|
if (languageAttrs.length > 0) parts.push(`<w:lang ${languageAttrs.join(" ")}/>`);
|
|
140
141
|
}
|
|
141
142
|
return parts.length === 0 ? "" : `<w:rPr>${parts.join("")}</w:rPr>`;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { serializePartElement } from "./partNamespaces.js";
|
|
2
|
-
import {
|
|
2
|
+
import { escapeXmlAttribute } from "@stll/docx-core";
|
|
3
3
|
//#region src/docx/serializer/themeSerializer.ts
|
|
4
4
|
const serializeThemeXml = (theme) => {
|
|
5
|
-
const name =
|
|
5
|
+
const name = escapeXmlAttribute(theme.name ?? "Folio Theme");
|
|
6
6
|
const elements = `${serializeColorScheme(theme.colorScheme)}${serializeFontScheme(theme)}${serializeFormatScheme(theme)}`;
|
|
7
7
|
return "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" + serializePartElement({
|
|
8
8
|
partPath: "word/theme/theme1.xml",
|
|
@@ -28,17 +28,17 @@ const serializeColorScheme = (colors) => {
|
|
|
28
28
|
hlink: colors?.hlink ?? "0563C1",
|
|
29
29
|
folHlink: colors?.folHlink ?? "954F72"
|
|
30
30
|
};
|
|
31
|
-
return `<a:clrScheme name="Folio">${Object.entries(values).map(([slot, value]) => `<a:${slot}><a:srgbClr val="${
|
|
31
|
+
return `<a:clrScheme name="Folio">${Object.entries(values).map(([slot, value]) => `<a:${slot}><a:srgbClr val="${escapeXmlAttribute(value)}"/></a:${slot}>`).join("")}</a:clrScheme>`;
|
|
32
32
|
};
|
|
33
33
|
const serializeFontScheme = (theme) => {
|
|
34
34
|
return `<a:fontScheme name="Folio">${serializeThemeFont(theme.fontScheme?.majorFont, "majorFont", "Arial")}${serializeThemeFont(theme.fontScheme?.minorFont, "minorFont", "Arial")}</a:fontScheme>`;
|
|
35
35
|
};
|
|
36
36
|
const serializeThemeFont = (font, element, fallback) => {
|
|
37
|
-
const scriptFonts = Object.entries(font?.fonts ?? {}).map(([script, typeface]) => `<a:font script="${
|
|
38
|
-
return `<a:${element}><a:latin typeface="${
|
|
37
|
+
const scriptFonts = Object.entries(font?.fonts ?? {}).map(([script, typeface]) => `<a:font script="${escapeXmlAttribute(script)}" typeface="${escapeXmlAttribute(typeface)}"/>`).join("");
|
|
38
|
+
return `<a:${element}><a:latin typeface="${escapeXmlAttribute(font?.latin ?? fallback)}"/><a:ea typeface="${escapeXmlAttribute(font?.ea ?? "")}"/><a:cs typeface="${escapeXmlAttribute(font?.cs ?? "")}"/>${scriptFonts}</a:${element}>`;
|
|
39
39
|
};
|
|
40
40
|
const serializeFormatScheme = (theme) => {
|
|
41
|
-
return `<a:fmtScheme name="${
|
|
41
|
+
return `<a:fmtScheme name="${escapeXmlAttribute(theme.formatScheme?.name ?? "Folio")}"><a:fillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:fillStyleLst><a:lnStyleLst><a:ln w="6350" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:bgFillStyleLst></a:fmtScheme>`;
|
|
42
42
|
};
|
|
43
43
|
//#endregion
|
|
44
44
|
export { serializeThemeXml };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DATE_UTC_ATTRIBUTE } from "../trackedChangeInfo.js";
|
|
2
|
-
import { escapeXml } from "./xmlUtils.js";
|
|
3
2
|
import { panic } from "better-result";
|
|
3
|
+
import { escapeXmlAttribute } from "@stll/docx-core";
|
|
4
4
|
import { normalizeRevisionId } from "@stll/docx-core/model";
|
|
5
5
|
//#region src/docx/serializer/trackedChangeAttributes.ts
|
|
6
6
|
/** Enforces the singular `w:rPrChange` child in a run-property container. */
|
|
@@ -26,6 +26,6 @@ const trackedChangeAttributeEntries = (info) => {
|
|
|
26
26
|
return entries;
|
|
27
27
|
};
|
|
28
28
|
const trackedChangeAttributeRecord = (info) => Object.fromEntries(trackedChangeAttributeEntries(info));
|
|
29
|
-
const serializeTrackedChangeAttributes = (info) => trackedChangeAttributeEntries(info).map(([name, value]) => `${name}="${
|
|
29
|
+
const serializeTrackedChangeAttributes = (info) => trackedChangeAttributeEntries(info).map(([name, value]) => `${name}="${escapeXmlAttribute(value)}"`).join(" ");
|
|
30
30
|
//#endregion
|
|
31
31
|
export { getSingularRunPropertyChange, serializeTrackedChangeAttributes, trackedChangeAttributeEntries, trackedChangeAttributeRecord };
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Shared XML utility functions for serializers.
|
|
4
4
|
*/
|
|
5
|
-
declare function escapeXml(text: string): string;
|
|
6
5
|
/**
|
|
7
6
|
* Format a numeric value as an integer XML attribute.
|
|
8
7
|
*
|
|
@@ -29,4 +28,4 @@ declare function intAttr(value: number | undefined | null): string;
|
|
|
29
28
|
*/
|
|
30
29
|
declare function isSingleWellFormedElement(xml: string, expectedLocalName: string): boolean;
|
|
31
30
|
//#endregion
|
|
32
|
-
export {
|
|
31
|
+
export { intAttr, isSingleWellFormedElement };
|
|
@@ -3,18 +3,6 @@ import { getLocalName, parseXml } from "../xmlParser.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* Shared XML utility functions for serializers.
|
|
5
5
|
*/
|
|
6
|
-
const XML_SPECIAL_CHARACTER_PATTERN = /[&<>"']/u;
|
|
7
|
-
const XML_SPECIAL_CHARACTER_GLOBAL_PATTERN = /[&<>"']/gu;
|
|
8
|
-
function escapeXml(text) {
|
|
9
|
-
if (!XML_SPECIAL_CHARACTER_PATTERN.test(text)) return text;
|
|
10
|
-
return text.replace(XML_SPECIAL_CHARACTER_GLOBAL_PATTERN, (character) => {
|
|
11
|
-
if (character === "&") return "&";
|
|
12
|
-
if (character === "<") return "<";
|
|
13
|
-
if (character === ">") return ">";
|
|
14
|
-
if (character === "\"") return """;
|
|
15
|
-
return "'";
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
6
|
/**
|
|
19
7
|
* Format a numeric value as an integer XML attribute.
|
|
20
8
|
*
|
|
@@ -57,4 +45,4 @@ function isSingleWellFormedElement(xml, expectedLocalName) {
|
|
|
57
45
|
return root?.name !== void 0 && getLocalName(root.name) === expectedLocalName;
|
|
58
46
|
}
|
|
59
47
|
//#endregion
|
|
60
|
-
export {
|
|
48
|
+
export { intAttr, isSingleWellFormedElement };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { XmlResourceLimits } from "../xmlResourceLimits.js";
|
|
1
2
|
import JSZip from "jszip";
|
|
2
3
|
//#region src/docx/server/boundedArchive.d.ts
|
|
3
4
|
declare module "jszip" {
|
|
@@ -27,6 +28,17 @@ type DocxArchiveOptions = {
|
|
|
27
28
|
maxEntryBytes?: number;
|
|
28
29
|
maxTotalBytes?: number;
|
|
29
30
|
maxEntries?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Bounds on parsed XML structure, applied to every XML part this archive
|
|
33
|
+
* hands out as a string.
|
|
34
|
+
*
|
|
35
|
+
* Every consumer of `readEntryString` parses the result into an object tree,
|
|
36
|
+
* and the byte caps above bound the markup, not the tree. Enforcing the
|
|
37
|
+
* element and attribute bounds here rather than at each parse site means a
|
|
38
|
+
* new consumer is bounded by construction: there is no way to obtain a part
|
|
39
|
+
* string from this archive that has not been counted.
|
|
40
|
+
*/
|
|
41
|
+
xmlLimits?: Partial<XmlResourceLimits>;
|
|
30
42
|
};
|
|
31
43
|
type DocxArchiveEntry = {
|
|
32
44
|
readonly path: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FOLIO_XML_RESOURCE_LIMITS, assertXmlResourceLimits, createXmlPackageBudget } from "../xmlResourceLimits.js";
|
|
1
2
|
import { TaggedError } from "better-result";
|
|
2
3
|
import JSZip from "jszip";
|
|
3
4
|
//#region src/docx/server/boundedArchive.ts
|
|
@@ -117,6 +118,12 @@ const loadDocxArchive = async (bytes, options = {}) => {
|
|
|
117
118
|
message: `DOCX archive declares ${declaredTotalBytes} cumulative bytes (max ${maxTotalBytes})`,
|
|
118
119
|
reason: "total-too-large"
|
|
119
120
|
});
|
|
121
|
+
const xmlLimits = {
|
|
122
|
+
...FOLIO_XML_RESOURCE_LIMITS,
|
|
123
|
+
...options.xmlLimits
|
|
124
|
+
};
|
|
125
|
+
const xmlBudget = createXmlPackageBudget();
|
|
126
|
+
const countedParts = /* @__PURE__ */ new Set();
|
|
120
127
|
let totalBytesRead = 0;
|
|
121
128
|
let readChain = Promise.resolve();
|
|
122
129
|
const readEntry = async (path, readOptions = {}) => {
|
|
@@ -151,7 +158,19 @@ const loadDocxArchive = async (bytes, options = {}) => {
|
|
|
151
158
|
}))),
|
|
152
159
|
async readEntryString(path) {
|
|
153
160
|
const content = await readEntry(path);
|
|
154
|
-
|
|
161
|
+
if (content === null) return null;
|
|
162
|
+
const xml = new TextDecoder("utf-8", { ignoreBOM: true }).decode(content);
|
|
163
|
+
const lower = path.toLowerCase();
|
|
164
|
+
if (!lower.endsWith(".xml") && !lower.endsWith(".rels")) return xml;
|
|
165
|
+
const charged = countedParts.has(path);
|
|
166
|
+
countedParts.add(path);
|
|
167
|
+
assertXmlResourceLimits({
|
|
168
|
+
xml,
|
|
169
|
+
limits: xmlLimits,
|
|
170
|
+
partPath: path,
|
|
171
|
+
...charged ? {} : { budget: xmlBudget }
|
|
172
|
+
});
|
|
173
|
+
return xml;
|
|
155
174
|
},
|
|
156
175
|
readEntryUint8: readEntry
|
|
157
176
|
};
|
|
@@ -26,7 +26,14 @@ const HEADING_LEVELS = [
|
|
|
26
26
|
5,
|
|
27
27
|
6
|
|
28
28
|
];
|
|
29
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* The heading levels a `TOC \o` field-argument may name (ECMA-376 17.16.5.68).
|
|
31
|
+
*
|
|
32
|
+
* `\o` selects by *style id* — "each integer corresponds to the style with a
|
|
33
|
+
* style ID of HeadingX" — not by outline level, which is what `\u` reads. The
|
|
34
|
+
* domain here is therefore the 1-9 of `Heading1`…`Heading9`, not the 0-8 of
|
|
35
|
+
* `w:outlineLvl`.
|
|
36
|
+
*/
|
|
30
37
|
const TOC_LEVEL_MIN = 1;
|
|
31
38
|
const TOC_LEVEL_MAX = 9;
|
|
32
39
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { deterministicHexId } from "../../utils/hexId.js";
|
|
2
|
+
import { createBuiltInStyleIndex, resolveHeadingLevel } from "../builtInStyles.js";
|
|
2
3
|
import { isNumberingReference } from "../numberingReference.js";
|
|
3
4
|
import { getParagraphText } from "../paragraphParser.js";
|
|
4
5
|
import { cloneParagraphWithoutPropertySource } from "../paragraphPropertySource.js";
|
|
@@ -77,6 +78,7 @@ function createBilingualDocument(source, options) {
|
|
|
77
78
|
const styles = source.package.styles;
|
|
78
79
|
const numbering = source.package.numbering;
|
|
79
80
|
const styleById = new Map((styles?.styles ?? []).map((style) => [style.styleId, style]));
|
|
81
|
+
const builtInStyles = createBuiltInStyleIndex(styleById.values(), styles?.docDefaults);
|
|
80
82
|
const blocks = flattenBlocks(source.package.document.content);
|
|
81
83
|
const cloner = createNumberingCloner({
|
|
82
84
|
numbering,
|
|
@@ -124,7 +126,7 @@ function createBilingualDocument(source, options) {
|
|
|
124
126
|
}
|
|
125
127
|
const { copy, ref } = copyParagraph(block);
|
|
126
128
|
rows.push({
|
|
127
|
-
kind: classifyParagraph(block, styleById),
|
|
129
|
+
kind: classifyParagraph(block, styleById, builtInStyles),
|
|
128
130
|
rowId: ref.targetParaId,
|
|
129
131
|
...ref
|
|
130
132
|
});
|
|
@@ -235,26 +237,15 @@ const isFieldOnlyParagraph = (paragraph) => {
|
|
|
235
237
|
* them would surface as a missing handle rather than as the rule it is.
|
|
236
238
|
*/
|
|
237
239
|
const isTranslatableParagraph = (paragraph, editableParagraphIds) => paragraph.paraId !== void 0 && editableParagraphIds.has(paragraph.paraId) && !isFieldOnlyParagraph(paragraph);
|
|
238
|
-
|
|
239
|
-
const HEADING_STYLE_PATTERN = /heading|nadpis|berschrift|titre|nag[łl]/iu;
|
|
240
|
-
const classifyParagraph = (paragraph, styleById) => {
|
|
240
|
+
const classifyParagraph = (paragraph, styleById, builtInStyles) => {
|
|
241
241
|
const formatting = paragraph.formatting;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
242
|
+
if (resolveHeadingLevel({
|
|
243
|
+
outlineLevel: formatting?.outlineLevel,
|
|
244
|
+
styleId: formatting?.styleId
|
|
245
|
+
}, builtInStyles) !== void 0) return "heading";
|
|
246
246
|
if (effectiveNumPr(paragraph, styleById) !== void 0) return "listItem";
|
|
247
247
|
return "paragraph";
|
|
248
248
|
};
|
|
249
|
-
const resolveInheritedOutlineLevel = (style, styleById) => {
|
|
250
|
-
const seen = /* @__PURE__ */ new Set();
|
|
251
|
-
let current = style;
|
|
252
|
-
while (current && !seen.has(current.styleId)) {
|
|
253
|
-
seen.add(current.styleId);
|
|
254
|
-
if (current.pPr?.outlineLevel !== void 0) return current.pPr.outlineLevel;
|
|
255
|
-
current = current.basedOn ? styleById.get(current.basedOn) : void 0;
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
249
|
/** The numbering a paragraph renders with: direct `numPr`, else the style chain's. */
|
|
259
250
|
const effectiveNumPr = (paragraph, styleById) => {
|
|
260
251
|
const direct = paragraph.formatting?.numPr;
|
|
@@ -703,6 +694,7 @@ const createParaIdMinter = (taken) => {
|
|
|
703
694
|
*/
|
|
704
695
|
function readBilingualDocument(document, editableParagraphIds) {
|
|
705
696
|
const styleById = new Map((document.package.styles?.styles ?? []).map((style) => [style.styleId, style]));
|
|
697
|
+
const builtInStyles = createBuiltInStyleIndex(styleById.values(), document.package.styles?.docDefaults);
|
|
706
698
|
const rows = [];
|
|
707
699
|
let missingHandleCount = 0;
|
|
708
700
|
for (const block of flattenBlocks(document.package.document.content)) {
|
|
@@ -777,7 +769,7 @@ function readBilingualDocument(document, editableParagraphIds) {
|
|
|
777
769
|
if (targetMissing) missingHandleCount += 1;
|
|
778
770
|
if (sourceMissing || targetMissing) continue;
|
|
779
771
|
rows.push({
|
|
780
|
-
kind: classifyParagraph(source, styleById),
|
|
772
|
+
kind: classifyParagraph(source, styleById, builtInStyles),
|
|
781
773
|
rowId: target.paraId,
|
|
782
774
|
sourceParaId: source.paraId,
|
|
783
775
|
targetParaId: target.paraId,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { escapeTableCell } from "../../markdown/escape.js";
|
|
2
2
|
import { RELATIONSHIP_TYPES, parseRelationships } from "../relsParser.js";
|
|
3
|
-
import { findAllDeep, findDeep, getAttribute, getAttributeByNamespaceUri, getLocalName, getNamespaceUri, getTextContent, parseXml } from "../xmlParser.js";
|
|
3
|
+
import { findAllDeep, findDeep, getAttribute, getAttributeByNamespaceUri, getLocalName, getNamespaceUri, getTextContent, parseOnOffValue, parseXml } from "../xmlParser.js";
|
|
4
4
|
import { DocxArchiveError, loadDocxArchive } from "./boundedArchive.js";
|
|
5
5
|
//#region src/docx/server/extractDocxText.ts
|
|
6
6
|
const DOCUMENT_RELS_PATH = "word/_rels/document.xml.rels";
|
|
@@ -68,7 +68,7 @@ const readRunMetrics = (paragraph) => {
|
|
|
68
68
|
const properties = findWordChild(run, "rPr");
|
|
69
69
|
const boldProperty = findWordChild(properties, "b");
|
|
70
70
|
const boldValue = getWordAttribute(boldProperty, "val");
|
|
71
|
-
const bold = boldProperty !== null && boldValue
|
|
71
|
+
const bold = boldProperty !== null && (parseOnOffValue(boldValue) ?? true);
|
|
72
72
|
const sizeProperty = findWordChild(properties, "sz");
|
|
73
73
|
const sizeValue = getWordAttribute(sizeProperty, "val");
|
|
74
74
|
const parsedSize = sizeValue === null ? NaN : Number.parseInt(sizeValue, 10);
|
|
@@ -270,8 +270,7 @@ const readTableCell = (cell, { depth, budget }) => {
|
|
|
270
270
|
const declaresHeaderRow = (row) => {
|
|
271
271
|
const header = findWordChild(findWordChild(row, "trPr"), "tblHeader");
|
|
272
272
|
if (header === null) return false;
|
|
273
|
-
|
|
274
|
-
return value !== "0" && value !== "false";
|
|
273
|
+
return parseOnOffValue(getWordAttribute(header, "val")) ?? true;
|
|
275
274
|
};
|
|
276
275
|
const readRowGridOffset = (row, localName) => {
|
|
277
276
|
const properties = findWordChild(row, "trPr");
|
|
@@ -3,6 +3,7 @@ import { DOCX_CONTAINER_TYPES, detectDocxContainerType } from "../encryption/con
|
|
|
3
3
|
import { DocxModelValidationError, validateFolioDocumentModel } from "../modelValidation.js";
|
|
4
4
|
import { DocxParseError, parseDocx } from "../parser.js";
|
|
5
5
|
import { findChildByLocalName, getAttributeAnyPrefix, getLocalName, getNamespacePrefix, parseXmlDocument } from "../xmlParser.js";
|
|
6
|
+
import { XmlResourceLimitError } from "../xmlResourceLimits.js";
|
|
6
7
|
import { getDocxXmlSafetyIssue } from "../xmlSafety.js";
|
|
7
8
|
import { DocxArchiveError, loadDocxArchive } from "./boundedArchive.js";
|
|
8
9
|
import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
|
|
@@ -112,11 +113,31 @@ const XML_SAFETY_ISSUE = {
|
|
|
112
113
|
message: "An XML package part is not well formed."
|
|
113
114
|
}
|
|
114
115
|
};
|
|
116
|
+
/**
|
|
117
|
+
* Read a part for inspection, including one the safe reader declines.
|
|
118
|
+
*
|
|
119
|
+
* `readEntryString` runs the XML structure preflight, and the preflight
|
|
120
|
+
* refuses to scan markup it cannot count — a document type declaration, an
|
|
121
|
+
* unterminated tag — which is exactly the markup this check exists to report.
|
|
122
|
+
* Such a part is read raw and classified below, so the report names why the
|
|
123
|
+
* package is invalid instead of saying it could not tell. A part that overran
|
|
124
|
+
* a real bound still refuses, and the caller reports that as indeterminate,
|
|
125
|
+
* because a package too large to scan is not a package known to be wrong.
|
|
126
|
+
*/
|
|
127
|
+
const readPartForInspection = async (archive, part) => {
|
|
128
|
+
try {
|
|
129
|
+
return await archive.readEntryString(part);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
if (!(error instanceof XmlResourceLimitError) || error.limit !== "syntax") throw error;
|
|
132
|
+
const bytes = await archive.readEntryUint8(part);
|
|
133
|
+
return bytes === null ? null : new TextDecoder("utf-8", { ignoreBOM: true }).decode(bytes);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
115
136
|
const validateXmlParts = async (archive, report) => {
|
|
116
137
|
const xmlByPath = /* @__PURE__ */ new Map();
|
|
117
138
|
let hasInvalidXml = false;
|
|
118
139
|
for (const part of archive.entries.filter((path) => XML_PART_PATTERN.test(path)).toSorted()) {
|
|
119
|
-
const xml = await archive
|
|
140
|
+
const xml = await readPartForInspection(archive, part);
|
|
120
141
|
if (xml === null) continue;
|
|
121
142
|
xmlByPath.set(part, xml);
|
|
122
143
|
const safetyIssue = getDocxXmlSafetyIssue(xml);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { XmlElement } from "./xmlParser.js";
|
|
3
|
+
//#region src/docx/shadingParser.d.ts
|
|
4
|
+
declare function parseShading(shd: XmlElement | null): document_d_exports.ShadingProperties | undefined;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { parseShading };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { isValidHexColor } from "../utils/colorResolver.js";
|
|
2
|
+
import { ShadingPatternSchema, ThemeColorSlotSchema, narrowEnum } from "./parserEnums.js";
|
|
3
|
+
import { getAttribute } from "./xmlParser.js";
|
|
4
|
+
//#region src/docx/shadingParser.ts
|
|
5
|
+
/** `ST_HexColor`'s reserved "let the consumer decide" token. */
|
|
6
|
+
const AUTOMATIC_COLOR = "auto";
|
|
7
|
+
const parseHexColor = (value) => {
|
|
8
|
+
if (value === AUTOMATIC_COLOR) return { auto: true };
|
|
9
|
+
if (value && isValidHexColor(value)) return { rgb: value };
|
|
10
|
+
};
|
|
11
|
+
function parseShading(shd) {
|
|
12
|
+
if (!shd) return;
|
|
13
|
+
const props = {};
|
|
14
|
+
const color = parseHexColor(getAttribute(shd, "w", "color"));
|
|
15
|
+
if (color) props.color = color;
|
|
16
|
+
const fill = parseHexColor(getAttribute(shd, "w", "fill"));
|
|
17
|
+
if (fill) props.fill = fill;
|
|
18
|
+
const themeFill = narrowEnum(getAttribute(shd, "w", "themeFill"), ThemeColorSlotSchema);
|
|
19
|
+
if (themeFill) {
|
|
20
|
+
props.fill ??= {};
|
|
21
|
+
props.fill.themeColor = themeFill;
|
|
22
|
+
}
|
|
23
|
+
const themeFillTint = getAttribute(shd, "w", "themeFillTint");
|
|
24
|
+
if (themeFillTint && props.fill) props.fill.themeTint = themeFillTint;
|
|
25
|
+
const themeFillShade = getAttribute(shd, "w", "themeFillShade");
|
|
26
|
+
if (themeFillShade && props.fill) props.fill.themeShade = themeFillShade;
|
|
27
|
+
const pattern = narrowEnum(getAttribute(shd, "w", "val"), ShadingPatternSchema);
|
|
28
|
+
if (pattern) props.pattern = pattern;
|
|
29
|
+
return Object.keys(props).length > 0 ? props : void 0;
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { parseShading };
|