@stll/folio-core 0.44.0 → 0.46.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/apply.js +59 -19
- package/dist/ai-edits/headless.js +2 -1
- package/dist/compare/compare.js +2 -5
- package/dist/compare/content-alignment.js +78 -53
- package/dist/compare/inline-atoms.js +34 -20
- package/dist/compare/scenario.js +2 -5
- package/dist/compare/types.d.ts +9 -1
- package/dist/compare/types.js +12 -1
- package/dist/compat/eigenpal.d.ts +2 -1
- package/dist/compat/eigenpal.js +2 -1
- package/dist/content-controls/checkboxDisplay.d.ts +11 -0
- package/dist/content-controls/checkboxDisplay.js +40 -0
- package/dist/content-controls/findContentControls.js +3 -1
- package/dist/content-controls/mutateContentControls.js +21 -4
- package/dist/controller/contentControlWidgetController.d.ts +1 -0
- package/dist/controller/contentControlWidgetController.js +3 -0
- package/dist/controller/hiddenEditorApi.js +3 -1
- package/dist/controller/hiddenEditorManager.d.ts +1 -1
- package/dist/controller/hiddenEditorManager.js +6 -3
- package/dist/display-list/build/buildDisplayList.js +20 -1
- package/dist/display-list/build/imagePrimitives.d.ts +1 -1
- package/dist/display-list/build/imagePrimitives.js +3 -1
- package/dist/display-list/build/paragraphPrimitives.js +21 -2
- package/dist/display-list/dom/renderDisplayListToDom.js +18 -10
- package/dist/display-list/types.d.ts +17 -0
- package/dist/document-operations.js +14 -3
- package/dist/docx/attributeRemainder.d.ts +43 -0
- package/dist/docx/attributeRemainder.js +65 -0
- package/dist/docx/blockContentParser.d.ts +1 -1
- package/dist/docx/blockContentParser.js +87 -63
- package/dist/docx/blockPlainText.d.ts +3 -4
- package/dist/docx/blockPlainText.js +1 -0
- package/dist/docx/bookmarkPlacement.js +2 -0
- package/dist/docx/borderParser.js +5 -5
- package/dist/docx/commentAnchorIndex.d.ts +30 -0
- package/dist/docx/commentAnchorIndex.js +50 -0
- package/dist/docx/commentParser.d.ts +1 -1
- package/dist/docx/commentParser.js +78 -50
- package/dist/docx/commentRangeIntegrity.d.ts +16 -1
- package/dist/docx/commentRangeIntegrity.js +42 -1
- package/dist/docx/commentRangeJoin.d.ts +9 -0
- package/dist/docx/commentRangeJoin.js +33 -0
- package/dist/docx/commentReferenceCompletion.d.ts +9 -0
- package/dist/docx/commentReferenceCompletion.js +47 -0
- package/dist/docx/commentReferenceNormalization.js +1 -0
- package/dist/docx/commentReplyMarkers.js +11 -19
- package/dist/docx/commentThreadKey.d.ts +18 -0
- package/dist/docx/commentThreadKey.js +22 -0
- package/dist/docx/compatibility.js +1 -0
- package/dist/docx/containerChildren.d.ts +111 -0
- package/dist/docx/containerChildren.gen.d.ts +28 -0
- package/dist/docx/containerChildren.gen.js +246 -0
- package/dist/docx/containerChildren.js +103 -0
- package/dist/docx/diagramPreview.js +87 -27
- package/dist/docx/documentParser.d.ts +1 -1
- package/dist/docx/documentParser.js +3 -3
- package/dist/docx/ensureParaIds.js +17 -7
- package/dist/docx/fieldParser.d.ts +1 -1
- package/dist/docx/fieldParser.js +4 -4
- package/dist/docx/fieldState.d.ts +26 -0
- package/dist/docx/fieldState.js +53 -0
- package/dist/docx/footnoteParser.d.ts +1 -1
- package/dist/docx/graphicFrameLocks.d.ts +16 -4
- package/dist/docx/graphicFrameLocks.js +19 -6
- package/dist/docx/groupDrawingParser.js +3 -3
- package/dist/docx/headerFooterParser.js +1 -1
- package/dist/docx/headerFooterReferenceNormalization.js +1 -0
- package/dist/docx/hyperlinkParser.d.ts +27 -8
- package/dist/docx/hyperlinkParser.js +78 -20
- package/dist/docx/imageParser.d.ts +9 -1
- package/dist/docx/imageParser.js +91 -20
- package/dist/docx/imageRawXml.d.ts +14 -1
- package/dist/docx/imageRawXml.js +30 -6
- package/dist/docx/inlineWrapperContent.d.ts +85 -0
- package/dist/docx/inlineWrapperContent.js +84 -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/normalizeBaseDirection.js +10 -1
- package/dist/docx/paraIdAttribute.d.ts +21 -0
- package/dist/docx/paraIdAttribute.js +64 -0
- package/dist/docx/paragraphParser.d.ts +1 -1
- package/dist/docx/paragraphParser.js +277 -168
- package/dist/docx/paragraphPropertySource.js +5 -1
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +1 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +9 -52
- package/dist/docx/paragraphTraversal.js +7 -4
- package/dist/docx/parseWarningMessage.js +2 -1
- package/dist/docx/parser.js +2 -2
- package/dist/docx/preservedRunContent.d.ts +32 -0
- package/dist/docx/preservedRunContent.js +86 -0
- package/dist/docx/previewBudget.d.ts +64 -0
- package/dist/docx/previewBudget.js +88 -0
- package/dist/docx/renderedPageBreakNormalization.js +2 -4
- package/dist/docx/revisionIdNormalization.js +17 -5
- package/dist/docx/rezip.js +91 -36
- package/dist/docx/runParser.d.ts +1 -1
- package/dist/docx/runParser.js +16 -12
- package/dist/docx/sdtPropertiesPatch.js +24 -18
- package/dist/docx/sectionParser.js +8 -1
- package/dist/docx/sectionReferenceHistory.js +2 -2
- package/dist/docx/selectiveSave.js +6 -6
- package/dist/docx/selectiveXmlPatch.d.ts +46 -2
- package/dist/docx/selectiveXmlPatch.js +86 -39
- package/dist/docx/serializer/blockSdtSerializer.js +38 -26
- package/dist/docx/serializer/borderSerializer.d.ts +1 -1
- package/dist/docx/serializer/borderSerializer.js +15 -14
- package/dist/docx/serializer/commentSerializer.d.ts +41 -16
- package/dist/docx/serializer/commentSerializer.js +87 -92
- package/dist/docx/serializer/documentSerializer.js +8 -11
- package/dist/docx/serializer/fontTableSerializer.js +6 -6
- package/dist/docx/serializer/headerFooterSerializer.js +12 -14
- package/dist/docx/serializer/markupRangeAttributes.js +2 -2
- package/dist/docx/serializer/noteSerializer.js +8 -8
- package/dist/docx/serializer/numberingSerializer.js +7 -6
- package/dist/docx/serializer/paragraphSerializer.js +93 -72
- package/dist/docx/serializer/partNamespaces.js +2 -2
- package/dist/docx/serializer/runSerializer.js +73 -44
- package/dist/docx/serializer/sectionPropertiesSerializer.js +19 -14
- package/dist/docx/serializer/settingsSerializer.js +4 -3
- package/dist/docx/serializer/stylesSerializer.js +6 -6
- package/dist/docx/serializer/tableSerializer.js +28 -17
- 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/createBilingualDocument.js +3 -1
- package/dist/docx/server/materializeYjsDocx.d.ts +1 -1
- package/dist/docx/server/materializeYjsDocx.js +9 -1
- package/dist/docx/server/migrateYjsAttrSchema.d.ts +55 -0
- package/dist/docx/server/migrateYjsAttrSchema.js +95 -0
- package/dist/docx/server/validateDocxConformance.js +22 -1
- package/dist/docx/shapeParser.js +12 -10
- package/dist/docx/tableParser.d.ts +1 -1
- package/dist/docx/tableParser.js +206 -85
- package/dist/docx/textBoxParser.d.ts +25 -2
- package/dist/docx/textBoxParser.js +59 -4
- package/dist/docx/unzip.d.ts +23 -0
- package/dist/docx/unzip.js +37 -26
- package/dist/docx/verbatimCapture.js +1 -1
- package/dist/docx/vmlImageParser.d.ts +17 -1
- package/dist/docx/vmlImageParser.js +20 -3
- package/dist/docx/vmlPreview.d.ts +1 -3
- package/dist/docx/vmlPreview.js +2 -30
- package/dist/docx/xmlEncoding.d.ts +5 -0
- package/dist/docx/xmlEncoding.js +12 -0
- package/dist/docx/xmlParser.d.ts +26 -2
- package/dist/docx/xmlParser.js +70 -27
- package/dist/docx/xmlResourceLimits.d.ts +89 -9
- package/dist/docx/xmlResourceLimits.js +105 -24
- package/dist/headless-layout.js +10 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +5 -2
- package/dist/internal/paragraphFormattingSerialization.js +3 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +102 -12
- package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
- package/dist/layout-engine/measure/tableCellFloating.js +2 -0
- package/dist/layout-engine/types.d.ts +23 -1
- package/dist/layout-painter/renderImage.d.ts +5 -1
- package/dist/layout-painter/renderImage.js +14 -6
- package/dist/layout-painter/renderPage.d.ts +2 -0
- package/dist/layout-painter/renderPage.js +4 -0
- package/dist/layout-painter/renderParagraph.d.ts +13 -1
- package/dist/layout-painter/renderParagraph.js +26 -6
- package/dist/managers/autoSaveCodec.d.ts +9 -1
- package/dist/managers/autoSaveCodec.js +14 -12
- package/dist/markdown/images.js +1 -4
- package/dist/markdown/renderBlock.js +1 -0
- package/dist/markdown/renderRuns.d.ts +1 -1
- package/dist/markdown/renderRuns.js +28 -5
- package/dist/markdown/renderTable.js +22 -5
- package/dist/markdown/trailers.js +4 -1
- package/dist/pdf/images.js +142 -6
- package/dist/prosemirror/attrs/index.d.ts +14 -3
- package/dist/prosemirror/attrs/index.js +224 -2
- package/dist/prosemirror/authoredTransformAttrs.d.ts +28 -0
- package/dist/prosemirror/authoredTransformAttrs.js +64 -0
- package/dist/prosemirror/commands/contentControls.js +10 -2
- package/dist/prosemirror/commands/image.d.ts +9 -2
- package/dist/prosemirror/commands/image.js +43 -28
- package/dist/prosemirror/commands/pageBreak.js +1 -1
- package/dist/prosemirror/commentReferenceAttrs.d.ts +8 -0
- package/dist/prosemirror/commentReferenceAttrs.js +33 -0
- package/dist/prosemirror/commentReferenceIntegrity.d.ts +47 -0
- package/dist/prosemirror/commentReferenceIntegrity.js +43 -0
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
- package/dist/prosemirror/conversion/fromProseDoc.js +338 -99
- package/dist/prosemirror/conversion/index.d.ts +2 -2
- package/dist/prosemirror/conversion/toProseDoc.d.ts +7 -9
- package/dist/prosemirror/conversion/toProseDoc.js +551 -327
- package/dist/prosemirror/extensions/StarterKit.js +8 -58
- package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -1
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +45 -27
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +3 -1
- package/dist/prosemirror/extensions/features/ImagePasteExtension.js +5 -3
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +1 -0
- package/dist/prosemirror/extensions/markRegistry.d.ts +41 -0
- package/dist/prosemirror/extensions/markRegistry.js +75 -0
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +2 -3
- package/dist/prosemirror/extensions/marks/InlineWrapperExtension.d.ts +18 -0
- package/dist/prosemirror/extensions/marks/InlineWrapperExtension.js +63 -0
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +2 -1
- package/dist/prosemirror/extensions/marks/markUtils.js +47 -4
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.d.ts +11 -0
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +87 -0
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +9 -7
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +20 -0
- package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.d.ts +32 -0
- package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +67 -0
- package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.d.ts +16 -0
- package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +60 -0
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +10 -2
- package/dist/prosemirror/extensions/nodes/TableExtension.js +3 -2
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +13 -5
- package/dist/prosemirror/inlineWrapperStack.d.ts +39 -0
- package/dist/prosemirror/inlineWrapperStack.js +75 -0
- package/dist/prosemirror/pageBreakRunProjection.d.ts +17 -7
- package/dist/prosemirror/pageBreakRunProjection.js +19 -9
- package/dist/prosemirror/paragraphFormattingProvenance.d.ts +6 -3
- package/dist/prosemirror/paragraphFormattingProvenance.js +12 -3
- package/dist/prosemirror/replacedAnnotations.d.ts +59 -0
- package/dist/prosemirror/replacedAnnotations.js +165 -0
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +3 -1
- package/dist/prosemirror/runFormattingInlineCarriers.js +5 -1
- package/dist/prosemirror/schema/index.d.ts +3 -3
- package/dist/prosemirror/schema/marks.d.ts +25 -1
- package/dist/prosemirror/schema/nodes.d.ts +139 -3
- package/dist/prosemirror/schema/nodes.js +16 -1
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +2 -0
- package/dist/prosemirror/trackedRunInlineAtoms.js +2 -0
- package/dist/prosemirror/validation.js +16 -2
- package/dist/prosemirror/yjsDocumentMetadata.d.ts +73 -0
- package/dist/prosemirror/yjsDocumentMetadata.js +171 -0
- package/dist/prosemirror/zeroWidthAnchors.js +2 -0
- package/dist/render-dom/commentAnchorAttributes.d.ts +37 -0
- package/dist/render-dom/commentAnchorAttributes.js +54 -0
- package/dist/server.d.ts +3 -1
- package/dist/server.js +3 -1
- 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.d.ts +9 -1
- package/dist/utils/clipboard.js +29 -2
- package/dist/utils/findReplace.js +1 -1
- package/dist/utils/imageLuminance.d.ts +15 -0
- package/dist/utils/imageLuminance.js +32 -0
- package/dist/utils/mergeDocumentContent.js +7 -17
- package/dist/utils/replaceText.js +1 -0
- 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/blockRangeMarkers.d.ts +0 -36
- package/dist/docx/blockRangeMarkers.js +0 -59
- package/dist/prosemirror/yjsParagraphSourceContract.d.ts +0 -9
- package/dist/prosemirror/yjsParagraphSourceContract.js +0 -26
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { modelParagraphFormattingEmission } from "../../internal/paragraphFormattingSerialization.js";
|
|
2
|
+
import { serializePreservedAttributes } from "../attributeRemainder.js";
|
|
3
|
+
import { fieldStateAttributes } from "../fieldState.js";
|
|
2
4
|
import { getParagraphPropertySource, paragraphPropertySourceMatchesEmission } from "../paragraphPropertySource.js";
|
|
3
5
|
import { reconcileRawSdtPr } from "../sdtPropertiesPatch.js";
|
|
4
6
|
import { DATE_UTC_ATTRIBUTE } from "../trackedChangeInfo.js";
|
|
@@ -10,8 +12,9 @@ import { serializeRun } from "./runSerializer.js";
|
|
|
10
12
|
import { serializeSectionProperties } from "./sectionPropertiesSerializer.js";
|
|
11
13
|
import { serializeTextFormatting } from "./textFormattingSerializer.js";
|
|
12
14
|
import { serializeTrackedChangeAttributes, trackedChangeAttributeRecord } from "./trackedChangeAttributes.js";
|
|
13
|
-
import {
|
|
15
|
+
import { isSingleWellFormedElement } from "./xmlUtils.js";
|
|
14
16
|
import { panic } from "better-result";
|
|
17
|
+
import { escapeXmlAttribute, escapeXmlText } from "@stll/docx-core";
|
|
15
18
|
import { PARAGRAPH_MARK_CHANGE_KINDS } from "@stll/docx-core/model";
|
|
16
19
|
//#region src/docx/serializer/paragraphSerializer.ts
|
|
17
20
|
/**
|
|
@@ -331,25 +334,26 @@ function serializeParagraphPropertyChange(change) {
|
|
|
331
334
|
/** The attribute list of a `w:hyperlink`, without its children. */
|
|
332
335
|
function hyperlinkAttributes(hyperlink) {
|
|
333
336
|
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="${
|
|
337
|
+
if (hyperlink.rId) attrs.push(`r:id="${escapeXmlAttribute(hyperlink.rId)}"`);
|
|
338
|
+
if (hyperlink.anchor) attrs.push(`w:anchor="${escapeXmlAttribute(hyperlink.anchor)}"`);
|
|
339
|
+
if (hyperlink.tooltip) attrs.push(`w:tooltip="${escapeXmlAttribute(hyperlink.tooltip)}"`);
|
|
340
|
+
if (hyperlink.target) attrs.push(`w:tgtFrame="${escapeXmlAttribute(hyperlink.target)}"`);
|
|
338
341
|
if (hyperlink.history === true) attrs.push("w:history=\"1\"");
|
|
339
342
|
else if (hyperlink.history === false) attrs.push("w:history=\"0\"");
|
|
340
|
-
if (hyperlink.docLocation) attrs.push(`w:docLocation="${
|
|
343
|
+
if (hyperlink.docLocation) attrs.push(`w:docLocation="${escapeXmlAttribute(hyperlink.docLocation)}"`);
|
|
341
344
|
return attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
|
|
342
345
|
}
|
|
343
346
|
/** One `w:hyperlink` child, with the caller deciding how a run is written. */
|
|
344
347
|
function serializeHyperlinkChild(child, serializeChildRun) {
|
|
345
348
|
if (child.type === "run") return serializeChildRun(child);
|
|
346
|
-
|
|
349
|
+
if (child.type === "bookmarkStart") return serializeBookmarkStart(child);
|
|
350
|
+
return child.type === "bookmarkEnd" ? serializeBookmarkEnd(child) : child.xml;
|
|
347
351
|
}
|
|
348
352
|
/**
|
|
349
353
|
* Serialize a hyperlink (w:hyperlink)
|
|
350
354
|
*/
|
|
351
|
-
function serializeHyperlink(hyperlink) {
|
|
352
|
-
const childrenXml = hyperlink.children.map((child) => serializeHyperlinkChild(child,
|
|
355
|
+
function serializeHyperlink(hyperlink, disposition = "kept") {
|
|
356
|
+
const childrenXml = hyperlink.children.map((child) => serializeHyperlinkChild(child, (run) => serializeInlineRun(run, disposition))).join("");
|
|
353
357
|
return `<w:hyperlink${hyperlinkAttributes(hyperlink)}>${childrenXml}</w:hyperlink>`;
|
|
354
358
|
}
|
|
355
359
|
/**
|
|
@@ -366,10 +370,11 @@ function serializeBookmarkEnd(bookmark) {
|
|
|
366
370
|
}
|
|
367
371
|
/** Serialize a simple field without changing its authored OOXML field form. */
|
|
368
372
|
function serializeSimpleField(field) {
|
|
369
|
-
const attrs = [`w:instr="${
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
+
const attrs = [`w:instr="${escapeXmlAttribute(field.instruction)}"`, ...fieldStateAttributes(field)];
|
|
374
|
+
const contentXml = field.content.map((item) => {
|
|
375
|
+
if (item.type === "run") return serializeRun(item);
|
|
376
|
+
return item.type === "hyperlink" ? serializeHyperlink(item) : item.xml;
|
|
377
|
+
}).join("");
|
|
373
378
|
return `<w:fldSimple ${attrs.join(" ")}>${contentXml}</w:fldSimple>`;
|
|
374
379
|
}
|
|
375
380
|
/**
|
|
@@ -381,14 +386,12 @@ function serializeComplexField(field) {
|
|
|
381
386
|
const parts = [];
|
|
382
387
|
const structuralFormatting = field.formatting ?? field.fieldResult[0]?.formatting;
|
|
383
388
|
const rPrXml = structuralFormatting ? serializeTextFormatting(structuralFormatting) : "";
|
|
384
|
-
const beginAttrs = ["w:fldCharType=\"begin\""];
|
|
385
|
-
if (field.fldLock) beginAttrs.push("w:fldLock=\"true\"");
|
|
386
|
-
if (field.dirty) beginAttrs.push("w:dirty=\"true\"");
|
|
389
|
+
const beginAttrs = ["w:fldCharType=\"begin\"", ...fieldStateAttributes(field)];
|
|
387
390
|
parts.push(`<w:r>${rPrXml}<w:fldChar ${beginAttrs.join(" ")}/></w:r>`);
|
|
388
391
|
if (field.fieldCode.length > 0) parts.push(...field.fieldCode.map((run) => serializeRun(run)));
|
|
389
392
|
else if (field.instruction.length > 0) {
|
|
390
393
|
const spaceAttr = field.instruction.startsWith(" ") || field.instruction.endsWith(" ") || field.instruction.includes(" ") ? " xml:space=\"preserve\"" : "";
|
|
391
|
-
parts.push(`<w:r>${rPrXml}<w:instrText${spaceAttr}>${
|
|
394
|
+
parts.push(`<w:r>${rPrXml}<w:instrText${spaceAttr}>${escapeXmlText(field.instruction)}</w:instrText></w:r>`);
|
|
392
395
|
}
|
|
393
396
|
parts.push(`<w:r>${rPrXml}<w:fldChar w:fldCharType="separate"/></w:r>`);
|
|
394
397
|
parts.push(...field.fieldResult.map((run) => serializeRun(run)));
|
|
@@ -406,29 +409,29 @@ function serializeComplexField(field) {
|
|
|
406
409
|
function synthesizeInlineSdtPr(props) {
|
|
407
410
|
const prParts = [];
|
|
408
411
|
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="${
|
|
412
|
+
if (props.alias) prParts.push(`<w:alias w:val="${escapeXmlAttribute(props.alias)}"/>`);
|
|
413
|
+
if (props.tag) prParts.push(`<w:tag w:val="${escapeXmlAttribute(props.tag)}"/>`);
|
|
411
414
|
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="${
|
|
415
|
+
if (props.placeholder) prParts.push(`<w:placeholder><w:docPart w:val="${escapeXmlAttribute(props.placeholder)}"/></w:placeholder>`);
|
|
413
416
|
if (props.showingPlaceholder) prParts.push("<w:showingPlcHdr/>");
|
|
414
417
|
switch (props.sdtType) {
|
|
415
418
|
case "plainText":
|
|
416
419
|
prParts.push("<w:text/>");
|
|
417
420
|
break;
|
|
418
421
|
case "date": {
|
|
419
|
-
const fullDateAttr = props.dateValueISO ? ` w:fullDate="${
|
|
420
|
-
const formatChild = props.dateFormat ? `<w:dateFormat w:val="${
|
|
422
|
+
const fullDateAttr = props.dateValueISO ? ` w:fullDate="${escapeXmlAttribute(props.dateValueISO)}"` : "";
|
|
423
|
+
const formatChild = props.dateFormat ? `<w:dateFormat w:val="${escapeXmlAttribute(props.dateFormat)}"/>` : "";
|
|
421
424
|
if (fullDateAttr || formatChild) prParts.push(`<w:date${fullDateAttr}>${formatChild}</w:date>`);
|
|
422
425
|
else prParts.push("<w:date/>");
|
|
423
426
|
break;
|
|
424
427
|
}
|
|
425
428
|
case "dropdown": {
|
|
426
|
-
const items = (props.listItems ?? []).map((i) => `<w:listItem w:displayText="${
|
|
429
|
+
const items = (props.listItems ?? []).map((i) => `<w:listItem w:displayText="${escapeXmlAttribute(i.displayText)}" w:value="${escapeXmlAttribute(i.value)}"/>`).join("");
|
|
427
430
|
prParts.push(`<w:dropDownList>${items}</w:dropDownList>`);
|
|
428
431
|
break;
|
|
429
432
|
}
|
|
430
433
|
case "comboBox": {
|
|
431
|
-
const items = (props.listItems ?? []).map((i) => `<w:listItem w:displayText="${
|
|
434
|
+
const items = (props.listItems ?? []).map((i) => `<w:listItem w:displayText="${escapeXmlAttribute(i.displayText)}" w:value="${escapeXmlAttribute(i.value)}"/>`).join("");
|
|
432
435
|
prParts.push(`<w:comboBox>${items}</w:comboBox>`);
|
|
433
436
|
break;
|
|
434
437
|
}
|
|
@@ -454,20 +457,22 @@ function synthesizeInlineSdtPr(props) {
|
|
|
454
457
|
* the properties block was re-synthesized from the modeled projection alone,
|
|
455
458
|
* silently dropping every unmodeled feature (and `w:sdtEndPr`) on save.
|
|
456
459
|
*/
|
|
457
|
-
function serializeInlineSdt(sdt) {
|
|
460
|
+
function serializeInlineSdt(sdt, disposition = "kept") {
|
|
458
461
|
const props = sdt.properties;
|
|
459
462
|
const contentXml = sdt.content.map((item) => {
|
|
460
463
|
switch (item.type) {
|
|
461
|
-
case "run": return
|
|
462
|
-
case "hyperlink": return serializeHyperlink(item);
|
|
464
|
+
case "run": return serializeInlineRun(item, disposition);
|
|
465
|
+
case "hyperlink": return serializeHyperlink(item, disposition);
|
|
463
466
|
case "simpleField": return serializeSimpleField(item);
|
|
464
467
|
case "complexField": return serializeComplexField(item);
|
|
465
|
-
case "inlineSdt": return serializeInlineSdt(item);
|
|
468
|
+
case "inlineSdt": return serializeInlineSdt(item, disposition);
|
|
469
|
+
case "inlineWrapper": return serializeParagraphContent(item, disposition);
|
|
466
470
|
case "insertion": return serializeTrackedChange("ins", item);
|
|
467
471
|
case "deletion": return serializeTrackedChange("del", item);
|
|
468
472
|
case "moveFrom": return serializeTrackedChange("moveFrom", item);
|
|
469
473
|
case "moveTo": return serializeTrackedChange("moveTo", item);
|
|
470
474
|
case "mathEquation": return item.ommlXml || "";
|
|
475
|
+
case "preservedInline": return item.xml;
|
|
471
476
|
default: return item;
|
|
472
477
|
}
|
|
473
478
|
}).join("");
|
|
@@ -488,6 +493,22 @@ function serializeMoveRangeStart(tag, marker) {
|
|
|
488
493
|
function rewriteRunTextAsDeleted(xml) {
|
|
489
494
|
return xml.replace(/<w:t\b/gu, "<w:delText").replace(/<\/w:t>/gu, "</w:delText>").replace(/<w:instrText\b/gu, "<w:delInstrText").replace(/<\/w:instrText>/gu, "</w:delInstrText>");
|
|
490
495
|
}
|
|
496
|
+
function serializeDeletedRun(run) {
|
|
497
|
+
const xml = serializeRun(run);
|
|
498
|
+
if (!run.content.some((c) => c.type === "drawing" || c.type === "shape")) return rewriteRunTextAsDeleted(xml);
|
|
499
|
+
if (!run.content.some((c) => c.type !== "drawing" && c.type !== "shape")) return xml;
|
|
500
|
+
return run.content.map((content) => {
|
|
501
|
+
const contentXml = serializeRun({
|
|
502
|
+
...run,
|
|
503
|
+
content: [content]
|
|
504
|
+
});
|
|
505
|
+
if (content.type === "drawing" || content.type === "shape") return contentXml;
|
|
506
|
+
return rewriteRunTextAsDeleted(contentXml);
|
|
507
|
+
}).join("");
|
|
508
|
+
}
|
|
509
|
+
function serializeInlineRun(run, disposition) {
|
|
510
|
+
return disposition === "removed" ? serializeDeletedRun(run) : serializeRun(run);
|
|
511
|
+
}
|
|
491
512
|
function trackedChangeTag(change) {
|
|
492
513
|
switch (change.type) {
|
|
493
514
|
case "insertion": return "ins";
|
|
@@ -498,29 +519,28 @@ function trackedChangeTag(change) {
|
|
|
498
519
|
}
|
|
499
520
|
function serializeTrackedChange(tag, change) {
|
|
500
521
|
const attrs = serializeTrackedChangeAttributes(change.info);
|
|
501
|
-
const
|
|
502
|
-
|
|
503
|
-
if (!run.content.some((c) => c.type === "drawing" || c.type === "shape")) return rewriteRunTextAsDeleted(xml);
|
|
504
|
-
if (!run.content.some((c) => c.type !== "drawing" && c.type !== "shape")) return xml;
|
|
505
|
-
return run.content.map((content) => {
|
|
506
|
-
const contentXml = serializeRun({
|
|
507
|
-
...run,
|
|
508
|
-
content: [content]
|
|
509
|
-
});
|
|
510
|
-
if (content.type === "drawing" || content.type === "shape") return contentXml;
|
|
511
|
-
return rewriteRunTextAsDeleted(contentXml);
|
|
512
|
-
}).join("");
|
|
513
|
-
};
|
|
514
|
-
const serializeContentRun = (run) => tag === "del" || tag === "moveFrom" ? serializeDeletedRun(run) : serializeRun(run);
|
|
522
|
+
const disposition = tag === "del" || tag === "moveFrom" ? "removed" : "kept";
|
|
523
|
+
const serializeContentRun = (run) => serializeInlineRun(run, disposition);
|
|
515
524
|
const serializeWrappedItem = (item) => {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
525
|
+
switch (item.type) {
|
|
526
|
+
case "run": return serializeContentRun(item);
|
|
527
|
+
case "simpleField":
|
|
528
|
+
case "complexField": {
|
|
529
|
+
const xml = item.type === "simpleField" ? serializeSimpleField(item) : serializeComplexField(item);
|
|
530
|
+
return disposition === "removed" ? rewriteRunTextAsDeleted(xml) : xml;
|
|
531
|
+
}
|
|
532
|
+
case "mathEquation": return item.ommlXml;
|
|
533
|
+
case "insertion":
|
|
534
|
+
case "deletion":
|
|
535
|
+
case "moveFrom":
|
|
536
|
+
case "moveTo": return serializeTrackedChange(trackedChangeTag(item), item);
|
|
537
|
+
case "bookmarkStart": return serializeBookmarkStart(item);
|
|
538
|
+
case "bookmarkEnd": return serializeBookmarkEnd(item);
|
|
539
|
+
case "preservedInline": return item.xml;
|
|
540
|
+
case "inlineWrapper":
|
|
541
|
+
case "inlineSdt": return serializeParagraphContent(item, disposition);
|
|
542
|
+
default: return item;
|
|
520
543
|
}
|
|
521
|
-
if (item.type === "mathEquation") return item.ommlXml;
|
|
522
|
-
if (item.type === "insertion" || item.type === "deletion" || item.type === "moveFrom" || item.type === "moveTo") return serializeTrackedChange(trackedChangeTag(item), item);
|
|
523
|
-
return item.type === "bookmarkStart" ? serializeBookmarkStart(item) : serializeBookmarkEnd(item);
|
|
524
544
|
};
|
|
525
545
|
const open = `<w:${tag} ${attrs}>`;
|
|
526
546
|
const close = `</w:${tag}>`;
|
|
@@ -553,27 +573,22 @@ function serializeCommentReferenceRun(id) {
|
|
|
553
573
|
/**
|
|
554
574
|
* Serialize a single paragraph content item.
|
|
555
575
|
*
|
|
556
|
-
* `
|
|
557
|
-
*
|
|
558
|
-
*
|
|
559
|
-
*
|
|
560
|
-
* editor (fromProseDoc) path emits range markers with no reference node, so the
|
|
561
|
-
* synthetic run is still written when the id is absent from the set.
|
|
576
|
+
* A `commentRangeEnd` writes an end and nothing else. Where the comment's
|
|
577
|
+
* reference run goes is authored data the model carries as its own item, and
|
|
578
|
+
* `completeCommentReferences` fills it in for a model that arrived without
|
|
579
|
+
* one; a guess made here from one paragraph cannot see the rest of the story.
|
|
562
580
|
*/
|
|
563
|
-
function serializeParagraphContent(content,
|
|
581
|
+
function serializeParagraphContent(content, disposition = "kept") {
|
|
564
582
|
switch (content.type) {
|
|
565
|
-
case "run": return
|
|
566
|
-
case "hyperlink": return serializeHyperlink(content);
|
|
583
|
+
case "run": return serializeInlineRun(content, disposition);
|
|
584
|
+
case "hyperlink": return serializeHyperlink(content, disposition);
|
|
567
585
|
case "bookmarkStart": return serializeBookmarkStart(content);
|
|
568
586
|
case "bookmarkEnd": return serializeBookmarkEnd(content);
|
|
569
587
|
case "simpleField": return serializeSimpleField(content);
|
|
570
588
|
case "complexField": return serializeComplexField(content);
|
|
571
|
-
case "inlineSdt": return serializeInlineSdt(content);
|
|
589
|
+
case "inlineSdt": return serializeInlineSdt(content, disposition);
|
|
572
590
|
case "commentRangeStart": return `<w:commentRangeStart ${markupRangeAttributes(content).join(" ")}/>`;
|
|
573
|
-
case "commentRangeEnd": {
|
|
574
|
-
const end = `<w:commentRangeEnd ${markupRangeAttributes(content).join(" ")}/>`;
|
|
575
|
-
return explicitCommentReferenceIds.has(content.id) ? end : `${end}${serializeCommentReferenceRun(content.id)}`;
|
|
576
|
-
}
|
|
591
|
+
case "commentRangeEnd": return `<w:commentRangeEnd ${markupRangeAttributes(content).join(" ")}/>`;
|
|
577
592
|
case "commentReference": return serializeCommentReferenceRun(content.id);
|
|
578
593
|
case "insertion": return serializeTrackedChange("ins", content);
|
|
579
594
|
case "deletion": return serializeTrackedChange("del", content);
|
|
@@ -583,11 +598,18 @@ function serializeParagraphContent(content, explicitCommentReferenceIds) {
|
|
|
583
598
|
case "moveFromRangeEnd": return `<w:moveFromRangeEnd ${markupRangeAttributes(content).join(" ")}/>`;
|
|
584
599
|
case "moveToRangeStart": return serializeMoveRangeStart("moveToRangeStart", content);
|
|
585
600
|
case "moveToRangeEnd": return `<w:moveToRangeEnd ${markupRangeAttributes(content).join(" ")}/>`;
|
|
586
|
-
case "
|
|
587
|
-
const
|
|
588
|
-
|
|
601
|
+
case "inlineWrapper": {
|
|
602
|
+
const inner = content.content.map((child) => serializeParagraphContent(child, disposition)).join("");
|
|
603
|
+
switch (content.kind) {
|
|
604
|
+
case "bidi": {
|
|
605
|
+
const tag = content.control === "override" ? "bdo" : "dir";
|
|
606
|
+
return `<w:${tag}${content.direction === void 0 ? "" : ` w:val="${escapeXmlAttribute(content.direction)}"`}>${inner}</w:${tag}>`;
|
|
607
|
+
}
|
|
608
|
+
default: return content.kind;
|
|
609
|
+
}
|
|
589
610
|
}
|
|
590
611
|
case "mathEquation": return content.ommlXml || "";
|
|
612
|
+
case "preservedInline": return content.xml;
|
|
591
613
|
default: return "";
|
|
592
614
|
}
|
|
593
615
|
}
|
|
@@ -600,21 +622,20 @@ function serializeParagraphContent(content, explicitCommentReferenceIds) {
|
|
|
600
622
|
function serializeParagraph(paragraph) {
|
|
601
623
|
const parts = [];
|
|
602
624
|
const attrs = [];
|
|
603
|
-
if (paragraph.paraId) attrs.push(`w14:paraId="${
|
|
604
|
-
if (paragraph.textId) attrs.push(`w14:textId="${
|
|
625
|
+
if (paragraph.paraId) attrs.push(`w14:paraId="${escapeXmlAttribute(paragraph.paraId)}"`);
|
|
626
|
+
if (paragraph.textId) attrs.push(`w14:textId="${escapeXmlAttribute(paragraph.textId)}"`);
|
|
605
627
|
if (paragraph.reviewCarrier) attrs.push(`folio:reviewCarrier="${paragraph.reviewCarrier}"`);
|
|
606
|
-
const
|
|
628
|
+
const written = serializePreservedAttributes(attrs, paragraph.preservedAttributes);
|
|
629
|
+
const attrsStr = written.length > 0 ? ` ${written.join(" ")}` : "";
|
|
607
630
|
parts.push(serializeParagraphFormattingWithOptions(paragraph.formatting, {
|
|
608
631
|
propertyChanges: paragraph.propertyChanges,
|
|
609
632
|
paragraphMarkChange: paragraph.pPrMark,
|
|
610
633
|
propertySource: getParagraphPropertySource(paragraph),
|
|
611
634
|
sectionProperties: paragraph.sectionProperties
|
|
612
635
|
}));
|
|
613
|
-
const explicitCommentReferenceIds = /* @__PURE__ */ new Set();
|
|
614
|
-
for (const content of paragraph.content) if (content.type === "commentReference") explicitCommentReferenceIds.add(content.id);
|
|
615
636
|
let pendingRenderedPageBreak = paragraph.renderedPageBreakBefore === true;
|
|
616
637
|
for (const content of paragraph.content) {
|
|
617
|
-
let contentXml = serializeParagraphContent(content
|
|
638
|
+
let contentXml = serializeParagraphContent(content);
|
|
618
639
|
if (contentXml) {
|
|
619
640
|
if (pendingRenderedPageBreak) {
|
|
620
641
|
const next = injectRenderedPageBreakIntoFirstRun(contentXml);
|
|
@@ -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,19 @@
|
|
|
1
1
|
import { isValidHexColor } from "../../utils/colorResolver.js";
|
|
2
|
+
import { normalizeImageLuminancePercent } from "../../utils/imageLuminance.js";
|
|
3
|
+
import { serializePreservedAttributes } from "../attributeRemainder.js";
|
|
2
4
|
import { THEME_COLOR_TO_DRAWING_SCHEME } from "../drawingUtils.js";
|
|
5
|
+
import { fieldStateAttributes } from "../fieldState.js";
|
|
3
6
|
import { serializeGraphicFrameLocks } from "../graphicFrameLocks.js";
|
|
7
|
+
import { DECORATIVE_EXTENSION_URI, DECORATIVE_NAMESPACE } from "../imageParser.js";
|
|
4
8
|
import { canReplayEditableImageRawXml } from "../imageRawXml.js";
|
|
9
|
+
import { serializeNonVisualDrawingNames } from "../nonVisualDrawingProps.js";
|
|
5
10
|
import { serializeParagraph } from "./paragraphSerializer.js";
|
|
6
11
|
import { serializeTable } from "./tableSerializer.js";
|
|
7
12
|
import { serializeTextFormatting } from "./textFormattingSerializer.js";
|
|
8
13
|
import { getSingularRunPropertyChange, serializeTrackedChangeAttributes } from "./trackedChangeAttributes.js";
|
|
9
|
-
import {
|
|
14
|
+
import { intAttr } from "./xmlUtils.js";
|
|
10
15
|
import { panic } from "better-result";
|
|
11
|
-
import { requiresXmlSpacePreserve } from "@stll/docx-core";
|
|
16
|
+
import { escapeXmlAttribute, escapeXmlText, requiresXmlSpacePreserve } from "@stll/docx-core";
|
|
12
17
|
//#region src/docx/serializer/runSerializer.ts
|
|
13
18
|
/**
|
|
14
19
|
* Run Serializer - Serialize runs to OOXML XML
|
|
@@ -59,7 +64,7 @@ function serializeRunProperties(formatting, propertyChanges) {
|
|
|
59
64
|
* Serialize text content (w:t)
|
|
60
65
|
*/
|
|
61
66
|
function serializeTextContent(content) {
|
|
62
|
-
return `<w:t${requiresXmlSpacePreserve(content.text) ? " xml:space=\"preserve\"" : ""}>${
|
|
67
|
+
return `<w:t${requiresXmlSpacePreserve(content.text) ? " xml:space=\"preserve\"" : ""}>${escapeXmlText(content.text)}</w:t>`;
|
|
63
68
|
}
|
|
64
69
|
/**
|
|
65
70
|
* Serialize tab content (w:tab)
|
|
@@ -67,9 +72,9 @@ function serializeTextContent(content) {
|
|
|
67
72
|
function serializeTabContent(content) {
|
|
68
73
|
if (!content.positional) return "<w:tab/>";
|
|
69
74
|
return `<w:ptab${[
|
|
70
|
-
content.positional.relativeTo ? ` w:relativeTo="${
|
|
71
|
-
content.positional.alignment ? ` w:alignment="${
|
|
72
|
-
content.positional.leader ? ` w:leader="${
|
|
75
|
+
content.positional.relativeTo ? ` w:relativeTo="${escapeXmlAttribute(content.positional.relativeTo)}"` : "",
|
|
76
|
+
content.positional.alignment ? ` w:alignment="${escapeXmlAttribute(content.positional.alignment)}"` : "",
|
|
77
|
+
content.positional.leader ? ` w:leader="${escapeXmlAttribute(content.positional.leader)}"` : ""
|
|
73
78
|
].join("")}/>`;
|
|
74
79
|
}
|
|
75
80
|
/**
|
|
@@ -88,7 +93,7 @@ function serializeBreakContent(content) {
|
|
|
88
93
|
* Serialize symbol content (w:sym)
|
|
89
94
|
*/
|
|
90
95
|
function serializeSymbolContent(content) {
|
|
91
|
-
return `<w:sym${content.font === "" ? "" : ` w:font="${
|
|
96
|
+
return `<w:sym${content.font === "" ? "" : ` w:font="${escapeXmlAttribute(content.font)}"`}${content.char === "" ? "" : ` w:char="${escapeXmlAttribute(content.char)}"`}/>`;
|
|
92
97
|
}
|
|
93
98
|
/**
|
|
94
99
|
* Serialize footnote/endnote reference
|
|
@@ -101,16 +106,13 @@ function serializeNoteReference(content) {
|
|
|
101
106
|
* Serialize field character (w:fldChar)
|
|
102
107
|
*/
|
|
103
108
|
function serializeFieldChar(content) {
|
|
104
|
-
|
|
105
|
-
if (content.fldLock) attrs.push("w:fldLock=\"true\"");
|
|
106
|
-
if (content.dirty) attrs.push("w:dirty=\"true\"");
|
|
107
|
-
return `<w:fldChar ${attrs.join(" ")}/>`;
|
|
109
|
+
return `<w:fldChar ${[`w:fldCharType="${content.charType}"`, ...fieldStateAttributes(content)].join(" ")}/>`;
|
|
108
110
|
}
|
|
109
111
|
/**
|
|
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
|
}
|
|
@@ -207,6 +209,17 @@ function serializeWrap(wrap) {
|
|
|
207
209
|
}
|
|
208
210
|
}
|
|
209
211
|
/**
|
|
212
|
+
* `a:xfrm` attributes for an authored transform.
|
|
213
|
+
*
|
|
214
|
+
* Written iff authored: `rot="0"` and `flipH="0"` are OOXML's defaults, so a
|
|
215
|
+
* truthiness guard cannot tell "the author said none" from "the author said
|
|
216
|
+
* nothing", and a save erased an authored zero.
|
|
217
|
+
*/
|
|
218
|
+
const serializeTransformAttrs = (transform) => {
|
|
219
|
+
if (transform === void 0) return "";
|
|
220
|
+
return `${transform.rotation === void 0 ? "" : ` rot="${Math.round(transform.rotation * 6e4)}"`}${transform.flipH === void 0 ? "" : ` flipH="${transform.flipH ? 1 : 0}"`}${transform.flipV === void 0 ? "" : ` flipV="${transform.flipV ? 1 : 0}"`}`;
|
|
221
|
+
};
|
|
222
|
+
/**
|
|
210
223
|
* Build the common a:graphic > pic:pic element for images.
|
|
211
224
|
*
|
|
212
225
|
* Takes the relationship id as a separate argument so the caller has to have
|
|
@@ -216,27 +229,27 @@ function serializeWrap(wrap) {
|
|
|
216
229
|
function serializePicGraphic(image, imageRId, sharedId) {
|
|
217
230
|
const cx = image.size.width;
|
|
218
231
|
const cy = image.size.height;
|
|
219
|
-
const rId =
|
|
232
|
+
const rId = escapeXmlAttribute(imageRId);
|
|
220
233
|
const id = sharedId;
|
|
221
234
|
const name = image.filename || `image${id}`;
|
|
222
|
-
|
|
223
|
-
if (image.transform?.rotation) xfrmAttrs += ` rot="${Math.round(image.transform.rotation * 6e4)}"`;
|
|
224
|
-
if (image.transform?.flipH) xfrmAttrs += " flipH=\"1\"";
|
|
225
|
-
if (image.transform?.flipV) xfrmAttrs += " flipV=\"1\"";
|
|
235
|
+
const xfrmAttrs = serializeTransformAttrs(image.transform);
|
|
226
236
|
const cropAttrs = [];
|
|
227
237
|
if (image.crop?.left) cropAttrs.push(`l="${Math.round(image.crop.left * 1e5)}"`);
|
|
228
238
|
if (image.crop?.top) cropAttrs.push(`t="${Math.round(image.crop.top * 1e5)}"`);
|
|
229
239
|
if (image.crop?.right) cropAttrs.push(`r="${Math.round(image.crop.right * 1e5)}"`);
|
|
230
240
|
if (image.crop?.bottom) cropAttrs.push(`b="${Math.round(image.crop.bottom * 1e5)}"`);
|
|
231
241
|
const srcRectEl = cropAttrs.length > 0 ? `<a:srcRect ${cropAttrs.join(" ")}/>` : "";
|
|
232
|
-
const
|
|
233
|
-
|
|
242
|
+
const luminanceAttrs = [];
|
|
243
|
+
if (image.effects?.brightness !== void 0 && Number.isFinite(image.effects.brightness)) luminanceAttrs.push(`bright="${Math.round(normalizeImageLuminancePercent(image.effects.brightness) * 1e3)}"`);
|
|
244
|
+
if (image.effects?.contrast !== void 0 && Number.isFinite(image.effects.contrast)) luminanceAttrs.push(`contrast="${Math.round(normalizeImageLuminancePercent(image.effects.contrast) * 1e3)}"`);
|
|
245
|
+
const blipChildren = `${luminanceAttrs.length === 0 ? "" : `<a:lum ${luminanceAttrs.join(" ")}/>`}${image.opacity !== void 0 && image.opacity < 1 ? `<a:alphaModFix amt="${Math.round(Math.max(0, image.opacity) * 1e5)}"/>` : ""}`;
|
|
246
|
+
const blipEl = blipChildren ? `<a:blip r:embed="${rId}">${blipChildren}</a:blip>` : `<a:blip r:embed="${rId}"/>`;
|
|
234
247
|
return [
|
|
235
248
|
"<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">",
|
|
236
249
|
"<a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">",
|
|
237
250
|
"<pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">",
|
|
238
251
|
"<pic:nvPicPr>",
|
|
239
|
-
`<pic:cNvPr id="${id}" name="${
|
|
252
|
+
`<pic:cNvPr id="${id}" name="${escapeXmlAttribute(name)}"${image.alt ? ` descr="${escapeXmlAttribute(image.alt)}"` : ""}/>`,
|
|
240
253
|
"<pic:cNvPicPr/>",
|
|
241
254
|
"</pic:nvPicPr>",
|
|
242
255
|
"<pic:blipFill>",
|
|
@@ -270,6 +283,19 @@ function serializeWrapDistanceAttrs(wrap) {
|
|
|
270
283
|
return attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
|
|
271
284
|
}
|
|
272
285
|
/**
|
|
286
|
+
* Serialize the `wp:docPr` extension list.
|
|
287
|
+
*
|
|
288
|
+
* The decorative flag is the one extension folio models; every other `a:ext`
|
|
289
|
+
* the source carried is replayed as it was captured, in its original order,
|
|
290
|
+
* because an extension dropped on save is a fact the document had and lost.
|
|
291
|
+
* The decorative extension goes first: it is the one folio may have added.
|
|
292
|
+
*/
|
|
293
|
+
function serializeDocPrExtensions(image) {
|
|
294
|
+
const preserved = image.docPrExtensions ?? [];
|
|
295
|
+
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("")}`;
|
|
296
|
+
return entries ? `<a:extLst>${entries}</a:extLst>` : "";
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
273
299
|
* Serialize drawing/image content (w:drawing) to full DrawingML XML
|
|
274
300
|
*/
|
|
275
301
|
function serializeDrawingContent(content) {
|
|
@@ -284,14 +310,15 @@ function serializeDrawingContent(content) {
|
|
|
284
310
|
const effB = image.padding?.bottom ?? 0;
|
|
285
311
|
const effectExtentEl = `<wp:effectExtent l="${intAttr(effL)}" t="${intAttr(effT)}" r="${intAttr(effR)}" b="${intAttr(effB)}"/>`;
|
|
286
312
|
const docPrId = getUniqueId(image.id);
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
const
|
|
294
|
-
const
|
|
313
|
+
const docPrNames = serializeNonVisualDrawingNames({
|
|
314
|
+
...image.docPrName !== void 0 ? { name: image.docPrName } : {},
|
|
315
|
+
...image.alt !== void 0 ? { alt: image.alt } : {},
|
|
316
|
+
...image.title !== void 0 ? { title: image.title } : {}
|
|
317
|
+
});
|
|
318
|
+
const hlinkClick = image.hlinkRId ? `<a:hlinkClick r:id="${escapeXmlAttribute(image.hlinkRId)}"/>` : "";
|
|
319
|
+
const docPrAttrs = `id="${docPrId}"${docPrNames}${image.hidden === void 0 ? "" : ` hidden="${image.hidden ? "1" : "0"}"`}`;
|
|
320
|
+
const docPrChildren = `${hlinkClick}${serializeDocPrExtensions(image)}`;
|
|
321
|
+
const docPr = docPrChildren ? `<wp:docPr ${docPrAttrs}>${docPrChildren}</wp:docPr>` : `<wp:docPr ${docPrAttrs}/>`;
|
|
295
322
|
const graphicFramePr = serializeGraphicFrameLocks(image.frameLocks);
|
|
296
323
|
const graphic = image.rId === void 0 ? "" : serializePicGraphic(image, image.rId, docPrId);
|
|
297
324
|
if (!isFloating) return [
|
|
@@ -299,7 +326,7 @@ function serializeDrawingContent(content) {
|
|
|
299
326
|
`<wp:inline${wrapDistanceAttrs}>`,
|
|
300
327
|
`<wp:extent cx="${intAttr(cx)}" cy="${intAttr(cy)}"/>`,
|
|
301
328
|
effectExtentEl,
|
|
302
|
-
|
|
329
|
+
docPr,
|
|
303
330
|
graphicFramePr,
|
|
304
331
|
graphic,
|
|
305
332
|
"</wp:inline>",
|
|
@@ -316,7 +343,7 @@ function serializeDrawingContent(content) {
|
|
|
316
343
|
`<wp:extent cx="${intAttr(cx)}" cy="${intAttr(cy)}"/>`,
|
|
317
344
|
effectExtentEl,
|
|
318
345
|
wrap,
|
|
319
|
-
|
|
346
|
+
docPr,
|
|
320
347
|
graphicFramePr,
|
|
321
348
|
graphic,
|
|
322
349
|
"</wp:anchor>",
|
|
@@ -339,7 +366,7 @@ const serializeTextBodyAnchor = (anchor) => {
|
|
|
339
366
|
};
|
|
340
367
|
function serializeGeometryAdjustments(shape) {
|
|
341
368
|
if (!shape.geometryAdjustments || shape.geometryAdjustments.length === 0) return "<a:avLst/>";
|
|
342
|
-
return `<a:avLst>${shape.geometryAdjustments.map(({ name, formula }) => `<a:gd name="${
|
|
369
|
+
return `<a:avLst>${shape.geometryAdjustments.map(({ name, formula }) => `<a:gd name="${escapeXmlAttribute(name)}" fmla="${escapeXmlAttribute(formula)}"/>`).join("")}</a:avLst>`;
|
|
343
370
|
}
|
|
344
371
|
/**
|
|
345
372
|
* Serialize shape content to full DrawingML XML (wps:wsp inside w:drawing)
|
|
@@ -352,14 +379,14 @@ function serializeShapeContent(content) {
|
|
|
352
379
|
const isFloating = shape.wrap && shape.wrap.type !== "inline";
|
|
353
380
|
const wrapDistances = serializeWrapDistanceAttrs(shape.wrap);
|
|
354
381
|
const docPrId = getUniqueId(shape.id);
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
382
|
+
const docPrNames = serializeNonVisualDrawingNames({
|
|
383
|
+
...shape.name !== void 0 ? { name: shape.name } : {},
|
|
384
|
+
...shape.alt !== void 0 ? { alt: shape.alt } : {},
|
|
385
|
+
...shape.title !== void 0 ? { title: shape.title } : {}
|
|
386
|
+
});
|
|
360
387
|
const spPr = [
|
|
361
388
|
"<wps:spPr>",
|
|
362
|
-
`<a:xfrm${
|
|
389
|
+
`<a:xfrm${serializeTransformAttrs(shape.transform)}>`,
|
|
363
390
|
"<a:off x=\"0\" y=\"0\"/>",
|
|
364
391
|
`<a:ext cx="${intAttr(cx)}" cy="${intAttr(cy)}"/>`,
|
|
365
392
|
"</a:xfrm>",
|
|
@@ -386,7 +413,7 @@ function serializeShapeContent(content) {
|
|
|
386
413
|
if (tb.autoFit === "shape") autoFitXml = "<a:spAutoFit/>";
|
|
387
414
|
else if (tb.autoFit === "normal") autoFitXml = "<a:normAutofit/>";
|
|
388
415
|
else if (tb.autoFit === "none") autoFitXml = "<a:noAutofit/>";
|
|
389
|
-
const bodyPrChildren = `${tb.wordArt?.preset ? `<a:prstTxWarp prst="${
|
|
416
|
+
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
417
|
const bodyPrXml = bodyPrChildren ? `<wps:bodyPr ${bpAttrs.join(" ")}>${bodyPrChildren}</wps:bodyPr>` : `<wps:bodyPr ${bpAttrs.join(" ")}/>`;
|
|
391
418
|
if (isTextBox) textBody = [
|
|
392
419
|
"<wps:txbx><w:txbxContent>",
|
|
@@ -414,7 +441,7 @@ function serializeShapeContent(content) {
|
|
|
414
441
|
`<wp:inline${wrapDistances}>`,
|
|
415
442
|
`<wp:extent cx="${intAttr(cx)}" cy="${intAttr(cy)}"/>`,
|
|
416
443
|
"<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>",
|
|
417
|
-
`<wp:docPr id="${docPrId}"
|
|
444
|
+
`<wp:docPr id="${docPrId}"${docPrNames}/>`,
|
|
418
445
|
"<wp:cNvGraphicFramePr/>",
|
|
419
446
|
graphic,
|
|
420
447
|
"</wp:inline>",
|
|
@@ -432,7 +459,7 @@ function serializeShapeContent(content) {
|
|
|
432
459
|
`<wp:extent cx="${intAttr(cx)}" cy="${intAttr(cy)}"/>`,
|
|
433
460
|
"<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>",
|
|
434
461
|
wrap,
|
|
435
|
-
`<wp:docPr id="${docPrId}"
|
|
462
|
+
`<wp:docPr id="${docPrId}"${docPrNames}/>`,
|
|
436
463
|
"<wp:cNvGraphicFramePr/>",
|
|
437
464
|
graphic,
|
|
438
465
|
"</wp:anchor>",
|
|
@@ -455,6 +482,7 @@ function serializeRunContent(content) {
|
|
|
455
482
|
case "softHyphen": return serializeSoftHyphen(content);
|
|
456
483
|
case "noBreakHyphen": return serializeNoBreakHyphen(content);
|
|
457
484
|
case "renderedPageBreak": return "<w:lastRenderedPageBreak/>";
|
|
485
|
+
case "preservedXml": return content.xml;
|
|
458
486
|
case "drawing":
|
|
459
487
|
if (content.rawXml && canReplayEditableImageRawXml(content)) return content.rawXml;
|
|
460
488
|
return serializeDrawingContent(content);
|
|
@@ -476,7 +504,8 @@ function serializeRun(run) {
|
|
|
476
504
|
const contentXml = serializeRunContent(content);
|
|
477
505
|
if (contentXml) parts.push(contentXml);
|
|
478
506
|
}
|
|
479
|
-
|
|
507
|
+
const attrs = serializePreservedAttributes([], run.preservedAttributes);
|
|
508
|
+
return `<w:r${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""}>${parts.join("")}</w:r>`;
|
|
480
509
|
}
|
|
481
510
|
/**
|
|
482
511
|
* Serialize multiple runs to OOXML XML
|