@stll/folio-core 0.44.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 +1 -0
- package/dist/compare/content-alignment.js +78 -53
- package/dist/compare/inline-atoms.js +34 -20
- 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/borderParser.js +5 -5
- package/dist/docx/commentParser.js +47 -36
- package/dist/docx/commentThreadKey.d.ts +18 -0
- package/dist/docx/commentThreadKey.js +22 -0
- package/dist/docx/diagramPreview.js +87 -27
- package/dist/docx/groupDrawingParser.js +3 -3
- package/dist/docx/hyperlinkParser.js +2 -2
- package/dist/docx/imageParser.d.ts +9 -1
- package/dist/docx/imageParser.js +58 -12
- package/dist/docx/imageRawXml.d.ts +14 -1
- package/dist/docx/imageRawXml.js +30 -6
- 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/paragraphTextBoxEnrichment.js +3 -0
- package/dist/docx/parser.js +2 -2
- package/dist/docx/previewBudget.d.ts +64 -0
- package/dist/docx/previewBudget.js +88 -0
- package/dist/docx/revisionIdNormalization.js +17 -5
- package/dist/docx/rezip.js +17 -18
- package/dist/docx/sdtPropertiesPatch.js +24 -18
- 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 +1 -1
- package/dist/docx/serializer/borderSerializer.js +13 -12
- package/dist/docx/serializer/commentSerializer.d.ts +41 -16
- package/dist/docx/serializer/commentSerializer.js +82 -85
- package/dist/docx/serializer/fontTableSerializer.js +6 -6
- package/dist/docx/serializer/headerFooterSerializer.js +5 -5
- package/dist/docx/serializer/markupRangeAttributes.js +2 -2
- package/dist/docx/serializer/numberingSerializer.js +7 -6
- package/dist/docx/serializer/paragraphSerializer.js +19 -18
- package/dist/docx/serializer/partNamespaces.js +2 -2
- package/dist/docx/serializer/runSerializer.js +48 -28
- 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 +10 -9
- 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/validateDocxConformance.js +22 -1
- package/dist/docx/shapeParser.js +7 -5
- package/dist/docx/textBoxParser.js +7 -2
- package/dist/docx/unzip.d.ts +23 -0
- package/dist/docx/unzip.js +32 -22
- package/dist/docx/verbatimCapture.js +1 -1
- package/dist/docx/vmlImageParser.js +3 -2
- package/dist/docx/vmlPreview.d.ts +1 -3
- package/dist/docx/vmlPreview.js +2 -30
- package/dist/docx/xmlParser.d.ts +16 -1
- package/dist/docx/xmlParser.js +56 -26
- package/dist/docx/xmlResourceLimits.d.ts +89 -9
- package/dist/docx/xmlResourceLimits.js +105 -24
- package/dist/internal/paragraphFormattingSerialization.js +3 -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/prosemirror/attrs/index.js +69 -0
- package/dist/prosemirror/commands/image.js +1 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +69 -29
- package/dist/prosemirror/conversion/toProseDoc.js +83 -19
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +2 -3
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +4 -0
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +7 -2
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +8 -4
- package/dist/prosemirror/paragraphFormattingProvenance.d.ts +6 -3
- package/dist/prosemirror/paragraphFormattingProvenance.js +12 -3
- package/dist/prosemirror/schema/nodes.d.ts +50 -1
- 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/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
|
@@ -10,8 +10,9 @@ import { serializeRun } from "./runSerializer.js";
|
|
|
10
10
|
import { serializeSectionProperties } from "./sectionPropertiesSerializer.js";
|
|
11
11
|
import { serializeTextFormatting } from "./textFormattingSerializer.js";
|
|
12
12
|
import { serializeTrackedChangeAttributes, trackedChangeAttributeRecord } from "./trackedChangeAttributes.js";
|
|
13
|
-
import {
|
|
13
|
+
import { isSingleWellFormedElement } from "./xmlUtils.js";
|
|
14
14
|
import { panic } from "better-result";
|
|
15
|
+
import { escapeXmlAttribute, escapeXmlText } from "@stll/docx-core";
|
|
15
16
|
import { PARAGRAPH_MARK_CHANGE_KINDS } from "@stll/docx-core/model";
|
|
16
17
|
//#region src/docx/serializer/paragraphSerializer.ts
|
|
17
18
|
/**
|
|
@@ -331,13 +332,13 @@ function serializeParagraphPropertyChange(change) {
|
|
|
331
332
|
/** The attribute list of a `w:hyperlink`, without its children. */
|
|
332
333
|
function hyperlinkAttributes(hyperlink) {
|
|
333
334
|
const attrs = [];
|
|
334
|
-
if (hyperlink.rId) attrs.push(`r:id="${
|
|
335
|
-
if (hyperlink.anchor) attrs.push(`w:anchor="${
|
|
336
|
-
if (hyperlink.tooltip) attrs.push(`w:tooltip="${
|
|
337
|
-
if (hyperlink.target) attrs.push(`w:tgtFrame="${
|
|
335
|
+
if (hyperlink.rId) attrs.push(`r:id="${escapeXmlAttribute(hyperlink.rId)}"`);
|
|
336
|
+
if (hyperlink.anchor) attrs.push(`w:anchor="${escapeXmlAttribute(hyperlink.anchor)}"`);
|
|
337
|
+
if (hyperlink.tooltip) attrs.push(`w:tooltip="${escapeXmlAttribute(hyperlink.tooltip)}"`);
|
|
338
|
+
if (hyperlink.target) attrs.push(`w:tgtFrame="${escapeXmlAttribute(hyperlink.target)}"`);
|
|
338
339
|
if (hyperlink.history === true) attrs.push("w:history=\"1\"");
|
|
339
340
|
else if (hyperlink.history === false) attrs.push("w:history=\"0\"");
|
|
340
|
-
if (hyperlink.docLocation) attrs.push(`w:docLocation="${
|
|
341
|
+
if (hyperlink.docLocation) attrs.push(`w:docLocation="${escapeXmlAttribute(hyperlink.docLocation)}"`);
|
|
341
342
|
return attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
|
|
342
343
|
}
|
|
343
344
|
/** One `w:hyperlink` child, with the caller deciding how a run is written. */
|
|
@@ -366,7 +367,7 @@ function serializeBookmarkEnd(bookmark) {
|
|
|
366
367
|
}
|
|
367
368
|
/** Serialize a simple field without changing its authored OOXML field form. */
|
|
368
369
|
function serializeSimpleField(field) {
|
|
369
|
-
const attrs = [`w:instr="${
|
|
370
|
+
const attrs = [`w:instr="${escapeXmlAttribute(field.instruction)}"`];
|
|
370
371
|
if (field.fldLock) attrs.push("w:fldLock=\"true\"");
|
|
371
372
|
if (field.dirty) attrs.push("w:dirty=\"true\"");
|
|
372
373
|
const contentXml = field.content.map((item) => item.type === "run" ? serializeRun(item) : serializeHyperlink(item)).join("");
|
|
@@ -388,7 +389,7 @@ function serializeComplexField(field) {
|
|
|
388
389
|
if (field.fieldCode.length > 0) parts.push(...field.fieldCode.map((run) => serializeRun(run)));
|
|
389
390
|
else if (field.instruction.length > 0) {
|
|
390
391
|
const spaceAttr = field.instruction.startsWith(" ") || field.instruction.endsWith(" ") || field.instruction.includes(" ") ? " xml:space=\"preserve\"" : "";
|
|
391
|
-
parts.push(`<w:r>${rPrXml}<w:instrText${spaceAttr}>${
|
|
392
|
+
parts.push(`<w:r>${rPrXml}<w:instrText${spaceAttr}>${escapeXmlText(field.instruction)}</w:instrText></w:r>`);
|
|
392
393
|
}
|
|
393
394
|
parts.push(`<w:r>${rPrXml}<w:fldChar w:fldCharType="separate"/></w:r>`);
|
|
394
395
|
parts.push(...field.fieldResult.map((run) => serializeRun(run)));
|
|
@@ -406,29 +407,29 @@ function serializeComplexField(field) {
|
|
|
406
407
|
function synthesizeInlineSdtPr(props) {
|
|
407
408
|
const prParts = [];
|
|
408
409
|
if (typeof props.id === "number") prParts.push(`<w:id w:val="${props.id}"/>`);
|
|
409
|
-
if (props.alias) prParts.push(`<w:alias w:val="${
|
|
410
|
-
if (props.tag) prParts.push(`<w:tag w:val="${
|
|
410
|
+
if (props.alias) prParts.push(`<w:alias w:val="${escapeXmlAttribute(props.alias)}"/>`);
|
|
411
|
+
if (props.tag) prParts.push(`<w:tag w:val="${escapeXmlAttribute(props.tag)}"/>`);
|
|
411
412
|
if (props.lock && props.lock !== "unlocked") prParts.push(`<w:lock w:val="${props.lock}"/>`);
|
|
412
|
-
if (props.placeholder) prParts.push(`<w:placeholder><w:docPart w:val="${
|
|
413
|
+
if (props.placeholder) prParts.push(`<w:placeholder><w:docPart w:val="${escapeXmlAttribute(props.placeholder)}"/></w:placeholder>`);
|
|
413
414
|
if (props.showingPlaceholder) prParts.push("<w:showingPlcHdr/>");
|
|
414
415
|
switch (props.sdtType) {
|
|
415
416
|
case "plainText":
|
|
416
417
|
prParts.push("<w:text/>");
|
|
417
418
|
break;
|
|
418
419
|
case "date": {
|
|
419
|
-
const fullDateAttr = props.dateValueISO ? ` w:fullDate="${
|
|
420
|
-
const formatChild = props.dateFormat ? `<w:dateFormat w:val="${
|
|
420
|
+
const fullDateAttr = props.dateValueISO ? ` w:fullDate="${escapeXmlAttribute(props.dateValueISO)}"` : "";
|
|
421
|
+
const formatChild = props.dateFormat ? `<w:dateFormat w:val="${escapeXmlAttribute(props.dateFormat)}"/>` : "";
|
|
421
422
|
if (fullDateAttr || formatChild) prParts.push(`<w:date${fullDateAttr}>${formatChild}</w:date>`);
|
|
422
423
|
else prParts.push("<w:date/>");
|
|
423
424
|
break;
|
|
424
425
|
}
|
|
425
426
|
case "dropdown": {
|
|
426
|
-
const items = (props.listItems ?? []).map((i) => `<w:listItem w:displayText="${
|
|
427
|
+
const items = (props.listItems ?? []).map((i) => `<w:listItem w:displayText="${escapeXmlAttribute(i.displayText)}" w:value="${escapeXmlAttribute(i.value)}"/>`).join("");
|
|
427
428
|
prParts.push(`<w:dropDownList>${items}</w:dropDownList>`);
|
|
428
429
|
break;
|
|
429
430
|
}
|
|
430
431
|
case "comboBox": {
|
|
431
|
-
const items = (props.listItems ?? []).map((i) => `<w:listItem w:displayText="${
|
|
432
|
+
const items = (props.listItems ?? []).map((i) => `<w:listItem w:displayText="${escapeXmlAttribute(i.displayText)}" w:value="${escapeXmlAttribute(i.value)}"/>`).join("");
|
|
432
433
|
prParts.push(`<w:comboBox>${items}</w:comboBox>`);
|
|
433
434
|
break;
|
|
434
435
|
}
|
|
@@ -585,7 +586,7 @@ function serializeParagraphContent(content, explicitCommentReferenceIds) {
|
|
|
585
586
|
case "moveToRangeEnd": return `<w:moveToRangeEnd ${markupRangeAttributes(content).join(" ")}/>`;
|
|
586
587
|
case "bidiWrapper": {
|
|
587
588
|
const tag = content.control === "override" ? "bdo" : "dir";
|
|
588
|
-
return `<w:${tag}${content.direction === void 0 ? "" : ` w:val="${
|
|
589
|
+
return `<w:${tag}${content.direction === void 0 ? "" : ` w:val="${escapeXmlAttribute(content.direction)}"`}>${content.content.map((child) => serializeParagraphContent(child, explicitCommentReferenceIds)).join("")}</w:${tag}>`;
|
|
589
590
|
}
|
|
590
591
|
case "mathEquation": return content.ommlXml || "";
|
|
591
592
|
default: return "";
|
|
@@ -600,8 +601,8 @@ function serializeParagraphContent(content, explicitCommentReferenceIds) {
|
|
|
600
601
|
function serializeParagraph(paragraph) {
|
|
601
602
|
const parts = [];
|
|
602
603
|
const attrs = [];
|
|
603
|
-
if (paragraph.paraId) attrs.push(`w14:paraId="${
|
|
604
|
-
if (paragraph.textId) attrs.push(`w14:textId="${
|
|
604
|
+
if (paragraph.paraId) attrs.push(`w14:paraId="${escapeXmlAttribute(paragraph.paraId)}"`);
|
|
605
|
+
if (paragraph.textId) attrs.push(`w14:textId="${escapeXmlAttribute(paragraph.textId)}"`);
|
|
605
606
|
if (paragraph.reviewCarrier) attrs.push(`folio:reviewCarrier="${paragraph.reviewCarrier}"`);
|
|
606
607
|
const attrsStr = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
|
|
607
608
|
parts.push(serializeParagraphFormattingWithOptions(paragraph.formatting, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { toTransitionalNamespaceUri } from "../transitionalSpelling.js";
|
|
2
|
-
import { escapeXml } from "./xmlUtils.js";
|
|
3
2
|
import { TaggedError } from "better-result";
|
|
3
|
+
import { escapeXmlAttribute } from "@stll/docx-core";
|
|
4
4
|
import { OOXML_NS } from "@stll/docx-utils";
|
|
5
5
|
//#region src/docx/serializer/partNamespaces.ts
|
|
6
6
|
/**
|
|
@@ -319,7 +319,7 @@ const serializePartElement = ({ partPath, rootName, rootAttributes, baselinePref
|
|
|
319
319
|
...[...NAMESPACE_TABLE.keys()].filter((prefix) => extra.has(prefix)),
|
|
320
320
|
...[...extra].filter((prefix) => !NAMESPACE_TABLE.has(prefix)).sort()
|
|
321
321
|
];
|
|
322
|
-
const attributes = orderedPrefixes.map((prefix) => `xmlns:${prefix}="${
|
|
322
|
+
const attributes = orderedPrefixes.map((prefix) => `xmlns:${prefix}="${escapeXmlAttribute(bindings.get(prefix))}"`);
|
|
323
323
|
const ignorable = orderedPrefixes.filter((prefix) => NAMESPACE_TABLE.get(prefix)?.ignorable === true);
|
|
324
324
|
if (ignorable.length > 0) attributes.push(`mc:Ignorable="${ignorable.join(" ")}"`);
|
|
325
325
|
return `${openingTag} ${attributes.join(" ")}>${body}</${rootName}>`;
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { isValidHexColor } from "../../utils/colorResolver.js";
|
|
2
2
|
import { THEME_COLOR_TO_DRAWING_SCHEME } from "../drawingUtils.js";
|
|
3
3
|
import { serializeGraphicFrameLocks } from "../graphicFrameLocks.js";
|
|
4
|
+
import { DECORATIVE_EXTENSION_URI, DECORATIVE_NAMESPACE } from "../imageParser.js";
|
|
4
5
|
import { canReplayEditableImageRawXml } from "../imageRawXml.js";
|
|
6
|
+
import { serializeNonVisualDrawingNames } from "../nonVisualDrawingProps.js";
|
|
5
7
|
import { serializeParagraph } from "./paragraphSerializer.js";
|
|
6
8
|
import { serializeTable } from "./tableSerializer.js";
|
|
7
9
|
import { serializeTextFormatting } from "./textFormattingSerializer.js";
|
|
8
10
|
import { getSingularRunPropertyChange, serializeTrackedChangeAttributes } from "./trackedChangeAttributes.js";
|
|
9
|
-
import {
|
|
11
|
+
import { intAttr } from "./xmlUtils.js";
|
|
10
12
|
import { panic } from "better-result";
|
|
11
|
-
import { requiresXmlSpacePreserve } from "@stll/docx-core";
|
|
13
|
+
import { escapeXmlAttribute, escapeXmlText, requiresXmlSpacePreserve } from "@stll/docx-core";
|
|
12
14
|
//#region src/docx/serializer/runSerializer.ts
|
|
13
15
|
/**
|
|
14
16
|
* Run Serializer - Serialize runs to OOXML XML
|
|
@@ -59,7 +61,7 @@ function serializeRunProperties(formatting, propertyChanges) {
|
|
|
59
61
|
* Serialize text content (w:t)
|
|
60
62
|
*/
|
|
61
63
|
function serializeTextContent(content) {
|
|
62
|
-
return `<w:t${requiresXmlSpacePreserve(content.text) ? " xml:space=\"preserve\"" : ""}>${
|
|
64
|
+
return `<w:t${requiresXmlSpacePreserve(content.text) ? " xml:space=\"preserve\"" : ""}>${escapeXmlText(content.text)}</w:t>`;
|
|
63
65
|
}
|
|
64
66
|
/**
|
|
65
67
|
* Serialize tab content (w:tab)
|
|
@@ -67,9 +69,9 @@ function serializeTextContent(content) {
|
|
|
67
69
|
function serializeTabContent(content) {
|
|
68
70
|
if (!content.positional) return "<w:tab/>";
|
|
69
71
|
return `<w:ptab${[
|
|
70
|
-
content.positional.relativeTo ? ` w:relativeTo="${
|
|
71
|
-
content.positional.alignment ? ` w:alignment="${
|
|
72
|
-
content.positional.leader ? ` w:leader="${
|
|
72
|
+
content.positional.relativeTo ? ` w:relativeTo="${escapeXmlAttribute(content.positional.relativeTo)}"` : "",
|
|
73
|
+
content.positional.alignment ? ` w:alignment="${escapeXmlAttribute(content.positional.alignment)}"` : "",
|
|
74
|
+
content.positional.leader ? ` w:leader="${escapeXmlAttribute(content.positional.leader)}"` : ""
|
|
73
75
|
].join("")}/>`;
|
|
74
76
|
}
|
|
75
77
|
/**
|
|
@@ -88,7 +90,7 @@ function serializeBreakContent(content) {
|
|
|
88
90
|
* Serialize symbol content (w:sym)
|
|
89
91
|
*/
|
|
90
92
|
function serializeSymbolContent(content) {
|
|
91
|
-
return `<w:sym${content.font === "" ? "" : ` w:font="${
|
|
93
|
+
return `<w:sym${content.font === "" ? "" : ` w:font="${escapeXmlAttribute(content.font)}"`}${content.char === "" ? "" : ` w:char="${escapeXmlAttribute(content.char)}"`}/>`;
|
|
92
94
|
}
|
|
93
95
|
/**
|
|
94
96
|
* Serialize footnote/endnote reference
|
|
@@ -110,7 +112,7 @@ function serializeFieldChar(content) {
|
|
|
110
112
|
* Serialize field instruction text (w:instrText)
|
|
111
113
|
*/
|
|
112
114
|
function serializeInstrText(content) {
|
|
113
|
-
return `<w:instrText${content.text.startsWith(" ") || content.text.endsWith(" ") || content.text.includes(" ") ? " xml:space=\"preserve\"" : ""}>${
|
|
115
|
+
return `<w:instrText${content.text.startsWith(" ") || content.text.endsWith(" ") || content.text.includes(" ") ? " xml:space=\"preserve\"" : ""}>${escapeXmlText(content.text)}</w:instrText>`;
|
|
114
116
|
}
|
|
115
117
|
/**
|
|
116
118
|
* Serialize soft hyphen (w:softHyphen)
|
|
@@ -127,11 +129,11 @@ function serializeNoBreakHyphen(_content) {
|
|
|
127
129
|
/** Serialize a color value to DrawingML a:srgbClr or a:schemeClr */
|
|
128
130
|
function serializeDrawingColor(color) {
|
|
129
131
|
if (!color) return "";
|
|
130
|
-
if (color.rgb && isValidHexColor(color.rgb)) return `<a:srgbClr val="${
|
|
132
|
+
if (color.rgb && isValidHexColor(color.rgb)) return `<a:srgbClr val="${escapeXmlAttribute(color.rgb.replace("#", ""))}"/>`;
|
|
131
133
|
if (color.themeColor) {
|
|
132
134
|
let clr = `<a:schemeClr val="${THEME_COLOR_TO_DRAWING_SCHEME[color.themeColor]}"`;
|
|
133
|
-
if (color.themeTint) clr += `><a:tint val="${
|
|
134
|
-
else if (color.themeShade) clr += `><a:shade val="${
|
|
135
|
+
if (color.themeTint) clr += `><a:tint val="${escapeXmlAttribute(color.themeTint)}"/></a:schemeClr>`;
|
|
136
|
+
else if (color.themeShade) clr += `><a:shade val="${escapeXmlAttribute(color.themeShade)}"/></a:schemeClr>`;
|
|
135
137
|
else clr += `/>`;
|
|
136
138
|
return clr;
|
|
137
139
|
}
|
|
@@ -216,7 +218,7 @@ function serializeWrap(wrap) {
|
|
|
216
218
|
function serializePicGraphic(image, imageRId, sharedId) {
|
|
217
219
|
const cx = image.size.width;
|
|
218
220
|
const cy = image.size.height;
|
|
219
|
-
const rId =
|
|
221
|
+
const rId = escapeXmlAttribute(imageRId);
|
|
220
222
|
const id = sharedId;
|
|
221
223
|
const name = image.filename || `image${id}`;
|
|
222
224
|
let xfrmAttrs = "";
|
|
@@ -236,7 +238,7 @@ function serializePicGraphic(image, imageRId, sharedId) {
|
|
|
236
238
|
"<a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">",
|
|
237
239
|
"<pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">",
|
|
238
240
|
"<pic:nvPicPr>",
|
|
239
|
-
`<pic:cNvPr id="${id}" name="${
|
|
241
|
+
`<pic:cNvPr id="${id}" name="${escapeXmlAttribute(name)}"${image.alt ? ` descr="${escapeXmlAttribute(image.alt)}"` : ""}/>`,
|
|
240
242
|
"<pic:cNvPicPr/>",
|
|
241
243
|
"</pic:nvPicPr>",
|
|
242
244
|
"<pic:blipFill>",
|
|
@@ -270,6 +272,19 @@ function serializeWrapDistanceAttrs(wrap) {
|
|
|
270
272
|
return attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
|
|
271
273
|
}
|
|
272
274
|
/**
|
|
275
|
+
* Serialize the `wp:docPr` extension list.
|
|
276
|
+
*
|
|
277
|
+
* The decorative flag is the one extension folio models; every other `a:ext`
|
|
278
|
+
* the source carried is replayed as it was captured, in its original order,
|
|
279
|
+
* because an extension dropped on save is a fact the document had and lost.
|
|
280
|
+
* The decorative extension goes first: it is the one folio may have added.
|
|
281
|
+
*/
|
|
282
|
+
function serializeDocPrExtensions(image) {
|
|
283
|
+
const preserved = image.docPrExtensions ?? [];
|
|
284
|
+
const entries = `${image.decorative === void 0 ? "" : `<a:ext uri="${DECORATIVE_EXTENSION_URI}"><adec:decorative xmlns:adec="${DECORATIVE_NAMESPACE}" val="${image.decorative ? "1" : "0"}"/></a:ext>`}${preserved.join("")}`;
|
|
285
|
+
return entries ? `<a:extLst>${entries}</a:extLst>` : "";
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
273
288
|
* Serialize drawing/image content (w:drawing) to full DrawingML XML
|
|
274
289
|
*/
|
|
275
290
|
function serializeDrawingContent(content) {
|
|
@@ -284,14 +299,15 @@ function serializeDrawingContent(content) {
|
|
|
284
299
|
const effB = image.padding?.bottom ?? 0;
|
|
285
300
|
const effectExtentEl = `<wp:effectExtent l="${intAttr(effL)}" t="${intAttr(effT)}" r="${intAttr(effR)}" b="${intAttr(effB)}"/>`;
|
|
286
301
|
const docPrId = getUniqueId(image.id);
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
const
|
|
294
|
-
const
|
|
302
|
+
const docPrNames = serializeNonVisualDrawingNames({
|
|
303
|
+
...image.docPrName !== void 0 ? { name: image.docPrName } : {},
|
|
304
|
+
...image.alt !== void 0 ? { alt: image.alt } : {},
|
|
305
|
+
...image.title !== void 0 ? { title: image.title } : {}
|
|
306
|
+
});
|
|
307
|
+
const hlinkClick = image.hlinkRId ? `<a:hlinkClick r:id="${escapeXmlAttribute(image.hlinkRId)}"/>` : "";
|
|
308
|
+
const docPrAttrs = `id="${docPrId}"${docPrNames}${image.hidden === void 0 ? "" : ` hidden="${image.hidden ? "1" : "0"}"`}`;
|
|
309
|
+
const docPrChildren = `${hlinkClick}${serializeDocPrExtensions(image)}`;
|
|
310
|
+
const docPr = docPrChildren ? `<wp:docPr ${docPrAttrs}>${docPrChildren}</wp:docPr>` : `<wp:docPr ${docPrAttrs}/>`;
|
|
295
311
|
const graphicFramePr = serializeGraphicFrameLocks(image.frameLocks);
|
|
296
312
|
const graphic = image.rId === void 0 ? "" : serializePicGraphic(image, image.rId, docPrId);
|
|
297
313
|
if (!isFloating) return [
|
|
@@ -299,7 +315,7 @@ function serializeDrawingContent(content) {
|
|
|
299
315
|
`<wp:inline${wrapDistanceAttrs}>`,
|
|
300
316
|
`<wp:extent cx="${intAttr(cx)}" cy="${intAttr(cy)}"/>`,
|
|
301
317
|
effectExtentEl,
|
|
302
|
-
|
|
318
|
+
docPr,
|
|
303
319
|
graphicFramePr,
|
|
304
320
|
graphic,
|
|
305
321
|
"</wp:inline>",
|
|
@@ -316,7 +332,7 @@ function serializeDrawingContent(content) {
|
|
|
316
332
|
`<wp:extent cx="${intAttr(cx)}" cy="${intAttr(cy)}"/>`,
|
|
317
333
|
effectExtentEl,
|
|
318
334
|
wrap,
|
|
319
|
-
|
|
335
|
+
docPr,
|
|
320
336
|
graphicFramePr,
|
|
321
337
|
graphic,
|
|
322
338
|
"</wp:anchor>",
|
|
@@ -339,7 +355,7 @@ const serializeTextBodyAnchor = (anchor) => {
|
|
|
339
355
|
};
|
|
340
356
|
function serializeGeometryAdjustments(shape) {
|
|
341
357
|
if (!shape.geometryAdjustments || shape.geometryAdjustments.length === 0) return "<a:avLst/>";
|
|
342
|
-
return `<a:avLst>${shape.geometryAdjustments.map(({ name, formula }) => `<a:gd name="${
|
|
358
|
+
return `<a:avLst>${shape.geometryAdjustments.map(({ name, formula }) => `<a:gd name="${escapeXmlAttribute(name)}" fmla="${escapeXmlAttribute(formula)}"/>`).join("")}</a:avLst>`;
|
|
343
359
|
}
|
|
344
360
|
/**
|
|
345
361
|
* Serialize shape content to full DrawingML XML (wps:wsp inside w:drawing)
|
|
@@ -352,7 +368,11 @@ function serializeShapeContent(content) {
|
|
|
352
368
|
const isFloating = shape.wrap && shape.wrap.type !== "inline";
|
|
353
369
|
const wrapDistances = serializeWrapDistanceAttrs(shape.wrap);
|
|
354
370
|
const docPrId = getUniqueId(shape.id);
|
|
355
|
-
const
|
|
371
|
+
const docPrNames = serializeNonVisualDrawingNames({
|
|
372
|
+
...shape.name !== void 0 ? { name: shape.name } : {},
|
|
373
|
+
...shape.alt !== void 0 ? { alt: shape.alt } : {},
|
|
374
|
+
...shape.title !== void 0 ? { title: shape.title } : {}
|
|
375
|
+
});
|
|
356
376
|
let xfrmAttrs = "";
|
|
357
377
|
if (shape.transform?.rotation) xfrmAttrs += ` rot="${Math.round(shape.transform.rotation * 6e4)}"`;
|
|
358
378
|
if (shape.transform?.flipH) xfrmAttrs += " flipH=\"1\"";
|
|
@@ -386,7 +406,7 @@ function serializeShapeContent(content) {
|
|
|
386
406
|
if (tb.autoFit === "shape") autoFitXml = "<a:spAutoFit/>";
|
|
387
407
|
else if (tb.autoFit === "normal") autoFitXml = "<a:normAutofit/>";
|
|
388
408
|
else if (tb.autoFit === "none") autoFitXml = "<a:noAutofit/>";
|
|
389
|
-
const bodyPrChildren = `${tb.wordArt?.preset ? `<a:prstTxWarp prst="${
|
|
409
|
+
const bodyPrChildren = `${tb.wordArt?.preset ? `<a:prstTxWarp prst="${escapeXmlAttribute(tb.wordArt.preset)}">${tb.wordArt.adjustments && tb.wordArt.adjustments.length > 0 ? `<a:avLst>${tb.wordArt.adjustments.map(({ name, formula }) => `<a:gd name="${escapeXmlAttribute(name)}" fmla="${escapeXmlAttribute(formula)}"/>`).join("")}</a:avLst>` : "<a:avLst/>"}</a:prstTxWarp>` : ""}${autoFitXml}`;
|
|
390
410
|
const bodyPrXml = bodyPrChildren ? `<wps:bodyPr ${bpAttrs.join(" ")}>${bodyPrChildren}</wps:bodyPr>` : `<wps:bodyPr ${bpAttrs.join(" ")}/>`;
|
|
391
411
|
if (isTextBox) textBody = [
|
|
392
412
|
"<wps:txbx><w:txbxContent>",
|
|
@@ -414,7 +434,7 @@ function serializeShapeContent(content) {
|
|
|
414
434
|
`<wp:inline${wrapDistances}>`,
|
|
415
435
|
`<wp:extent cx="${intAttr(cx)}" cy="${intAttr(cy)}"/>`,
|
|
416
436
|
"<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>",
|
|
417
|
-
`<wp:docPr id="${docPrId}"
|
|
437
|
+
`<wp:docPr id="${docPrId}"${docPrNames}/>`,
|
|
418
438
|
"<wp:cNvGraphicFramePr/>",
|
|
419
439
|
graphic,
|
|
420
440
|
"</wp:inline>",
|
|
@@ -432,7 +452,7 @@ function serializeShapeContent(content) {
|
|
|
432
452
|
`<wp:extent cx="${intAttr(cx)}" cy="${intAttr(cy)}"/>`,
|
|
433
453
|
"<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>",
|
|
434
454
|
wrap,
|
|
435
|
-
`<wp:docPr id="${docPrId}"
|
|
455
|
+
`<wp:docPr id="${docPrId}"${docPrNames}/>`,
|
|
436
456
|
"<wp:cNvGraphicFramePr/>",
|
|
437
457
|
graphic,
|
|
438
458
|
"</wp:anchor>",
|
|
@@ -2,10 +2,11 @@ import { getUnserializedSectionPropertyChildNames } from "../sectionParser.js";
|
|
|
2
2
|
import { serializeSectionReferenceHistory } from "../sectionReferenceHistory.js";
|
|
3
3
|
import { serializeBorder } from "./borderSerializer.js";
|
|
4
4
|
import { serializeTrackedChangeAttributes } from "./trackedChangeAttributes.js";
|
|
5
|
-
import {
|
|
5
|
+
import { intAttr } from "./xmlUtils.js";
|
|
6
|
+
import { escapeXmlAttribute } from "@stll/docx-core";
|
|
6
7
|
//#region src/docx/serializer/sectionPropertiesSerializer.ts
|
|
7
|
-
const serializeHeaderReference = (ref) => `<w:headerReference w:type="${ref.type}" r:id="${
|
|
8
|
-
const serializeFooterReference = (ref) => `<w:footerReference w:type="${ref.type}" r:id="${
|
|
8
|
+
const serializeHeaderReference = (ref) => `<w:headerReference w:type="${ref.type}" r:id="${escapeXmlAttribute(ref.rId)}"/>`;
|
|
9
|
+
const serializeFooterReference = (ref) => `<w:footerReference w:type="${ref.type}" r:id="${escapeXmlAttribute(ref.rId)}"/>`;
|
|
9
10
|
function serializeFootnoteProperties(props) {
|
|
10
11
|
if (!props) return "";
|
|
11
12
|
const parts = [];
|
|
@@ -84,7 +85,7 @@ function serializePageNumbering(props) {
|
|
|
84
85
|
if (pageNumbering.format) attrs.push(`w:fmt="${pageNumbering.format}"`);
|
|
85
86
|
if (pageNumbering.start !== void 0) attrs.push(`w:start="${intAttr(pageNumbering.start)}"`);
|
|
86
87
|
if (pageNumbering.chapterStyle !== void 0) attrs.push(`w:chapStyle="${intAttr(pageNumbering.chapterStyle)}"`);
|
|
87
|
-
if (pageNumbering.chapterSeparator) attrs.push(`w:chapSep="${
|
|
88
|
+
if (pageNumbering.chapterSeparator) attrs.push(`w:chapSep="${escapeXmlAttribute(pageNumbering.chapterSeparator)}"`);
|
|
88
89
|
return attrs.length > 0 ? `<w:pgNumType ${attrs.join(" ")}/>` : "";
|
|
89
90
|
}
|
|
90
91
|
function serializePageBorders(props) {
|
|
@@ -114,10 +115,10 @@ function serializeBackground(props) {
|
|
|
114
115
|
const attrs = [];
|
|
115
116
|
const { background } = props;
|
|
116
117
|
if (background.color?.auto) attrs.push("w:color=\"auto\"");
|
|
117
|
-
else if (background.color?.rgb) attrs.push(`w:color="${
|
|
118
|
-
if (background.themeColor ?? background.color?.themeColor) attrs.push(`w:themeColor="${
|
|
119
|
-
if (background.themeTint ?? background.color?.themeTint) attrs.push(`w:themeTint="${
|
|
120
|
-
if (background.themeShade ?? background.color?.themeShade) attrs.push(`w:themeShade="${
|
|
118
|
+
else if (background.color?.rgb) attrs.push(`w:color="${escapeXmlAttribute(background.color.rgb)}"`);
|
|
119
|
+
if (background.themeColor ?? background.color?.themeColor) attrs.push(`w:themeColor="${escapeXmlAttribute(background.themeColor ?? background.color?.themeColor ?? "")}"`);
|
|
120
|
+
if (background.themeTint ?? background.color?.themeTint) attrs.push(`w:themeTint="${escapeXmlAttribute(background.themeTint ?? background.color?.themeTint ?? "")}"`);
|
|
121
|
+
if (background.themeShade ?? background.color?.themeShade) attrs.push(`w:themeShade="${escapeXmlAttribute(background.themeShade ?? background.color?.themeShade ?? "")}"`);
|
|
121
122
|
return attrs.length > 0 ? `<w:background ${attrs.join(" ")}/>` : "";
|
|
122
123
|
}
|
|
123
124
|
function serializeDocGrid(props) {
|
|
@@ -127,7 +128,7 @@ function serializeDocGrid(props) {
|
|
|
127
128
|
if (dg.type) attrs.push(`w:type="${dg.type}"`);
|
|
128
129
|
if (dg.linePitch !== void 0) attrs.push(`w:linePitch="${intAttr(dg.linePitch)}"`);
|
|
129
130
|
if (dg.charSpace !== void 0) attrs.push(`w:charSpace="${intAttr(dg.charSpace)}"`);
|
|
130
|
-
return attrs.length > 0 ? `<w:docGrid ${attrs.join(" ")}/>` : "";
|
|
131
|
+
return attrs.length > 0 ? `<w:docGrid ${attrs.join(" ")}/>` : "<w:docGrid/>";
|
|
131
132
|
}
|
|
132
133
|
function serializeOnOffElement(value, name) {
|
|
133
134
|
if (value === void 0) return "";
|
|
@@ -173,7 +174,7 @@ function serializeSectionProperties(props) {
|
|
|
173
174
|
serializeOnOffElement(props.noEndnote, "noEndnote"),
|
|
174
175
|
serializeOnOffElement(props.rtlGutter, "rtlGutter")
|
|
175
176
|
]) if (xml) parts.push(xml);
|
|
176
|
-
if (props.printerSettingsRelationshipId) parts.push(`<w:printerSettings r:id="${
|
|
177
|
+
if (props.printerSettingsRelationshipId) parts.push(`<w:printerSettings r:id="${escapeXmlAttribute(props.printerSettingsRelationshipId)}"/>`);
|
|
177
178
|
for (const change of props.propertyChanges ?? []) parts.push(serializeSectionPropertyChange(change));
|
|
178
179
|
const unserializedChildNames = getUnserializedSectionPropertyChildNames(props);
|
|
179
180
|
if (unserializedChildNames && unserializedChildNames.length > 0) return "";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { serializePartElement } from "./partNamespaces.js";
|
|
2
|
-
import {
|
|
2
|
+
import { intAttr } from "./xmlUtils.js";
|
|
3
|
+
import { escapeXmlAttribute } from "@stll/docx-core";
|
|
3
4
|
//#region src/docx/serializer/settingsSerializer.ts
|
|
4
5
|
const serializeSettingsXml = (settings) => {
|
|
5
6
|
const parts = [`<w:defaultTabStop w:val="${intAttr(settings.defaultTabStop)}"/>`];
|
|
@@ -7,8 +8,8 @@ const serializeSettingsXml = (settings) => {
|
|
|
7
8
|
if (settings.updateFields) parts.push("<w:updateFields w:val=\"true\"/>");
|
|
8
9
|
if (settings.themeFontLang) {
|
|
9
10
|
const attrs = [];
|
|
10
|
-
if (settings.themeFontLang.eastAsia) attrs.push(`w:eastAsia="${
|
|
11
|
-
if (settings.themeFontLang.bidi) attrs.push(`w:bidi="${
|
|
11
|
+
if (settings.themeFontLang.eastAsia) attrs.push(`w:eastAsia="${escapeXmlAttribute(settings.themeFontLang.eastAsia)}"`);
|
|
12
|
+
if (settings.themeFontLang.bidi) attrs.push(`w:bidi="${escapeXmlAttribute(settings.themeFontLang.bidi)}"`);
|
|
12
13
|
if (attrs.length > 0) parts.push(`<w:themeFontLang ${attrs.join(" ")}/>`);
|
|
13
14
|
}
|
|
14
15
|
return "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" + serializePartElement({
|
|
@@ -2,7 +2,7 @@ import { serializeParagraphFormatting } from "./paragraphSerializer.js";
|
|
|
2
2
|
import { serializePartElement } from "./partNamespaces.js";
|
|
3
3
|
import { serializeTableCellFormatting, serializeTableFormatting, serializeTableRowFormatting } from "./tableSerializer.js";
|
|
4
4
|
import { serializeTextFormatting } from "./textFormattingSerializer.js";
|
|
5
|
-
import {
|
|
5
|
+
import { escapeXmlAttribute } from "@stll/docx-core";
|
|
6
6
|
//#region src/docx/serializer/stylesSerializer.ts
|
|
7
7
|
const serializeStylesXml = (definitions) => {
|
|
8
8
|
return "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" + serializePartElement({
|
|
@@ -28,12 +28,12 @@ const serializeLatentStyles = (definitions) => {
|
|
|
28
28
|
return `<w:latentStyles${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""}/>`;
|
|
29
29
|
};
|
|
30
30
|
const serializeStyle = (style) => {
|
|
31
|
-
const attrs = [`w:type="${style.type}"`, `w:styleId="${
|
|
31
|
+
const attrs = [`w:type="${style.type}"`, `w:styleId="${escapeXmlAttribute(style.styleId)}"`];
|
|
32
32
|
if (style.default) attrs.push("w:default=\"1\"");
|
|
33
|
-
const parts = [`<w:name w:val="${
|
|
34
|
-
if (style.basedOn) parts.push(`<w:basedOn w:val="${
|
|
35
|
-
if (style.next) parts.push(`<w:next w:val="${
|
|
36
|
-
if (style.link) parts.push(`<w:link w:val="${
|
|
33
|
+
const parts = [`<w:name w:val="${escapeXmlAttribute(style.name ?? style.styleId)}"/>`];
|
|
34
|
+
if (style.basedOn) parts.push(`<w:basedOn w:val="${escapeXmlAttribute(style.basedOn)}"/>`);
|
|
35
|
+
if (style.next) parts.push(`<w:next w:val="${escapeXmlAttribute(style.next)}"/>`);
|
|
36
|
+
if (style.link) parts.push(`<w:link w:val="${escapeXmlAttribute(style.link)}"/>`);
|
|
37
37
|
if (style.uiPriority !== void 0) parts.push(`<w:uiPriority w:val="${style.uiPriority}"/>`);
|
|
38
38
|
pushBooleanElement(parts, "hidden", style.hidden);
|
|
39
39
|
pushBooleanElement(parts, "semiHidden", style.semiHidden);
|
|
@@ -7,7 +7,8 @@ import { sanitizeCapturedXmlElement } from "../verbatimCapture.js";
|
|
|
7
7
|
import { NAMESPACES, OOXML_NAMESPACE_SCOPE, parseXml } from "../xmlParser.js";
|
|
8
8
|
import { serializeBorder } from "./borderSerializer.js";
|
|
9
9
|
import { serializeTrackedChangeAttributes } from "./trackedChangeAttributes.js";
|
|
10
|
-
import {
|
|
10
|
+
import { intAttr } from "./xmlUtils.js";
|
|
11
|
+
import { escapeXmlAttribute } from "@stll/docx-core";
|
|
11
12
|
//#region src/docx/serializer/tableSerializer.ts
|
|
12
13
|
/**
|
|
13
14
|
* The captured element with the revision children written back into it.
|
|
@@ -130,15 +131,15 @@ function serializeCellMargins(margins, elementName) {
|
|
|
130
131
|
function serializeShading(shading) {
|
|
131
132
|
if (!shading) return "";
|
|
132
133
|
const attrs = [];
|
|
133
|
-
if (shading.pattern) attrs.push(`w:val="${
|
|
134
|
+
if (shading.pattern) attrs.push(`w:val="${escapeXmlAttribute(shading.pattern)}"`);
|
|
134
135
|
else attrs.push("w:val=\"clear\"");
|
|
135
|
-
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)}"`);
|
|
136
137
|
else if (shading.color?.auto) attrs.push("w:color=\"auto\"");
|
|
137
|
-
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)}"`);
|
|
138
139
|
else if (shading.fill?.auto) attrs.push("w:fill=\"auto\"");
|
|
139
|
-
if (shading.fill?.themeColor) attrs.push(`w:themeFill="${
|
|
140
|
-
if (shading.fill?.themeTint) attrs.push(`w:themeFillTint="${
|
|
141
|
-
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)}"`);
|
|
142
143
|
if (attrs.length === 0) return "";
|
|
143
144
|
return `<w:shd ${attrs.join(" ")}/>`;
|
|
144
145
|
}
|
|
@@ -154,7 +155,7 @@ function serializeShading(shading) {
|
|
|
154
155
|
function serializeTableLook(look) {
|
|
155
156
|
if (!look) return "";
|
|
156
157
|
const attrs = [];
|
|
157
|
-
if (look.val !== void 0) attrs.push(`w:val="${
|
|
158
|
+
if (look.val !== void 0) attrs.push(`w:val="${escapeXmlAttribute(look.val)}"`);
|
|
158
159
|
for (const flag of TABLE_LOOK_FLAGS) {
|
|
159
160
|
const stated = look[flag];
|
|
160
161
|
if (stated !== void 0) attrs.push(`w:${flag}="${stated ? "1" : "0"}"`);
|
|
@@ -189,7 +190,7 @@ function serializeTableFormatting(formatting, propertyChanges) {
|
|
|
189
190
|
if (tableSource !== null) return withRevisionChildren(tableSource, (propertyChanges ?? []).map((change) => serializeTablePropertyChange(change)));
|
|
190
191
|
const parts = [];
|
|
191
192
|
if (formatting) {
|
|
192
|
-
if (formatting.styleId) parts.push(`<w:tblStyle w:val="${
|
|
193
|
+
if (formatting.styleId) parts.push(`<w:tblStyle w:val="${escapeXmlAttribute(formatting.styleId)}"/>`);
|
|
193
194
|
const floatingXml = serializeFloatingTableProperties(formatting.floating);
|
|
194
195
|
if (floatingXml) parts.push(floatingXml);
|
|
195
196
|
if (formatting.overlap) parts.push(`<w:tblOverlap w:val="${formatting.overlap}"/>`);
|
|
@@ -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>`;
|