@stll/folio-core 0.37.3 → 0.37.5

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.
Files changed (33) hide show
  1. package/dist/ai-edits/index.d.ts +2 -1
  2. package/dist/ai-edits/types.d.ts +2 -2
  3. package/dist/compare/content-alignment.js +213 -135
  4. package/dist/compare/content-types.d.ts +3 -1
  5. package/dist/compat/eigenpal.d.ts +2 -2
  6. package/dist/docx/numberingParser.d.ts +1 -13
  7. package/dist/docx/numberingParser.js +1 -10
  8. package/dist/docx/paragraphParser.js +1 -5
  9. package/dist/docx/paragraphPropertySource.d.ts +8 -5
  10. package/dist/docx/paragraphPropertySource.js +16 -4
  11. package/dist/docx/serializer/borderSerializer.d.ts +4 -1
  12. package/dist/docx/serializer/borderSerializer.js +20 -18
  13. package/dist/docx/serializer/commentSerializer.js +1 -1
  14. package/dist/docx/serializer/numberingSerializer.js +1 -1
  15. package/dist/docx/serializer/paragraphSerializer.js +11 -177
  16. package/dist/docx/serializer/runSerializer.d.ts +1 -5
  17. package/dist/docx/serializer/runSerializer.js +2 -129
  18. package/dist/docx/serializer/stylesSerializer.js +1 -1
  19. package/dist/docx/serializer/textFormattingSerializer.d.ts +17 -0
  20. package/dist/docx/serializer/textFormattingSerializer.js +142 -0
  21. package/dist/index.d.ts +2 -2
  22. package/dist/internal/paragraphFormattingSerialization.d.ts +23 -0
  23. package/dist/internal/paragraphFormattingSerialization.js +157 -0
  24. package/dist/prosemirror/attrs/index.js +13 -0
  25. package/dist/prosemirror/conversion/fromProseDoc.js +28 -20
  26. package/dist/prosemirror/conversion/toProseDoc.js +1 -0
  27. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -63
  28. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +9 -0
  29. package/dist/prosemirror/schema/nodes.d.ts +11 -1
  30. package/dist/prosemirror/schema/nodes.js +7 -0
  31. package/dist/server.d.ts +2 -1
  32. package/dist/version-comparison.js +23 -27
  33. package/package.json +1 -1
@@ -1,11 +1,13 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
+ import { ModeledParagraphFormattingEmission } from "../internal/paragraphFormattingSerialization.js";
2
3
  import { Transaction } from "prosemirror-state";
3
4
  import { Fragment, Mark, Node, NodeType } from "prosemirror-model";
4
5
  //#region src/docx/paragraphPropertySource.d.ts
5
- type ParagraphPropertySource = {
6
+ declare const paragraphPropertySourceEmissionFingerprint: unique symbol;
7
+ type ParagraphPropertySource = Readonly<{
6
8
  xml: string;
7
- formattingJson: string;
8
- };
9
+ [paragraphPropertySourceEmissionFingerprint]: string;
10
+ }>;
9
11
  declare const PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR = "_docxParagraphSourceToken";
10
12
  declare const PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR = "_docxParagraphSourceContract";
11
13
  /** Durable identity nested inside opaque collapsed-cell ProseMirror attrs; stripped on restore. */
@@ -26,8 +28,9 @@ declare const isParagraphPropertySourceToken: (token: unknown) => token is strin
26
28
  declare const paragraphPropertySourceTokenMatchesContract: (token: unknown, contract: string) => token is string;
27
29
  /** Visit the v1 provenance scope in canonical OOXML body-story order. */
28
30
  declare const visitDocumentStoryParagraphs: (content: document_d_exports.Document["package"]["document"]["content"], visit: (paragraph: document_d_exports.Paragraph) => void) => void;
29
- declare const assignParagraphPropertySource: (paragraph: document_d_exports.Paragraph, source: ParagraphPropertySource) => void;
31
+ declare const assignParagraphPropertySource: (paragraph: document_d_exports.Paragraph, xml: string) => void;
30
32
  declare const getParagraphPropertySource: (paragraph: document_d_exports.Paragraph) => ParagraphPropertySource | undefined;
33
+ declare const paragraphPropertySourceMatchesEmission: (source: ParagraphPropertySource, emission: ModeledParagraphFormattingEmission) => boolean;
31
34
  /** Copy the captured `w:pPr` without claiming the source paragraph's durable identity. */
32
35
  declare const copyParagraphPropertyCapture: (target: document_d_exports.Paragraph, source: document_d_exports.Paragraph) => void;
33
36
  declare const copyParagraphPropertySource: (target: document_d_exports.Paragraph, source: document_d_exports.Paragraph) => void;
@@ -126,4 +129,4 @@ declare const linkParagraphPropertySourceCandidate: (target: document_d_exports.
126
129
  declare const getParagraphPropertySourceCandidate: (paragraph: document_d_exports.Paragraph) => document_d_exports.Paragraph | undefined;
127
130
  declare const getParagraphPropertySourceTransferId: (paragraph: document_d_exports.Paragraph) => string | undefined;
128
131
  //#endregion
129
- export { DecodedTableCellParagraphSourcePayload, PARAGRAPH_PROPERTY_SOURCE_VALIDATION_CODES, PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR, ParagraphPropertySourceValidationCode, ParagraphPropertySourceValidationError, TABLE_CELL_PARAGRAPH_SOURCE_BINDING_ATTR, TABLE_CELL_PARAGRAPH_SOURCE_PAYLOAD_ERROR_CLASSIFICATIONS, TableCellParagraphPropertySourceBinding, TableCellParagraphSourcePayloadErrorClassification, assignDocumentParagraphPropertySourceContract, assignParagraphPropertySource, cloneDocumentWithParagraphPropertySources, cloneParagraphWithPropertySource, cloneParagraphWithoutPropertySource, cloneTableCellsWithParagraphPropertyCaptures, copyDocumentParagraphPropertySourceContract, copyDocumentParagraphPropertySources, copyParagraphPropertyCapture, copyParagraphPropertySource, createProseParagraphWithPropertySource, decodeTableCellParagraphSourcePayload, getDocumentParagraphPropertySourceContract, getExplicitParagraphPropertySourceTransfers, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceToken, getParagraphPropertySourceTransferId, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, isParagraphPropertySourceToken, joinProseParagraphsWithRightPropertySource, linkParagraphPropertySourceCandidate, linkProseParagraphPropertySource, paragraphPropertySourceBelongsToDocument, paragraphPropertySourceTokenMatchesContract, recreateProseNodeWithDetachedParagraphPropertySource, recreateProseNodeWithParagraphPropertySource, restoreTableCellsWithParagraphPropertySources, setProseParagraphMarkupWithPropertySource, transferProseParagraphPropertySource, transportTableCellsWithParagraphPropertySources, visitDocumentStoryParagraphs, visitTableCellParagraphPropertySourceBindings };
132
+ export { DecodedTableCellParagraphSourcePayload, PARAGRAPH_PROPERTY_SOURCE_VALIDATION_CODES, PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR, ParagraphPropertySourceValidationCode, ParagraphPropertySourceValidationError, TABLE_CELL_PARAGRAPH_SOURCE_BINDING_ATTR, TABLE_CELL_PARAGRAPH_SOURCE_PAYLOAD_ERROR_CLASSIFICATIONS, TableCellParagraphPropertySourceBinding, TableCellParagraphSourcePayloadErrorClassification, assignDocumentParagraphPropertySourceContract, assignParagraphPropertySource, cloneDocumentWithParagraphPropertySources, cloneParagraphWithPropertySource, cloneParagraphWithoutPropertySource, cloneTableCellsWithParagraphPropertyCaptures, copyDocumentParagraphPropertySourceContract, copyDocumentParagraphPropertySources, copyParagraphPropertyCapture, copyParagraphPropertySource, createProseParagraphWithPropertySource, decodeTableCellParagraphSourcePayload, getDocumentParagraphPropertySourceContract, getExplicitParagraphPropertySourceTransfers, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceToken, getParagraphPropertySourceTransferId, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, isParagraphPropertySourceToken, joinProseParagraphsWithRightPropertySource, linkParagraphPropertySourceCandidate, linkProseParagraphPropertySource, paragraphPropertySourceBelongsToDocument, paragraphPropertySourceMatchesEmission, paragraphPropertySourceTokenMatchesContract, recreateProseNodeWithDetachedParagraphPropertySource, recreateProseNodeWithParagraphPropertySource, restoreTableCellsWithParagraphPropertySources, setProseParagraphMarkupWithPropertySource, transferProseParagraphPropertySource, transportTableCellsWithParagraphPropertySources, visitDocumentStoryParagraphs, visitTableCellParagraphPropertySourceBindings };
@@ -1,8 +1,18 @@
1
+ import { modelParagraphFormattingEmission } from "../internal/paragraphFormattingSerialization.js";
2
+ import { canonicalJson } from "../utils/canonicalJson.js";
1
3
  import { visitDocxParagraphs } from "./paragraphTraversal.js";
2
4
  import { TaggedError, panic } from "better-result";
3
5
  import { PluginKey } from "prosemirror-state";
4
6
  //#region src/docx/paragraphPropertySource.ts
7
+ const paragraphPropertySourceEmissionFingerprint = Symbol("paragraphPropertySourceEmissionFingerprint");
5
8
  const paragraphPropertySources = /* @__PURE__ */ new WeakMap();
9
+ const paragraphFormattingEmissionFingerprint = (emission) => canonicalJson(emission);
10
+ const ownedParagraphPropertySource = (paragraph, xml) => {
11
+ return Object.freeze({
12
+ xml,
13
+ [paragraphPropertySourceEmissionFingerprint]: paragraphFormattingEmissionFingerprint(modelParagraphFormattingEmission(paragraph.formatting))
14
+ });
15
+ };
6
16
  const paragraphPropertySourceOwners = /* @__PURE__ */ new WeakMap();
7
17
  const proseParagraphSourceOwners = /* @__PURE__ */ new WeakMap();
8
18
  const paragraphPropertySourceCandidates = /* @__PURE__ */ new WeakMap();
@@ -80,16 +90,18 @@ const paragraphPropertySourceTokenMatchesContract = (token, contract) => {
80
90
  const visitDocumentStoryParagraphs = (content, visit) => {
81
91
  visitDocxParagraphs({ documentBody: { content } }, visit);
82
92
  };
83
- const assignParagraphPropertySource = (paragraph, source) => {
84
- paragraphPropertySources.set(paragraph, source);
93
+ const assignParagraphPropertySource = (paragraph, xml) => {
94
+ paragraphPropertySources.set(paragraph, ownedParagraphPropertySource(paragraph, xml));
85
95
  paragraphPropertySourceOwners.set(paragraph, paragraph);
86
96
  };
87
97
  const getParagraphPropertySource = (paragraph) => paragraphPropertySources.get(paragraph);
98
+ const paragraphPropertySourceMatchesEmission = (source, emission) => source[paragraphPropertySourceEmissionFingerprint] === paragraphFormattingEmissionFingerprint(emission);
88
99
  /** Copy the captured `w:pPr` without claiming the source paragraph's durable identity. */
89
100
  const copyParagraphPropertyCapture = (target, source) => {
90
101
  const propertySource = paragraphPropertySources.get(source);
91
102
  if (propertySource) {
92
- paragraphPropertySources.set(target, { ...propertySource });
103
+ const copiedSource = Object.freeze({ ...propertySource });
104
+ paragraphPropertySources.set(target, copiedSource);
93
105
  paragraphPropertySourceOwners.set(target, paragraphPropertySourceOwners.get(source) ?? source);
94
106
  }
95
107
  };
@@ -676,4 +688,4 @@ const linkParagraphPropertySourceCandidate = (target, source) => {
676
688
  const getParagraphPropertySourceCandidate = (paragraph) => paragraphPropertySourceCandidates.get(paragraph);
677
689
  const getParagraphPropertySourceTransferId = (paragraph) => paragraphPropertySourceTransferIds.get(paragraph);
678
690
  //#endregion
679
- export { PARAGRAPH_PROPERTY_SOURCE_VALIDATION_CODES, PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR, ParagraphPropertySourceValidationError, TABLE_CELL_PARAGRAPH_SOURCE_BINDING_ATTR, TABLE_CELL_PARAGRAPH_SOURCE_PAYLOAD_ERROR_CLASSIFICATIONS, assignDocumentParagraphPropertySourceContract, assignParagraphPropertySource, cloneDocumentWithParagraphPropertySources, cloneParagraphWithPropertySource, cloneParagraphWithoutPropertySource, cloneTableCellsWithParagraphPropertyCaptures, copyDocumentParagraphPropertySourceContract, copyDocumentParagraphPropertySources, copyParagraphPropertyCapture, copyParagraphPropertySource, createProseParagraphWithPropertySource, decodeTableCellParagraphSourcePayload, getDocumentParagraphPropertySourceContract, getExplicitParagraphPropertySourceTransfers, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceToken, getParagraphPropertySourceTransferId, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, isParagraphPropertySourceToken, joinProseParagraphsWithRightPropertySource, linkParagraphPropertySourceCandidate, linkProseParagraphPropertySource, paragraphPropertySourceBelongsToDocument, paragraphPropertySourceTokenMatchesContract, recreateProseNodeWithDetachedParagraphPropertySource, recreateProseNodeWithParagraphPropertySource, restoreTableCellsWithParagraphPropertySources, setProseParagraphMarkupWithPropertySource, transferProseParagraphPropertySource, transportTableCellsWithParagraphPropertySources, visitDocumentStoryParagraphs, visitTableCellParagraphPropertySourceBindings };
691
+ export { PARAGRAPH_PROPERTY_SOURCE_VALIDATION_CODES, PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR, ParagraphPropertySourceValidationError, TABLE_CELL_PARAGRAPH_SOURCE_BINDING_ATTR, TABLE_CELL_PARAGRAPH_SOURCE_PAYLOAD_ERROR_CLASSIFICATIONS, assignDocumentParagraphPropertySourceContract, assignParagraphPropertySource, cloneDocumentWithParagraphPropertySources, cloneParagraphWithPropertySource, cloneParagraphWithoutPropertySource, cloneTableCellsWithParagraphPropertyCaptures, copyDocumentParagraphPropertySourceContract, copyDocumentParagraphPropertySources, copyParagraphPropertyCapture, copyParagraphPropertySource, createProseParagraphWithPropertySource, decodeTableCellParagraphSourcePayload, getDocumentParagraphPropertySourceContract, getExplicitParagraphPropertySourceTransfers, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceToken, getParagraphPropertySourceTransferId, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, isParagraphPropertySourceToken, joinProseParagraphsWithRightPropertySource, linkParagraphPropertySourceCandidate, linkProseParagraphPropertySource, paragraphPropertySourceBelongsToDocument, paragraphPropertySourceMatchesEmission, paragraphPropertySourceTokenMatchesContract, recreateProseNodeWithDetachedParagraphPropertySource, recreateProseNodeWithParagraphPropertySource, restoreTableCellsWithParagraphPropertySources, setProseParagraphMarkupWithPropertySource, transferProseParagraphPropertySource, transportTableCellsWithParagraphPropertySources, visitDocumentStoryParagraphs, visitTableCellParagraphPropertySourceBindings };
@@ -1,5 +1,8 @@
1
1
  import { document_d_exports } from "../../types/document.js";
2
2
  //#region src/docx/serializer/borderSerializer.d.ts
3
+ type ExhaustiveFields<Source, Classified extends keyof Source> = Exclude<keyof Source, Classified> extends never ? Source : never;
4
+ type ClassifiedBorderField = "style" | "color" | "size" | "space" | "shadow" | "frame" | "artRelationshipId" | "topLeftArtRelationshipId" | "topRightArtRelationshipId" | "bottomLeftArtRelationshipId" | "bottomRightArtRelationshipId";
5
+ type ExhaustiveBorder = ExhaustiveFields<document_d_exports.BorderSpec, ClassifiedBorderField>;
3
6
  /**
4
7
  * Serialize a single border element (`<w:top .../>`, `<w:left .../>`, ...).
5
8
  *
@@ -19,6 +22,6 @@ import { document_d_exports } from "../../types/document.js";
19
22
  * untrusted and are `escapeXml`'d before re-entering XML attributes; for valid
20
23
  * documents these are enum/hex values, so escaping is a no-op.
21
24
  */
22
- declare function serializeBorder(border: document_d_exports.BorderSpec | undefined, elementName: string): string;
25
+ declare function serializeBorder(input: ExhaustiveBorder | undefined, elementName: string): string;
23
26
  //#endregion
24
27
  export { serializeBorder };
@@ -19,25 +19,27 @@ import { escapeXml, intAttr } from "./xmlUtils.js";
19
19
  * untrusted and are `escapeXml`'d before re-entering XML attributes; for valid
20
20
  * documents these are enum/hex values, so escaping is a no-op.
21
21
  */
22
- function serializeBorder(border, elementName) {
23
- if (!border) return "";
24
- const attrs = [`w:val="${escapeXml(border.style)}"`];
25
- if (border.size !== void 0) attrs.push(`w:sz="${intAttr(border.size)}"`);
26
- if (border.space !== void 0) attrs.push(`w:space="${intAttr(border.space)}"`);
27
- if (border.color) {
28
- if (border.color.auto) attrs.push("w:color=\"auto\"");
29
- else if (border.color.rgb) attrs.push(`w:color="${escapeXml(border.color.rgb)}"`);
30
- if (border.color.themeColor) attrs.push(`w:themeColor="${escapeXml(border.color.themeColor)}"`);
31
- if (border.color.themeTint) attrs.push(`w:themeTint="${escapeXml(border.color.themeTint)}"`);
32
- if (border.color.themeShade) attrs.push(`w:themeShade="${escapeXml(border.color.themeShade)}"`);
22
+ function serializeBorder(input, elementName) {
23
+ if (!input) return "";
24
+ const { style, color, size, space, shadow, frame, artRelationshipId, topLeftArtRelationshipId, topRightArtRelationshipId, bottomLeftArtRelationshipId, bottomRightArtRelationshipId } = input;
25
+ const attrs = [`w:val="${escapeXml(style)}"`];
26
+ if (size !== void 0) attrs.push(`w:sz="${intAttr(size)}"`);
27
+ if (space !== void 0) attrs.push(`w:space="${intAttr(space)}"`);
28
+ if (color) {
29
+ const { auto, rgb, themeColor, themeTint, themeShade } = color;
30
+ if (auto) attrs.push("w:color=\"auto\"");
31
+ else if (rgb) attrs.push(`w:color="${escapeXml(rgb)}"`);
32
+ if (themeColor) attrs.push(`w:themeColor="${escapeXml(themeColor)}"`);
33
+ if (themeTint) attrs.push(`w:themeTint="${escapeXml(themeTint)}"`);
34
+ if (themeShade) attrs.push(`w:themeShade="${escapeXml(themeShade)}"`);
33
35
  }
34
- if (border.shadow) attrs.push("w:shadow=\"true\"");
35
- if (border.frame) attrs.push("w:frame=\"true\"");
36
- if (border.artRelationshipId) attrs.push(`w:id="${escapeXml(border.artRelationshipId)}"`);
37
- if (border.topLeftArtRelationshipId) attrs.push(`w:topLeft="${escapeXml(border.topLeftArtRelationshipId)}"`);
38
- if (border.topRightArtRelationshipId) attrs.push(`w:topRight="${escapeXml(border.topRightArtRelationshipId)}"`);
39
- if (border.bottomLeftArtRelationshipId) attrs.push(`w:bottomLeft="${escapeXml(border.bottomLeftArtRelationshipId)}"`);
40
- if (border.bottomRightArtRelationshipId) attrs.push(`w:bottomRight="${escapeXml(border.bottomRightArtRelationshipId)}"`);
36
+ if (shadow) attrs.push("w:shadow=\"true\"");
37
+ if (frame) attrs.push("w:frame=\"true\"");
38
+ if (artRelationshipId) attrs.push(`w:id="${escapeXml(artRelationshipId)}"`);
39
+ if (topLeftArtRelationshipId) attrs.push(`w:topLeft="${escapeXml(topLeftArtRelationshipId)}"`);
40
+ if (topRightArtRelationshipId) attrs.push(`w:topRight="${escapeXml(topRightArtRelationshipId)}"`);
41
+ if (bottomLeftArtRelationshipId) attrs.push(`w:bottomLeft="${escapeXml(bottomLeftArtRelationshipId)}"`);
42
+ if (bottomRightArtRelationshipId) attrs.push(`w:bottomRight="${escapeXml(bottomRightArtRelationshipId)}"`);
41
43
  return `<w:${elementName} ${attrs.join(" ")}/>`;
42
44
  }
43
45
  //#endregion
@@ -1,7 +1,7 @@
1
1
  import { deterministicHexId } from "../../utils/hexId.js";
2
2
  import { serializeParagraph } from "./paragraphSerializer.js";
3
3
  import { serializePartElement } from "./partNamespaces.js";
4
- import { serializeTextFormatting } from "./runSerializer.js";
4
+ import { serializeTextFormatting } from "./textFormattingSerializer.js";
5
5
  import { escapeXml } from "./xmlUtils.js";
6
6
  //#region src/docx/serializer/commentSerializer.ts
7
7
  /**
@@ -1,5 +1,5 @@
1
1
  import { serializePartElement } from "./partNamespaces.js";
2
- import { serializeTextFormatting } from "./runSerializer.js";
2
+ import { serializeTextFormatting } from "./textFormattingSerializer.js";
3
3
  import { escapeXml, intAttr } from "./xmlUtils.js";
4
4
  //#region src/docx/serializer/numberingSerializer.ts
5
5
  /**
@@ -1,142 +1,19 @@
1
- import { canonicalJson } from "../../utils/canonicalJson.js";
2
- import { isValidHexColor } from "../../utils/colorResolver.js";
3
- import { numPrEqual } from "../numberingParser.js";
4
- import { getParagraphPropertySource } from "../paragraphPropertySource.js";
1
+ import { modelParagraphFormattingEmission } from "../../internal/paragraphFormattingSerialization.js";
2
+ import { getParagraphPropertySource, paragraphPropertySourceMatchesEmission } from "../paragraphPropertySource.js";
5
3
  import { reconcileRawSdtPr } from "../sdtPropertiesPatch.js";
6
4
  import { DATE_UTC_ATTRIBUTE } from "../trackedChangeInfo.js";
7
5
  import { toTransitionalNamespaceUri } from "../transitionalSpelling.js";
8
6
  import { captureVerbatimXml, sanitizeCapturedXmlElement } from "../verbatimCapture.js";
9
7
  import { NAMESPACES, OOXML_NAMESPACE_SCOPE, cloneElement, getChildElements, getLocalName, parseXml } from "../xmlParser.js";
10
- import { serializeBorder } from "./borderSerializer.js";
11
- import { serializeRun, serializeTextFormatting } from "./runSerializer.js";
8
+ import { serializeRun } from "./runSerializer.js";
12
9
  import { serializeSectionProperties } from "./sectionPropertiesSerializer.js";
10
+ import { serializeTextFormatting } from "./textFormattingSerializer.js";
13
11
  import { serializeTrackedChangeAttributes, trackedChangeAttributeRecord } from "./trackedChangeAttributes.js";
14
- import { escapeXml, intAttr, isSingleWellFormedElement } from "./xmlUtils.js";
12
+ import { escapeXml, isSingleWellFormedElement } from "./xmlUtils.js";
15
13
  import { panic } from "better-result";
16
14
  import { PARAGRAPH_MARK_CHANGE_KINDS } from "@stll/docx-core/model";
17
15
  //#region src/docx/serializer/paragraphSerializer.ts
18
16
  /**
19
- * Serialize paragraph borders (w:pBdr)
20
- */
21
- function serializeParagraphBorders(borders) {
22
- if (!borders) return "";
23
- const parts = [];
24
- if (borders.top) {
25
- const topXml = serializeBorder(borders.top, "top");
26
- if (topXml) parts.push(topXml);
27
- }
28
- if (borders.left) {
29
- const leftXml = serializeBorder(borders.left, "left");
30
- if (leftXml) parts.push(leftXml);
31
- }
32
- if (borders.bottom) {
33
- const bottomXml = serializeBorder(borders.bottom, "bottom");
34
- if (bottomXml) parts.push(bottomXml);
35
- }
36
- if (borders.right) {
37
- const rightXml = serializeBorder(borders.right, "right");
38
- if (rightXml) parts.push(rightXml);
39
- }
40
- if (borders.between) {
41
- const betweenXml = serializeBorder(borders.between, "between");
42
- if (betweenXml) parts.push(betweenXml);
43
- }
44
- if (borders.bar) {
45
- const barXml = serializeBorder(borders.bar, "bar");
46
- if (barXml) parts.push(barXml);
47
- }
48
- if (parts.length === 0) return "";
49
- return `<w:pBdr>${parts.join("")}</w:pBdr>`;
50
- }
51
- /**
52
- * Serialize shading properties (w:shd)
53
- */
54
- function serializeShading(shading) {
55
- if (!shading) return "";
56
- const attrs = [];
57
- if (shading.pattern) attrs.push(`w:val="${escapeXml(shading.pattern)}"`);
58
- else attrs.push("w:val=\"clear\"");
59
- if (shading.color?.rgb && isValidHexColor(shading.color.rgb)) attrs.push(`w:color="${escapeXml(shading.color.rgb)}"`);
60
- else if (shading.color?.auto) attrs.push("w:color=\"auto\"");
61
- if (shading.fill?.rgb && isValidHexColor(shading.fill.rgb)) attrs.push(`w:fill="${escapeXml(shading.fill.rgb)}"`);
62
- else if (shading.fill?.auto) attrs.push("w:fill=\"auto\"");
63
- if (shading.fill?.themeColor) attrs.push(`w:themeFill="${escapeXml(shading.fill.themeColor)}"`);
64
- if (shading.fill?.themeTint) attrs.push(`w:themeFillTint="${escapeXml(shading.fill.themeTint)}"`);
65
- if (shading.fill?.themeShade) attrs.push(`w:themeFillShade="${escapeXml(shading.fill.themeShade)}"`);
66
- if (attrs.length === 0) return "";
67
- return `<w:shd ${attrs.join(" ")}/>`;
68
- }
69
- /**
70
- * Serialize tab stops (w:tabs)
71
- */
72
- function serializeTabStops(tabs) {
73
- if (!tabs || tabs.length === 0) return "";
74
- return `<w:tabs>${tabs.map((tab) => {
75
- const attrs = [`w:val="${tab.alignment}"`, `w:pos="${intAttr(tab.position)}"`];
76
- if (tab.leader && tab.leader !== "none") attrs.push(`w:leader="${tab.leader}"`);
77
- return `<w:tab ${attrs.join(" ")}/>`;
78
- }).join("")}</w:tabs>`;
79
- }
80
- /**
81
- * Serialize spacing properties (w:spacing)
82
- */
83
- function serializeSpacing(formatting) {
84
- const attrs = [];
85
- if (formatting.spaceBefore !== void 0) attrs.push(`w:before="${intAttr(formatting.spaceBefore)}"`);
86
- if (formatting.spaceAfter !== void 0) attrs.push(`w:after="${intAttr(formatting.spaceAfter)}"`);
87
- if (formatting.lineSpacing !== void 0) attrs.push(`w:line="${intAttr(formatting.lineSpacing)}"`);
88
- if (formatting.lineSpacingRule) attrs.push(`w:lineRule="${formatting.lineSpacingRule}"`);
89
- if (formatting.beforeAutospacing !== void 0) attrs.push(`w:beforeAutospacing="${formatting.beforeAutospacing ? "1" : "0"}"`);
90
- if (formatting.afterAutospacing !== void 0) attrs.push(`w:afterAutospacing="${formatting.afterAutospacing ? "1" : "0"}"`);
91
- if (attrs.length === 0) return "";
92
- return `<w:spacing ${attrs.join(" ")}/>`;
93
- }
94
- /**
95
- * Serialize indentation properties (w:ind)
96
- */
97
- function serializeIndentation(formatting) {
98
- const attrs = [];
99
- if (formatting.indentLeft !== void 0) attrs.push(`w:left="${intAttr(formatting.indentLeft)}"`);
100
- if (formatting.indentRight !== void 0) attrs.push(`w:right="${intAttr(formatting.indentRight)}"`);
101
- if (formatting.indentFirstLine !== void 0) if (formatting.hangingIndent) attrs.push(`w:hanging="${intAttr(Math.abs(formatting.indentFirstLine))}"`);
102
- else attrs.push(`w:firstLine="${intAttr(formatting.indentFirstLine)}"`);
103
- if (attrs.length === 0) return "";
104
- return `<w:ind ${attrs.join(" ")}/>`;
105
- }
106
- /**
107
- * Serialize numbering properties (w:numPr)
108
- */
109
- function serializeNumbering(numPr) {
110
- if (!numPr) return "";
111
- const parts = [];
112
- if (numPr.ilvl !== void 0) parts.push(`<w:ilvl w:val="${intAttr(numPr.ilvl)}"/>`);
113
- if (numPr.numId !== void 0) parts.push(`<w:numId w:val="${intAttr(numPr.numId)}"/>`);
114
- if (parts.length === 0) return "";
115
- return `<w:numPr>${parts.join("")}</w:numPr>`;
116
- }
117
- /**
118
- * Serialize frame properties (w:framePr)
119
- */
120
- function serializeFrameProperties(frame) {
121
- if (!frame) return "";
122
- const attrs = [];
123
- if (frame.dropCap) attrs.push(`w:dropCap="${frame.dropCap}"`);
124
- if (frame.lines !== void 0) attrs.push(`w:lines="${intAttr(frame.lines)}"`);
125
- if (frame.width !== void 0) attrs.push(`w:w="${intAttr(frame.width)}"`);
126
- if (frame.height !== void 0) attrs.push(`w:h="${intAttr(frame.height)}"`);
127
- if (frame.hSpace !== void 0) attrs.push(`w:hSpace="${intAttr(frame.hSpace)}"`);
128
- if (frame.vSpace !== void 0) attrs.push(`w:vSpace="${intAttr(frame.vSpace)}"`);
129
- if (frame.hAnchor) attrs.push(`w:hAnchor="${frame.hAnchor}"`);
130
- if (frame.vAnchor) attrs.push(`w:vAnchor="${frame.vAnchor}"`);
131
- if (frame.x !== void 0) attrs.push(`w:x="${frame.x}"`);
132
- if (frame.y !== void 0) attrs.push(`w:y="${frame.y}"`);
133
- if (frame.xAlign) attrs.push(`w:xAlign="${frame.xAlign}"`);
134
- if (frame.yAlign) attrs.push(`w:yAlign="${frame.yAlign}"`);
135
- if (frame.wrap) attrs.push(`w:wrap="${frame.wrap}"`);
136
- if (attrs.length === 0) return "";
137
- return `<w:framePr ${attrs.join(" ")}/>`;
138
- }
139
- /**
140
17
  * Serialize paragraph formatting properties to w:pPr XML
141
18
  */
142
19
  function serializeParagraphMarkChange(mark) {
@@ -361,7 +238,7 @@ const verifiedParagraphPropertySource = (formatting, source) => {
361
238
  if (!source) return null;
362
239
  const replayableSource = replayableParagraphPropertySourceXml(source.xml);
363
240
  if (replayableSource === null) return null;
364
- return canonicalJson(formatting ?? {}) === source.formattingJson ? replayableSource : null;
241
+ return paragraphPropertySourceMatchesEmission(source, formatting) ? replayableSource : null;
365
242
  };
366
243
  const withTrailingParagraphPropertyChildren = (sourceXml, children) => {
367
244
  const written = children.join("");
@@ -398,6 +275,7 @@ const withParagraphMarkChange = (sourceXml, mark) => {
398
275
  return captureVerbatimXml(cloneElement(root, { elements }));
399
276
  };
400
277
  const serializeParagraphFormattingWithOptions = (formatting, { propertyChanges, paragraphMarkChange, propertySource, sectionProperties } = {}) => {
278
+ const modeledFormatting = modelParagraphFormattingEmission(formatting);
401
279
  const serializablePropertyChangeCount = propertyChanges?.length ?? 0;
402
280
  if (serializablePropertyChangeCount > 1) panic("A paragraph cannot serialize more than one w:pPrChange", { count: serializablePropertyChangeCount });
403
281
  const paragraphMarkXml = paragraphMarkChange ? serializeParagraphMarkChange(paragraphMarkChange) : "";
@@ -408,50 +286,15 @@ const serializeParagraphFormattingWithOptions = (formatting, { propertyChanges,
408
286
  sectionPropertiesXml,
409
287
  ...propertyChangesXml
410
288
  ].some((xml) => xml.includes(`${DATE_UTC_ATTRIBUTE}=`));
411
- const verifiedSource = verifiedParagraphPropertySource(formatting, propertySource);
289
+ const verifiedSource = verifiedParagraphPropertySource(modeledFormatting, propertySource);
412
290
  if (verifiedSource !== null && (!composedChildrenUseDateUtc || !sourceShadowsDateUtcPrefix(verifiedSource))) {
413
291
  const sourceWithMark = paragraphMarkChange ? withParagraphMarkChange(verifiedSource, paragraphMarkChange) : verifiedSource;
414
292
  return withTrailingParagraphPropertyChildren(sourceWithMark, [sectionPropertiesXml, ...propertyChangesXml]);
415
293
  }
416
294
  const parts = [];
417
- const pushToggle = (name, value) => {
418
- if (value === true) parts.push(`<w:${name}/>`);
419
- else if (value === false) parts.push(`<w:${name} w:val="0"/>`);
420
- };
421
- if (formatting) {
422
- if (formatting.styleId) parts.push(`<w:pStyle w:val="${escapeXml(formatting.styleId)}"/>`);
423
- pushToggle("keepNext", formatting.keepNext);
424
- pushToggle("keepLines", formatting.keepLines);
425
- pushToggle("pageBreakBefore", formatting.pageBreakBefore);
426
- const frameXml = serializeFrameProperties(formatting.frame);
427
- if (frameXml) parts.push(frameXml);
428
- pushToggle("widowControl", formatting.widowControl);
429
- const numPrXml = formatting.numPrFromStyle != null && numPrEqual(formatting.numPr, formatting.numPrFromStyle) ? "" : serializeNumbering(formatting.numPr);
430
- if (numPrXml) parts.push(numPrXml);
431
- pushToggle("suppressLineNumbers", formatting.suppressLineNumbers);
432
- const bordersXml = serializeParagraphBorders(formatting.borders);
433
- if (bordersXml) parts.push(bordersXml);
434
- const shadingXml = serializeShading(formatting.shading);
435
- if (shadingXml) parts.push(shadingXml);
436
- const tabsXml = serializeTabStops(formatting.tabs);
437
- if (tabsXml) parts.push(tabsXml);
438
- pushToggle("suppressAutoHyphens", formatting.suppressAutoHyphens);
439
- pushToggle("kinsoku", formatting.kinsoku);
440
- pushToggle("overflowPunct", formatting.overflowPunctuation);
441
- pushToggle("bidi", formatting.bidi);
442
- pushToggle("snapToGrid", formatting.snapToGrid);
443
- const spacingXml = serializeSpacing(formatting);
444
- if (spacingXml) parts.push(spacingXml);
445
- const indXml = serializeIndentation(formatting);
446
- if (indXml) parts.push(indXml);
447
- pushToggle("contextualSpacing", formatting.contextualSpacing);
448
- if (formatting.alignment) parts.push(`<w:jc w:val="${formatting.alignment}"/>`);
449
- if (formatting.outlineLevel !== void 0) parts.push(`<w:outlineLvl w:val="${formatting.outlineLevel}"/>`);
450
- if (paragraphMarkChange || formatting.runProperties || formatting.runInWithNext) {
451
- const fullInner = `${paragraphMarkChange ? paragraphMarkXml : ""}${formatting.runProperties ? extractRPrInner(serializeTextFormatting(formatting.runProperties)) : ""}${formatting.runInWithNext ? "<w:specVanish/>" : ""}`;
452
- if (fullInner.length > 0) parts.push(`<w:rPr>${fullInner}</w:rPr>`);
453
- }
454
- } else if (paragraphMarkChange) parts.push(`<w:rPr>${paragraphMarkXml}</w:rPr>`);
295
+ if (modeledFormatting.propertiesXml) parts.push(modeledFormatting.propertiesXml);
296
+ const paragraphMarkPropertiesInnerXml = `${paragraphMarkXml}${modeledFormatting.paragraphMarkPropertiesInnerXml ?? ""}`;
297
+ if (paragraphMarkPropertiesInnerXml) parts.push(`<w:rPr>${paragraphMarkPropertiesInnerXml}</w:rPr>`);
455
298
  parts.push(sectionPropertiesXml);
456
299
  if (propertyChangesXml.length > 0) parts.push(...propertyChangesXml);
457
300
  const inner = parts.join("");
@@ -468,15 +311,6 @@ function extractPPrInner(pPrXml) {
468
311
  if (!pPrXml.startsWith("<w:pPr>") || !pPrXml.endsWith("</w:pPr>")) return "";
469
312
  return pPrXml.slice(7, -8);
470
313
  }
471
- /**
472
- * Strip the outer `<w:rPr>...</w:rPr>` wrapper so callers can splice
473
- * additional rPr children (e.g. `<w:specVanish/>`) and re-emit a
474
- * single rPr element.
475
- */
476
- function extractRPrInner(rPrXml) {
477
- if (!rPrXml.startsWith("<w:rPr>") || !rPrXml.endsWith("</w:rPr>")) return "";
478
- return rPrXml.slice(7, -8);
479
- }
480
314
  function serializeParagraphPropertyChange(change) {
481
315
  const previousPPrInner = extractPPrInner(serializeParagraphFormatting(change.previousFormatting) || "<w:pPr/>");
482
316
  const normalizedPreviousPPr = previousPPrInner.length > 0 ? `<w:pPr>${previousPPrInner}</w:pPr>` : "<w:pPr/>";
@@ -5,10 +5,6 @@ import { document_d_exports } from "../../types/document.js";
5
5
  * to keep IDs deterministic across saves.
6
6
  */
7
7
  declare function resetAutoIdCounter(): void;
8
- /**
9
- * Serialize text formatting properties to w:rPr XML
10
- */
11
- declare function serializeTextFormatting(formatting: document_d_exports.TextFormatting | undefined): string;
12
8
  /**
13
9
  * Serialize a run to OOXML XML (w:r)
14
10
  *
@@ -52,4 +48,4 @@ declare function createBreakRun(breakType?: "page" | "column" | "textWrapping",
52
48
  */
53
49
  declare function createTabRun(formatting?: document_d_exports.TextFormatting): document_d_exports.Run;
54
50
  //#endregion
55
- export { createBreakRun, createEmptyRun, createTabRun, createTextRun, getRunPlainText, hasRunContent, hasRunFormatting, resetAutoIdCounter, serializeRun, serializeRuns, serializeTextFormatting };
51
+ export { createBreakRun, createEmptyRun, createTabRun, createTextRun, getRunPlainText, hasRunContent, hasRunFormatting, resetAutoIdCounter, serializeRun, serializeRuns };
@@ -1,10 +1,9 @@
1
- import { HIGHLIGHT_COLOR_VALUES } from "../../types/documentEnumValues.js";
2
1
  import { isValidHexColor } from "../../utils/colorResolver.js";
3
- import { roundHorizontalScalePercentForSerialization } from "../../utils/horizontalScale.js";
4
2
  import { THEME_COLOR_TO_DRAWING_SCHEME } from "../drawingUtils.js";
5
3
  import { requiresXmlSpacePreserve } from "../textWhitespace.js";
6
4
  import { serializeParagraph } from "./paragraphSerializer.js";
7
5
  import { serializeTable } from "./tableSerializer.js";
6
+ import { serializeTextFormatting } from "./textFormattingSerializer.js";
8
7
  import { getSingularRunPropertyChange, serializeTrackedChangeAttributes } from "./trackedChangeAttributes.js";
9
8
  import { escapeXml, intAttr } from "./xmlUtils.js";
10
9
  import { panic } from "better-result";
@@ -38,132 +37,6 @@ function getUniqueId(id) {
38
37
  if (id !== void 0 && id !== "" && id !== 0) return String(id);
39
38
  return String(nextAutoId++);
40
39
  }
41
- /** Valid OOXML highlight color names (ECMA-376 §17.18.40) */
42
- const VALID_HIGHLIGHT_COLORS = new Set(HIGHLIGHT_COLOR_VALUES);
43
- /**
44
- * Serialize a color element (w:color)
45
- */
46
- function serializeColorElement(color) {
47
- if (!color) return "";
48
- const attrs = [];
49
- if (color.auto) attrs.push("w:val=\"auto\"");
50
- else if (color.rgb && isValidHexColor(color.rgb)) attrs.push(`w:val="${escapeXml(color.rgb)}"`);
51
- if (color.themeColor) attrs.push(`w:themeColor="${escapeXml(color.themeColor)}"`);
52
- if (color.themeTint) attrs.push(`w:themeTint="${escapeXml(color.themeTint)}"`);
53
- if (color.themeShade) attrs.push(`w:themeShade="${escapeXml(color.themeShade)}"`);
54
- if (attrs.length === 0) return "";
55
- return `<w:color ${attrs.join(" ")}/>`;
56
- }
57
- /**
58
- * Serialize shading properties (w:shd)
59
- */
60
- function serializeShading(shading) {
61
- if (!shading) return "";
62
- const attrs = [];
63
- if (shading.pattern) attrs.push(`w:val="${escapeXml(shading.pattern)}"`);
64
- else attrs.push("w:val=\"clear\"");
65
- if (shading.color?.rgb && isValidHexColor(shading.color.rgb)) attrs.push(`w:color="${escapeXml(shading.color.rgb)}"`);
66
- else if (shading.color?.auto) attrs.push("w:color=\"auto\"");
67
- if (shading.fill?.rgb && isValidHexColor(shading.fill.rgb)) attrs.push(`w:fill="${escapeXml(shading.fill.rgb)}"`);
68
- else if (shading.fill?.auto) attrs.push("w:fill=\"auto\"");
69
- if (shading.fill?.themeColor) attrs.push(`w:themeFill="${escapeXml(shading.fill.themeColor)}"`);
70
- if (shading.fill?.themeTint) attrs.push(`w:themeFillTint="${escapeXml(shading.fill.themeTint)}"`);
71
- if (shading.fill?.themeShade) attrs.push(`w:themeFillShade="${escapeXml(shading.fill.themeShade)}"`);
72
- if (attrs.length === 0) return "";
73
- return `<w:shd ${attrs.join(" ")}/>`;
74
- }
75
- /**
76
- * Serialize text formatting properties to w:rPr XML
77
- */
78
- function serializeTextFormatting(formatting) {
79
- if (!formatting) return "";
80
- const parts = [];
81
- if (formatting.styleId) parts.push(`<w:rStyle w:val="${escapeXml(formatting.styleId)}"/>`);
82
- if (formatting.fontFamily) {
83
- const fontAttrs = [];
84
- if (formatting.fontFamily.ascii) fontAttrs.push(`w:ascii="${escapeXml(formatting.fontFamily.ascii)}"`);
85
- if (formatting.fontFamily.hAnsi) fontAttrs.push(`w:hAnsi="${escapeXml(formatting.fontFamily.hAnsi)}"`);
86
- if (formatting.fontFamily.eastAsia) fontAttrs.push(`w:eastAsia="${escapeXml(formatting.fontFamily.eastAsia)}"`);
87
- if (formatting.fontFamily.cs) fontAttrs.push(`w:cs="${escapeXml(formatting.fontFamily.cs)}"`);
88
- if (formatting.fontFamily.hint) fontAttrs.push(`w:hint="${escapeXml(formatting.fontFamily.hint)}"`);
89
- if (formatting.fontFamily.asciiTheme) fontAttrs.push(`w:asciiTheme="${escapeXml(formatting.fontFamily.asciiTheme)}"`);
90
- if (formatting.fontFamily.hAnsiTheme) fontAttrs.push(`w:hAnsiTheme="${escapeXml(formatting.fontFamily.hAnsiTheme)}"`);
91
- if (formatting.fontFamily.eastAsiaTheme) fontAttrs.push(`w:eastAsiaTheme="${escapeXml(formatting.fontFamily.eastAsiaTheme)}"`);
92
- if (formatting.fontFamily.csTheme) fontAttrs.push(`w:cstheme="${escapeXml(formatting.fontFamily.csTheme)}"`);
93
- if (fontAttrs.length > 0) parts.push(`<w:rFonts ${fontAttrs.join(" ")}/>`);
94
- }
95
- if (formatting.bold === true) parts.push("<w:b/>");
96
- else if (formatting.bold === false) parts.push("<w:b w:val=\"0\"/>");
97
- if (formatting.boldCs === true) parts.push("<w:bCs/>");
98
- else if (formatting.boldCs === false) parts.push("<w:bCs w:val=\"0\"/>");
99
- if (formatting.italic === true) parts.push("<w:i/>");
100
- else if (formatting.italic === false) parts.push("<w:i w:val=\"0\"/>");
101
- if (formatting.italicCs === true) parts.push("<w:iCs/>");
102
- else if (formatting.italicCs === false) parts.push("<w:iCs w:val=\"0\"/>");
103
- if (formatting.allCaps === true) parts.push("<w:caps/>");
104
- else if (formatting.allCaps === false) parts.push("<w:caps w:val=\"0\"/>");
105
- if (formatting.smallCaps === true) parts.push("<w:smallCaps/>");
106
- else if (formatting.smallCaps === false) parts.push("<w:smallCaps w:val=\"0\"/>");
107
- if (formatting.strike === true) parts.push("<w:strike/>");
108
- else if (formatting.strike === false) parts.push("<w:strike w:val=\"0\"/>");
109
- if (formatting.doubleStrike === true) parts.push("<w:dstrike/>");
110
- else if (formatting.doubleStrike === false) parts.push("<w:dstrike w:val=\"0\"/>");
111
- if (formatting.outline === true) parts.push("<w:outline/>");
112
- else if (formatting.outline === false) parts.push("<w:outline w:val=\"0\"/>");
113
- if (formatting.shadow === true) parts.push("<w:shadow/>");
114
- else if (formatting.shadow === false) parts.push("<w:shadow w:val=\"0\"/>");
115
- if (formatting.emboss === true) parts.push("<w:emboss/>");
116
- else if (formatting.emboss === false) parts.push("<w:emboss w:val=\"0\"/>");
117
- if (formatting.imprint === true) parts.push("<w:imprint/>");
118
- else if (formatting.imprint === false) parts.push("<w:imprint w:val=\"0\"/>");
119
- if (formatting.hidden === true) parts.push("<w:vanish/>");
120
- else if (formatting.hidden === false) parts.push("<w:vanish w:val=\"0\"/>");
121
- const colorXml = serializeColorElement(formatting.color);
122
- if (colorXml) parts.push(colorXml);
123
- if (formatting.spacing !== void 0) parts.push(`<w:spacing w:val="${intAttr(formatting.spacing)}"/>`);
124
- const horizontalScale = roundHorizontalScalePercentForSerialization(formatting.scale);
125
- if (horizontalScale !== void 0) parts.push(`<w:w w:val="${intAttr(horizontalScale)}"/>`);
126
- if (formatting.kerning !== void 0) parts.push(`<w:kern w:val="${intAttr(formatting.kerning)}"/>`);
127
- if (formatting.position !== void 0) parts.push(`<w:position w:val="${intAttr(formatting.position)}"/>`);
128
- if (formatting.fontSize !== void 0) parts.push(`<w:sz w:val="${intAttr(formatting.fontSize)}"/>`);
129
- if (formatting.fontSizeCs !== void 0) parts.push(`<w:szCs w:val="${intAttr(formatting.fontSizeCs)}"/>`);
130
- let customHighlightShadingXml = "";
131
- if (formatting.highlight) {
132
- if (VALID_HIGHLIGHT_COLORS.has(formatting.highlight)) parts.push(`<w:highlight w:val="${formatting.highlight}"/>`);
133
- else if (!formatting.shading) {
134
- const hex = formatting.highlight.replace(/^#/u, "");
135
- if (/^[0-9a-fA-F]{6}$/u.test(hex)) customHighlightShadingXml = `<w:shd w:val="clear" w:color="auto" w:fill="${hex}"/>`;
136
- }
137
- }
138
- if (formatting.underline) {
139
- const uAttrs = [`w:val="${formatting.underline.style}"`];
140
- if (formatting.underline.color) {
141
- if (formatting.underline.color.rgb && isValidHexColor(formatting.underline.color.rgb)) uAttrs.push(`w:color="${escapeXml(formatting.underline.color.rgb)}"`);
142
- if (formatting.underline.color.themeColor) uAttrs.push(`w:themeColor="${escapeXml(formatting.underline.color.themeColor)}"`);
143
- if (formatting.underline.color.themeTint) uAttrs.push(`w:themeTint="${escapeXml(formatting.underline.color.themeTint)}"`);
144
- if (formatting.underline.color.themeShade) uAttrs.push(`w:themeShade="${escapeXml(formatting.underline.color.themeShade)}"`);
145
- }
146
- parts.push(`<w:u ${uAttrs.join(" ")}/>`);
147
- }
148
- if (formatting.effect) parts.push(`<w:effect w:val="${formatting.effect}"/>`);
149
- const shadingXml = serializeShading(formatting.shading) || customHighlightShadingXml;
150
- if (shadingXml) parts.push(shadingXml);
151
- if (formatting.vertAlign) parts.push(`<w:vertAlign w:val="${formatting.vertAlign}"/>`);
152
- if (formatting.rtl === true) parts.push("<w:rtl/>");
153
- else if (formatting.rtl === false) parts.push("<w:rtl w:val=\"0\"/>");
154
- if (formatting.cs === true) parts.push("<w:cs/>");
155
- else if (formatting.cs === false) parts.push("<w:cs w:val=\"0\"/>");
156
- if (formatting.emphasisMark) parts.push(`<w:em w:val="${formatting.emphasisMark}"/>`);
157
- if (formatting.language) {
158
- const languageAttrs = [];
159
- if (formatting.language.val) languageAttrs.push(`w:val="${escapeXml(formatting.language.val)}"`);
160
- if (formatting.language.eastAsia) languageAttrs.push(`w:eastAsia="${escapeXml(formatting.language.eastAsia)}"`);
161
- if (formatting.language.bidi) languageAttrs.push(`w:bidi="${escapeXml(formatting.language.bidi)}"`);
162
- if (languageAttrs.length > 0) parts.push(`<w:lang ${languageAttrs.join(" ")}/>`);
163
- }
164
- if (parts.length === 0) return "";
165
- return `<w:rPr>${parts.join("")}</w:rPr>`;
166
- }
167
40
  function extractRPrInner(rPrXml) {
168
41
  if (!rPrXml.startsWith("<w:rPr>") || !rPrXml.endsWith("</w:rPr>")) return "";
169
42
  return rPrXml.slice(7, -8);
@@ -667,4 +540,4 @@ function createTabRun(formatting) {
667
540
  };
668
541
  }
669
542
  //#endregion
670
- export { createBreakRun, createEmptyRun, createTabRun, createTextRun, getRunPlainText, hasRunContent, hasRunFormatting, resetAutoIdCounter, serializeRun, serializeRuns, serializeTextFormatting };
543
+ export { createBreakRun, createEmptyRun, createTabRun, createTextRun, getRunPlainText, hasRunContent, hasRunFormatting, resetAutoIdCounter, serializeRun, serializeRuns };
@@ -1,7 +1,7 @@
1
1
  import { serializeParagraphFormatting } from "./paragraphSerializer.js";
2
2
  import { serializePartElement } from "./partNamespaces.js";
3
- import { serializeTextFormatting } from "./runSerializer.js";
4
3
  import { serializeTableCellFormatting, serializeTableFormatting, serializeTableRowFormatting } from "./tableSerializer.js";
4
+ import { serializeTextFormatting } from "./textFormattingSerializer.js";
5
5
  import { escapeXml } from "./xmlUtils.js";
6
6
  //#region src/docx/serializer/stylesSerializer.ts
7
7
  const serializeStylesXml = (definitions) => {
@@ -0,0 +1,17 @@
1
+ import { document_d_exports } from "../../types/document.js";
2
+ //#region src/docx/serializer/textFormattingSerializer.d.ts
3
+ type ExhaustiveFields<Source, Classified extends keyof Source> = Exclude<keyof Source, Classified> extends never ? Source : never;
4
+ type ClassifiedShadingField = "color" | "fill" | "pattern";
5
+ type ExhaustiveShadingProperties = ExhaustiveFields<document_d_exports.ShadingProperties, ClassifiedShadingField>;
6
+ type ClassifiedTextFormattingField = "bold" | "boldCs" | "italic" | "italicCs" | "underline" | "strike" | "doubleStrike" | "vertAlign" | "smallCaps" | "allCaps" | "hidden" | "color" | "highlight" | "shading" | "fontSize" | "fontSizeCs" | "fontFamily" | "language" | "spacing" | "position" | "scale" | "kerning" | "effect" | "emphasisMark" | "emboss" | "imprint" | "outline" | "shadow" | "rtl" | "cs" | "styleId";
7
+ type ExhaustiveTextFormatting = ExhaustiveFields<document_d_exports.TextFormatting, ClassifiedTextFormattingField>;
8
+ /**
9
+ * Serialize shading properties (w:shd)
10
+ */
11
+ declare function serializeShading(shading: ExhaustiveShadingProperties | undefined): string;
12
+ /**
13
+ * Serialize text formatting properties to w:rPr XML
14
+ */
15
+ declare function serializeTextFormatting(input: ExhaustiveTextFormatting | undefined): string;
16
+ //#endregion
17
+ export { serializeShading, serializeTextFormatting };