@stll/folio-core 0.33.1 → 0.34.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.
Files changed (121) hide show
  1. package/dist/ai-edits/apply.d.ts +15 -0
  2. package/dist/ai-edits/apply.js +271 -48
  3. package/dist/ai-edits/headless.d.ts +47 -3
  4. package/dist/ai-edits/headless.js +53 -3
  5. package/dist/ai-edits/index.d.ts +1 -1
  6. package/dist/ai-edits/snapshot.d.ts +19 -1
  7. package/dist/ai-edits/snapshot.js +49 -5
  8. package/dist/ai-edits/table-geometry.d.ts +56 -0
  9. package/dist/ai-edits/table-geometry.js +217 -0
  10. package/dist/ai-edits/table-row-column-mutations.d.ts +34 -16
  11. package/dist/ai-edits/table-row-column-mutations.js +12 -4
  12. package/dist/ai-edits/table-template.d.ts +60 -0
  13. package/dist/ai-edits/table-template.js +176 -0
  14. package/dist/ai-edits/types.d.ts +13 -6
  15. package/dist/compare/__fixtures__/body-sequence.d.ts +61 -2
  16. package/dist/compare/__fixtures__/body-sequence.js +94 -6
  17. package/dist/compare/compare.d.ts +14 -7
  18. package/dist/compare/compare.js +136 -38
  19. package/dist/compare/formatting.d.ts +1 -1
  20. package/dist/compare/formatting.js +38 -9
  21. package/dist/compare/plan.d.ts +31 -1
  22. package/dist/compare/plan.js +233 -66
  23. package/dist/compare/types.d.ts +9 -7
  24. package/dist/compare/types.js +7 -5
  25. package/dist/compare/verification.d.ts +31 -12
  26. package/dist/compare/verification.js +106 -18
  27. package/dist/compat/eigenpal.d.ts +4 -4
  28. package/dist/compat/eigenpal.js +2 -2
  29. package/dist/controller/headerFooterEditorManager.js +11 -9
  30. package/dist/controller/layoutPipeline.js +24 -3
  31. package/dist/display-list/build/watermarkPrimitives.js +15 -3
  32. package/dist/display-list/primitives.d.ts +1 -1
  33. package/dist/document-operations.d.ts +10 -1
  34. package/dist/document-operations.js +33 -5
  35. package/dist/docx/appVersionNormalization.d.ts +49 -0
  36. package/dist/docx/appVersionNormalization.js +74 -0
  37. package/dist/docx/blockContentParser.js +3 -2
  38. package/dist/docx/drawingUtils.js +4 -3
  39. package/dist/docx/headerFooterParser.js +8 -14
  40. package/dist/docx/paragraphParser.js +48 -2
  41. package/dist/docx/rezip.d.ts +10 -4
  42. package/dist/docx/rezip.js +54 -18
  43. package/dist/docx/runParser.js +9 -8
  44. package/dist/docx/sdtProperties.js +4 -3
  45. package/dist/docx/selectiveSave.js +7 -6
  46. package/dist/docx/serializer/commentSerializer.d.ts +1 -1
  47. package/dist/docx/serializer/commentSerializer.js +43 -19
  48. package/dist/docx/serializer/documentSerializer.d.ts +3 -1
  49. package/dist/docx/serializer/documentSerializer.js +42 -78
  50. package/dist/docx/serializer/fontTableSerializer.js +8 -5
  51. package/dist/docx/serializer/headerFooterSerializer.d.ts +4 -1
  52. package/dist/docx/serializer/headerFooterSerializer.js +55 -31
  53. package/dist/docx/serializer/noteSerializer.js +34 -30
  54. package/dist/docx/serializer/numberingSerializer.js +8 -4
  55. package/dist/docx/serializer/paragraphSerializer.d.ts +1 -1
  56. package/dist/docx/serializer/paragraphSerializer.js +22 -12
  57. package/dist/docx/serializer/partNamespaces.d.ts +78 -0
  58. package/dist/docx/serializer/partNamespaces.js +324 -0
  59. package/dist/docx/serializer/runSerializer.js +1 -1
  60. package/dist/docx/serializer/settingsSerializer.js +8 -2
  61. package/dist/docx/serializer/stylesSerializer.js +8 -5
  62. package/dist/docx/serializer/tableSerializer.js +83 -18
  63. package/dist/docx/serializer/themeSerializer.js +10 -2
  64. package/dist/docx/server/build.d.ts +1 -1
  65. package/dist/docx/settingsParser.js +3 -0
  66. package/dist/docx/strictValueEncodings.gen.d.ts +15 -0
  67. package/dist/docx/strictValueEncodings.gen.js +275 -0
  68. package/dist/docx/tableParser.d.ts +0 -6
  69. package/dist/docx/tableParser.js +52 -7
  70. package/dist/docx/transitionalSpelling.d.ts +23 -0
  71. package/dist/docx/transitionalSpelling.js +36 -0
  72. package/dist/docx/universalMeasure.d.ts +20 -0
  73. package/dist/docx/universalMeasure.js +32 -0
  74. package/dist/docx/verbatimCapture.d.ts +20 -0
  75. package/dist/docx/verbatimCapture.js +131 -0
  76. package/dist/docx/vmlImageParser.js +4 -3
  77. package/dist/docx/watermarkParser.d.ts +2 -4
  78. package/dist/docx/watermarkParser.js +8 -9
  79. package/dist/docx/xmlParser.d.ts +23 -2
  80. package/dist/docx/xmlParser.js +43 -11
  81. package/dist/headless-layout.js +14 -2
  82. package/dist/index.d.ts +4 -4
  83. package/dist/index.js +2 -2
  84. package/dist/layout-bridge/convert/footnoteLayout.d.ts +2 -2
  85. package/dist/layout-bridge/convert/footnoteLayout.js +23 -10
  86. package/dist/layout-bridge/convert/headerFooterLayout.js +13 -2
  87. package/dist/layout-bridge/convert/toFlowBlocks.js +96 -13
  88. package/dist/layout-engine/index.js +11 -4
  89. package/dist/layout-engine/justifiedLineFit.d.ts +4 -4
  90. package/dist/layout-engine/justifiedLineFit.js +4 -4
  91. package/dist/layout-engine/measure/lineBreakProvider.js +1 -0
  92. package/dist/layout-engine/measure/measureBlocks.js +1 -1
  93. package/dist/layout-engine/measure/measureParagraph.js +29 -29
  94. package/dist/layout-painter/renderPage.js +6 -1
  95. package/dist/layout-painter/renderParagraph.js +18 -8
  96. package/dist/layout-painter/renderWatermark.js +11 -4
  97. package/dist/model.d.ts +3 -3
  98. package/dist/model.js +2 -2
  99. package/dist/prosemirror/attrs/index.js +11 -0
  100. package/dist/prosemirror/commands/comments.js +24 -3
  101. package/dist/prosemirror/containerFinalParagraph.d.ts +47 -0
  102. package/dist/prosemirror/containerFinalParagraph.js +92 -0
  103. package/dist/prosemirror/conversion/fromProseDoc.d.ts +8 -1
  104. package/dist/prosemirror/conversion/fromProseDoc.js +47 -9
  105. package/dist/prosemirror/conversion/toProseDoc.js +61 -14
  106. package/dist/prosemirror/extensions/core/DocExtension.js +7 -1
  107. package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
  108. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +1 -0
  109. package/dist/prosemirror/extensions/nodes/TableExtension.js +5 -0
  110. package/dist/prosemirror/schema/marks.d.ts +3 -9
  111. package/dist/prosemirror/schema/nodes.d.ts +22 -0
  112. package/dist/server.d.ts +1 -1
  113. package/dist/types/block-id.d.ts +12 -1
  114. package/dist/types/block-id.js +17 -1
  115. package/dist/utils/canonicalJson.d.ts +14 -0
  116. package/dist/utils/canonicalJson.js +19 -0
  117. package/dist/utils/fontResolver.js +51 -0
  118. package/dist/utils/formatToStyle.d.ts +1 -1
  119. package/dist/utils/formatToStyle.js +41 -1
  120. package/dist/watermark/index.js +7 -0
  121. package/package.json +2 -2
@@ -1,5 +1,6 @@
1
1
  import { deterministicHexId } from "../../utils/hexId.js";
2
2
  import { serializeParagraph } from "./paragraphSerializer.js";
3
+ import { serializePartElement } from "./partNamespaces.js";
3
4
  import { serializeTextFormatting } from "./runSerializer.js";
4
5
  import { escapeXml } from "./xmlUtils.js";
5
6
  //#region src/docx/serializer/commentSerializer.ts
@@ -36,12 +37,29 @@ function serializeComment(comment) {
36
37
  xml += "</w:comment>";
37
38
  return xml;
38
39
  }
39
- const COMMENT_EXTENSION_NAMESPACES = {
40
- w14: "http://schemas.microsoft.com/office/word/2010/wordml",
41
- wp14: "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing"
42
- };
43
- const COMMENTS_HEADER = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
44
- <w:comments xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" ${Object.entries(COMMENT_EXTENSION_NAMESPACES).map(([prefix, namespace]) => `xmlns:${prefix}="${namespace}"`).join(" ")} xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="${Object.keys(COMMENT_EXTENSION_NAMESPACES).join(" ")}">`;
40
+ const COMMENTS_BASELINE_PREFIXES = [
41
+ "wpc",
42
+ "mc",
43
+ "o",
44
+ "r",
45
+ "m",
46
+ "v",
47
+ "wp",
48
+ "w10",
49
+ "w",
50
+ "w14",
51
+ "wp14",
52
+ "wpg",
53
+ "wpi",
54
+ "wne",
55
+ "wps"
56
+ ];
57
+ const COMMENTS_EXTENDED_BASELINE_PREFIXES = [
58
+ "mc",
59
+ "w",
60
+ "w15"
61
+ ];
62
+ const XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n";
45
63
  /**
46
64
  * Serialize comments array to comments.xml content. Returns a valid empty
47
65
  * `<w:comments/>` document for an empty array so callers can overwrite an
@@ -49,18 +67,21 @@ const COMMENTS_HEADER = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
49
67
  * comment — leaving the previous file in place would otherwise re-emit
50
68
  * the orphaned comment threads on every save.
51
69
  */
52
- function serializeComments(comments) {
70
+ function serializeComments(comments, sourceBindings) {
53
71
  const topLevel = [];
54
72
  const replies = [];
55
73
  for (const c of comments) {
56
74
  const { parentId } = c;
57
75
  (parentId === null || parentId === void 0 ? topLevel : replies).push(c);
58
76
  }
59
- let xml = COMMENTS_HEADER;
60
- for (const comment of topLevel) xml += serializeComment(comment);
61
- for (const reply of replies) xml += serializeComment(reply);
62
- xml += "</w:comments>";
63
- return xml;
77
+ const body = topLevel.map((comment) => serializeComment(comment)).join("") + replies.map((reply) => serializeComment(reply)).join("");
78
+ return XML_DECLARATION + serializePartElement({
79
+ partPath: "word/comments.xml",
80
+ rootName: "w:comments",
81
+ baselinePrefixes: COMMENTS_BASELINE_PREFIXES,
82
+ sourceBindings,
83
+ body
84
+ });
64
85
  }
65
86
  /** The `w14:paraId` Word threads a comment by: its LAST paragraph's paraId. */
66
87
  function commentThreadParaId(comment) {
@@ -145,7 +166,6 @@ function buildCommentExtendedEntries(comments) {
145
166
  }
146
167
  return entries.length > 0 ? entries : null;
147
168
  }
148
- const COMMENTS_EXTENDED_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<w15:commentsEx xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:w15=\"http://schemas.microsoft.com/office/word/2012/wordml\" mc:Ignorable=\"w15\">";
149
169
  /**
150
170
  * Serialize `commentsExtended.xml` (`w15:commentsEx`) for reply threading and
151
171
  * resolved state, or `null` when no comment needs an entry (see
@@ -155,13 +175,17 @@ const COMMENTS_EXTENDED_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\" stand
155
175
  function serializeCommentsExtended(comments) {
156
176
  const entries = buildCommentExtendedEntries(comments);
157
177
  if (!entries) return null;
158
- let xml = COMMENTS_EXTENDED_HEADER;
159
- for (const entry of entries) {
178
+ const body = entries.map((entry) => {
160
179
  const parentAttr = entry.paraIdParent !== void 0 ? ` w15:paraIdParent="${escapeXml(entry.paraIdParent)}"` : "";
161
- xml += `<w15:commentEx w15:paraId="${escapeXml(entry.paraId)}"${parentAttr} w15:done="${entry.done ? "1" : "0"}"/>`;
162
- }
163
- xml += "</w15:commentsEx>";
164
- return xml;
180
+ return `<w15:commentEx w15:paraId="${escapeXml(entry.paraId)}"${parentAttr} w15:done="${entry.done ? "1" : "0"}"/>`;
181
+ }).join("");
182
+ return XML_DECLARATION + serializePartElement({
183
+ partPath: "word/commentsExtended.xml",
184
+ rootName: "w15:commentsEx",
185
+ baselinePrefixes: COMMENTS_EXTENDED_BASELINE_PREFIXES,
186
+ sourceBindings: void 0,
187
+ body
188
+ });
165
189
  }
166
190
  //#endregion
167
191
  export { ensureThreadedCommentParaIds, serializeComments, serializeCommentsExtended };
@@ -11,9 +11,11 @@ declare function serializeDocumentBody(body: document_d_exports.DocumentBody): s
11
11
  * Serialize a complete Document to valid document.xml
12
12
  *
13
13
  * @param doc - The document to serialize
14
+ * @param sourceBindings - Root `xmlns:*` of the part being replaced, so a
15
+ * prefix only the source document bound keeps its URI
14
16
  * @returns Complete XML string for document.xml
15
17
  */
16
- declare function serializeDocument(doc: document_d_exports.Document): string;
18
+ declare function serializeDocument(doc: document_d_exports.Document, sourceBindings?: ReadonlyMap<string, string>): string;
17
19
  /**
18
20
  * Serialize just the document body (useful for partial updates)
19
21
  *
@@ -1,78 +1,41 @@
1
1
  import { serializeBlockSdt } from "./blockSdtSerializer.js";
2
2
  import { serializeParagraph } from "./paragraphSerializer.js";
3
+ import { serializePartElement } from "./partNamespaces.js";
3
4
  import { resetAutoIdCounter } from "./runSerializer.js";
4
5
  import { serializeSectionProperties } from "./sectionPropertiesSerializer.js";
5
6
  import { serializeTable } from "./tableSerializer.js";
6
7
  //#region src/docx/serializer/documentSerializer.ts
7
8
  /**
8
- * Standard OOXML namespaces for document.xml
9
- */
10
- const NAMESPACES = {
11
- a: "http://schemas.openxmlformats.org/drawingml/2006/main",
12
- wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
13
- cx: "http://schemas.microsoft.com/office/drawing/2014/chartex",
14
- cx1: "http://schemas.microsoft.com/office/drawing/2015/9/8/chartex",
15
- cx2: "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex",
16
- cx3: "http://schemas.microsoft.com/office/drawing/2016/5/9/chartex",
17
- cx4: "http://schemas.microsoft.com/office/drawing/2016/5/10/chartex",
18
- cx5: "http://schemas.microsoft.com/office/drawing/2016/5/11/chartex",
19
- cx6: "http://schemas.microsoft.com/office/drawing/2016/5/12/chartex",
20
- cx7: "http://schemas.microsoft.com/office/drawing/2016/5/13/chartex",
21
- cx8: "http://schemas.microsoft.com/office/drawing/2016/5/14/chartex",
22
- mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
23
- aink: "http://schemas.microsoft.com/office/drawing/2016/ink",
24
- am3d: "http://schemas.microsoft.com/office/drawing/2017/model3d",
25
- o: "urn:schemas-microsoft-com:office:office",
26
- oel: "http://schemas.microsoft.com/office/2019/extlst",
27
- pic: "http://schemas.openxmlformats.org/drawingml/2006/picture",
28
- r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
29
- m: "http://schemas.openxmlformats.org/officeDocument/2006/math",
30
- v: "urn:schemas-microsoft-com:vml",
31
- wp14: "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
32
- wp: "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
33
- w10: "urn:schemas-microsoft-com:office:word",
34
- w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
35
- w14: "http://schemas.microsoft.com/office/word/2010/wordml",
36
- w15: "http://schemas.microsoft.com/office/word/2012/wordml",
37
- w16cex: "http://schemas.microsoft.com/office/word/2018/wordml/cex",
38
- w16cid: "http://schemas.microsoft.com/office/word/2016/wordml/cid",
39
- w16: "http://schemas.microsoft.com/office/word/2018/wordml",
40
- w16sdtdh: "http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash",
41
- w16se: "http://schemas.microsoft.com/office/word/2015/wordml/symex",
42
- wpg: "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
43
- wpi: "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
44
- wne: "http://schemas.microsoft.com/office/word/2006/wordml",
45
- wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape"
46
- };
47
- /**
48
- * Build namespace declaration string for document element
49
- */
50
- function buildNamespaceDeclarations() {
51
- const declared = {
52
- a: NAMESPACES.a,
53
- wpc: NAMESPACES.wpc,
54
- mc: NAMESPACES.mc,
55
- o: NAMESPACES.o,
56
- pic: NAMESPACES.pic,
57
- r: NAMESPACES.r,
58
- m: NAMESPACES.m,
59
- v: NAMESPACES.v,
60
- wp14: NAMESPACES.wp14,
61
- wp: NAMESPACES.wp,
62
- w10: NAMESPACES.w10,
63
- w: NAMESPACES.w,
64
- w14: NAMESPACES.w14,
65
- w15: NAMESPACES.w15,
66
- w16: NAMESPACES.w16,
67
- w16cex: NAMESPACES.w16cex,
68
- w16cid: NAMESPACES.w16cid,
69
- w16sdtdh: NAMESPACES.w16sdtdh,
70
- w16se: NAMESPACES.w16se,
71
- wpg: NAMESPACES.wpg,
72
- wps: NAMESPACES.wps
73
- };
74
- return Object.entries(declared).map(([prefix, uri]) => `xmlns:${prefix}="${uri}"`).join(" ");
75
- }
9
+ * Prefixes document.xml declares whether or not the body uses them.
10
+ *
11
+ * A canonical `<w:sdtPr>` legitimately carries `<w16sdtdh:dataHash>` /
12
+ * `<w16cex:*>` / `<w16cid:*>` children the parser stores opaquely, and the
13
+ * drawing prefixes host raw-replayed shapes, so the part keeps the shape Word
14
+ * writes even when this particular save emits none of them.
15
+ */
16
+ const DOCUMENT_BASELINE_PREFIXES = [
17
+ "a",
18
+ "wpc",
19
+ "mc",
20
+ "o",
21
+ "pic",
22
+ "r",
23
+ "m",
24
+ "v",
25
+ "wp14",
26
+ "wp",
27
+ "w10",
28
+ "w",
29
+ "w14",
30
+ "w15",
31
+ "w16",
32
+ "w16cex",
33
+ "w16cid",
34
+ "w16sdtdh",
35
+ "w16se",
36
+ "wpg",
37
+ "wps"
38
+ ];
76
39
  /**
77
40
  * Serialize a single block content item (paragraph, table, or block-level SDT).
78
41
  */
@@ -103,19 +66,20 @@ function serializeDocumentBody(body) {
103
66
  * Serialize a complete Document to valid document.xml
104
67
  *
105
68
  * @param doc - The document to serialize
69
+ * @param sourceBindings - Root `xmlns:*` of the part being replaced, so a
70
+ * prefix only the source document bound keeps its URI
106
71
  * @returns Complete XML string for document.xml
107
72
  */
108
- function serializeDocument(doc) {
73
+ function serializeDocument(doc, sourceBindings) {
109
74
  resetAutoIdCounter();
110
- const parts = [];
111
- parts.push("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
112
- const nsDecl = buildNamespaceDeclarations();
113
- parts.push(`<w:document ${nsDecl} mc:Ignorable="w14 w15 wp14">`);
114
- parts.push("<w:body>");
115
- parts.push(serializeDocumentBody(doc.package.document));
116
- parts.push("</w:body>");
117
- parts.push("</w:document>");
118
- return parts.join("");
75
+ const body = `<w:body>${serializeDocumentBody(doc.package.document)}</w:body>`;
76
+ return "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + serializePartElement({
77
+ partPath: "word/document.xml",
78
+ rootName: "w:document",
79
+ baselinePrefixes: DOCUMENT_BASELINE_PREFIXES,
80
+ sourceBindings,
81
+ body
82
+ });
119
83
  }
120
84
  /**
121
85
  * Serialize just the document body (useful for partial updates)
@@ -1,10 +1,13 @@
1
+ import { serializePartElement } from "./partNamespaces.js";
1
2
  import { escapeXml } from "./xmlUtils.js";
2
3
  //#region src/docx/serializer/fontTableSerializer.ts
3
- const W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
4
- const serializeFontTableXml = (fontTable) => {
5
- const fonts = fontTable.fonts.map(serializeFont).join("");
6
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w:fonts xmlns:w="${W_NS}">${fonts}</w:fonts>`;
7
- };
4
+ const serializeFontTableXml = (fontTable) => "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" + serializePartElement({
5
+ partPath: "word/fontTable.xml",
6
+ rootName: "w:fonts",
7
+ baselinePrefixes: ["w"],
8
+ sourceBindings: void 0,
9
+ body: fontTable.fonts.map(serializeFont).join("")
10
+ });
8
11
  const serializeFont = (font) => {
9
12
  const parts = [];
10
13
  if (font.altName) parts.push(`<w:altName w:val="${escapeXml(font.altName)}"/>`);
@@ -1,11 +1,14 @@
1
1
  import { document_d_exports } from "../../types/document.js";
2
+ import { SourcePart } from "./partNamespaces.js";
2
3
  //#region src/docx/serializer/headerFooterSerializer.d.ts
3
4
  /**
4
5
  * Serialize a HeaderFooter object to valid OOXML XML
5
6
  *
6
7
  * @param hf - HeaderFooter object to serialize
8
+ * @param source - The part being replaced, so a prefix only the source
9
+ * document bound keeps its URI
7
10
  * @returns Complete XML string for header*.xml or footer*.xml
8
11
  */
9
- declare function serializeHeaderFooter(hf: document_d_exports.HeaderFooter): string;
12
+ declare function serializeHeaderFooter(hf: document_d_exports.HeaderFooter, source?: SourcePart): string;
10
13
  //#endregion
11
14
  export { serializeHeaderFooter };
@@ -1,35 +1,36 @@
1
1
  import { canReplayHeaderFooterVerbatim, getHeaderFooterVerbatimXml } from "../headerFooterVerbatim.js";
2
+ import { isEmptyParagraph } from "../paragraphParser.js";
3
+ import { captureVerbatimXml } from "../verbatimCapture.js";
4
+ import { getLocalName, parseXmlDocument } from "../xmlParser.js";
2
5
  import { serializeBlockSdt } from "./blockSdtSerializer.js";
3
6
  import { serializeParagraph } from "./paragraphSerializer.js";
7
+ import { serializePartElement } from "./partNamespaces.js";
4
8
  import { serializeTable } from "./tableSerializer.js";
5
9
  import { escapeXml } from "./xmlUtils.js";
6
10
  //#region src/docx/serializer/headerFooterSerializer.ts
7
- const NAMESPACES = {
8
- wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
9
- mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
10
- o: "urn:schemas-microsoft-com:office:office",
11
- r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
12
- m: "http://schemas.openxmlformats.org/officeDocument/2006/math",
13
- v: "urn:schemas-microsoft-com:vml",
14
- a: "http://schemas.openxmlformats.org/drawingml/2006/main",
15
- pic: "http://schemas.openxmlformats.org/drawingml/2006/picture",
16
- wp14: "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
17
- wp: "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
18
- w10: "urn:schemas-microsoft-com:office:word",
19
- w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
20
- w14: "http://schemas.microsoft.com/office/word/2010/wordml",
21
- w15: "http://schemas.microsoft.com/office/word/2012/wordml",
22
- w16: "http://schemas.microsoft.com/office/word/2018/wordml",
23
- w16cex: "http://schemas.microsoft.com/office/word/2018/wordml/cex",
24
- w16cid: "http://schemas.microsoft.com/office/word/2016/wordml/cid",
25
- w16sdtdh: "http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash",
26
- w16se: "http://schemas.microsoft.com/office/word/2015/wordml/symex",
27
- wpg: "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
28
- wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape"
29
- };
30
- function buildNamespaceDeclarations() {
31
- return Object.entries(NAMESPACES).map(([prefix, uri]) => `xmlns:${prefix}="${uri}"`).join(" ");
32
- }
11
+ const HEADER_FOOTER_BASELINE_PREFIXES = [
12
+ "wpc",
13
+ "mc",
14
+ "o",
15
+ "r",
16
+ "m",
17
+ "v",
18
+ "a",
19
+ "pic",
20
+ "wp14",
21
+ "wp",
22
+ "w10",
23
+ "w",
24
+ "w14",
25
+ "w15",
26
+ "w16",
27
+ "w16cex",
28
+ "w16cid",
29
+ "w16sdtdh",
30
+ "w16se",
31
+ "wpg",
32
+ "wps"
33
+ ];
33
34
  /**
34
35
  * Serialize a block content item (paragraph, table, or block-level SDT) for
35
36
  * header/footer.
@@ -43,21 +44,44 @@ function serializeBlock(block) {
43
44
  * Serialize a HeaderFooter object to valid OOXML XML
44
45
  *
45
46
  * @param hf - HeaderFooter object to serialize
47
+ * @param source - The part being replaced, so a prefix only the source
48
+ * document bound keeps its URI
46
49
  * @returns Complete XML string for header*.xml or footer*.xml
47
50
  */
48
- function serializeHeaderFooter(hf) {
51
+ function serializeHeaderFooter(hf, source) {
49
52
  const verbatim = getHeaderFooterVerbatimXml(hf);
50
53
  if (verbatim && canReplayHeaderFooterVerbatim(hf)) return verbatim;
51
54
  const rootTag = hf.type === "header" ? "w:hdr" : "w:ftr";
52
- const nsDecl = buildNamespaceDeclarations();
53
55
  const watermarkXml = serializeWatermarkParagraph(hf);
54
56
  const watermarkInsertIndex = hf.watermarkBlockIndex !== void 0 ? Math.max(0, Math.min(hf.watermarkBlockIndex, hf.content.length)) : 0;
55
57
  const blocksXml = hf.content.map((block) => serializeBlock(block));
56
58
  let contentXml;
57
- if (watermarkXml) contentXml = blocksXml.slice(0, watermarkInsertIndex).join("") + watermarkXml + blocksXml.slice(watermarkInsertIndex).join("");
58
- else contentXml = blocksXml.join("");
59
+ if (watermarkXml) {
60
+ const watermarkHost = hf.content.at(watermarkInsertIndex);
61
+ const rawWatermarkXml = hf.rawWatermarkXml;
62
+ const mergesIntoRetainedHost = rawWatermarkXml !== void 0 && watermarkHost?.type === "paragraph" && isEmptyParagraph(watermarkHost);
63
+ if (mergesIntoRetainedHost) blocksXml[watermarkInsertIndex] = serializeRawWatermarkIntoHost({
64
+ hostXml: blocksXml[watermarkInsertIndex] ?? serializeParagraph(watermarkHost),
65
+ rawWatermarkXml
66
+ });
67
+ contentXml = blocksXml.slice(0, watermarkInsertIndex).join("") + (mergesIntoRetainedHost ? "" : watermarkXml) + blocksXml.slice(watermarkInsertIndex).join("");
68
+ } else contentXml = blocksXml.join("");
59
69
  if (!contentXml) contentXml = "<w:p><w:pPr/></w:p>";
60
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<${rootTag} ${nsDecl}>${contentXml}</${rootTag}>`;
70
+ return "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" + serializePartElement({
71
+ partPath: source?.path ?? `word/${hf.type}.xml`,
72
+ rootName: rootTag,
73
+ baselinePrefixes: HEADER_FOOTER_BASELINE_PREFIXES,
74
+ sourceBindings: source?.bindings,
75
+ body: contentXml
76
+ });
77
+ }
78
+ function serializeRawWatermarkIntoHost({ hostXml, rawWatermarkXml }) {
79
+ const rawHost = parseXmlDocument(rawWatermarkXml);
80
+ if (!rawHost || getLocalName(rawHost.name ?? "") !== "p") return rawWatermarkXml;
81
+ const watermarkContent = (rawHost.elements ?? []).filter((child) => child.type === "element" && getLocalName(child.name ?? "") !== "pPr").map((child) => captureVerbatimXml(child)).join("");
82
+ if (!watermarkContent) return rawWatermarkXml;
83
+ const namespaceAttributes = Object.entries(rawHost.attributes ?? {}).filter(([name, value]) => value !== void 0 && (name === "xmlns" || name.startsWith("xmlns:"))).map(([name, value]) => ` ${name}="${escapeXml(String(value))}"`).join("");
84
+ return hostXml.replace(/^<w:p(?=[\s>])/u, `<w:p${namespaceAttributes}`).replace("</w:p>", `${watermarkContent}</w:p>`);
61
85
  }
62
86
  function serializeWatermarkParagraph(hf) {
63
87
  if (hf.rawWatermarkXml) return hf.rawWatermarkXml;
@@ -1,33 +1,38 @@
1
1
  import { serializeBlockSdt } from "./blockSdtSerializer.js";
2
2
  import { serializeParagraph } from "./paragraphSerializer.js";
3
+ import { serializePartElement } from "./partNamespaces.js";
3
4
  import { serializeTable } from "./tableSerializer.js";
4
5
  //#region src/docx/serializer/noteSerializer.ts
5
- const NAMESPACES = {
6
- wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
7
- mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
8
- o: "urn:schemas-microsoft-com:office:office",
9
- r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
10
- m: "http://schemas.openxmlformats.org/officeDocument/2006/math",
11
- v: "urn:schemas-microsoft-com:vml",
12
- a: "http://schemas.openxmlformats.org/drawingml/2006/main",
13
- pic: "http://schemas.openxmlformats.org/drawingml/2006/picture",
14
- wp14: "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
15
- wp: "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
16
- w10: "urn:schemas-microsoft-com:office:word",
17
- w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
18
- w14: "http://schemas.microsoft.com/office/word/2010/wordml",
19
- w15: "http://schemas.microsoft.com/office/word/2012/wordml",
20
- w16: "http://schemas.microsoft.com/office/word/2018/wordml",
21
- w16cex: "http://schemas.microsoft.com/office/word/2018/wordml/cex",
22
- w16cid: "http://schemas.microsoft.com/office/word/2016/wordml/cid",
23
- w16sdtdh: "http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash",
24
- w16se: "http://schemas.microsoft.com/office/word/2015/wordml/symex",
25
- wpg: "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
26
- wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape"
27
- };
28
- function buildNamespaceDeclarations() {
29
- return Object.entries(NAMESPACES).map(([prefix, uri]) => `xmlns:${prefix}="${uri}"`).join(" ");
30
- }
6
+ const NOTE_BASELINE_PREFIXES = [
7
+ "wpc",
8
+ "mc",
9
+ "o",
10
+ "r",
11
+ "m",
12
+ "v",
13
+ "a",
14
+ "pic",
15
+ "wp14",
16
+ "wp",
17
+ "w10",
18
+ "w",
19
+ "w14",
20
+ "w15",
21
+ "w16",
22
+ "w16cex",
23
+ "w16cid",
24
+ "w16sdtdh",
25
+ "w16se",
26
+ "wpg",
27
+ "wps"
28
+ ];
29
+ const serializeNotePart = (elementName, body) => "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" + serializePartElement({
30
+ partPath: `word/${elementName}s.xml`,
31
+ rootName: `w:${elementName}s`,
32
+ baselinePrefixes: NOTE_BASELINE_PREFIXES,
33
+ sourceBindings: void 0,
34
+ body
35
+ });
31
36
  /**
32
37
  * Serialize a block content item (paragraph, table, or block-level SDT) for a
33
38
  * note body.
@@ -63,9 +68,8 @@ function insertNoteReferenceMark(xml, elementName) {
63
68
  return `${xml.slice(0, insertAt)}${referenceRun}${xml.slice(insertAt)}`;
64
69
  }
65
70
  function serializeNewNotePart(elementName, notes) {
66
- const nsDecl = buildNamespaceDeclarations();
67
71
  const serializedNotes = notes.map((note) => insertNoteReferenceMark(serializeNote(elementName, note), elementName)).join("");
68
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w:${elementName}s ${nsDecl} mc:Ignorable="w14 w15 wp14">` + serializeRequiredNoteSeparators(elementName) + serializedNotes + `</w:${elementName}s>`;
72
+ return serializeNotePart(elementName, serializeRequiredNoteSeparators(elementName) + serializedNotes);
69
73
  }
70
74
  /**
71
75
  * Serialize footnotes to a complete word/footnotes.xml string.
@@ -74,7 +78,7 @@ function serializeNewNotePart(elementName, notes) {
74
78
  * @returns Complete footnotes.xml string.
75
79
  */
76
80
  function serializeFootnotes(footnotes) {
77
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w:footnotes ${buildNamespaceDeclarations()} mc:Ignorable="w14 w15 wp14">${footnotes.map((fn) => serializeNote("footnote", fn)).join("")}</w:footnotes>`;
81
+ return serializeNotePart("footnote", footnotes.map((fn) => serializeNote("footnote", fn)).join(""));
78
82
  }
79
83
  /**
80
84
  * Serialize endnotes to a complete word/endnotes.xml string.
@@ -83,7 +87,7 @@ function serializeFootnotes(footnotes) {
83
87
  * @returns Complete endnotes.xml string.
84
88
  */
85
89
  function serializeEndnotes(endnotes) {
86
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w:endnotes ${buildNamespaceDeclarations()} mc:Ignorable="w14 w15 wp14">${endnotes.map((en) => serializeNote("endnote", en)).join("")}</w:endnotes>`;
90
+ return serializeNotePart("endnote", endnotes.map((en) => serializeNote("endnote", en)).join(""));
87
91
  }
88
92
  /** Serialize a brand-new footnote part, including Word's required separators
89
93
  * and the automatic reference mark at the start of every normal note. */
@@ -1,7 +1,7 @@
1
+ import { serializePartElement } from "./partNamespaces.js";
1
2
  import { serializeTextFormatting } from "./runSerializer.js";
2
3
  import { escapeXml, intAttr } from "./xmlUtils.js";
3
4
  //#region src/docx/serializer/numberingSerializer.ts
4
- const W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
5
5
  /**
6
6
  * Serialize a level's paragraph properties — the modeled subset is indentation
7
7
  * plus tab stops (see `parseLevelParagraphProps`). Returns "" when neither is
@@ -87,9 +87,13 @@ function serializeNum(instance) {
87
87
  * change detection and per-definition splicing, never to overwrite the part.
88
88
  */
89
89
  function serializeNumberingXml(numbering) {
90
- const abstractNums = numbering.abstractNums.map(serializeAbstractNum).join("");
91
- const nums = numbering.nums.map(serializeNum).join("");
92
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w:numbering xmlns:w="${W_NS}">${abstractNums}${nums}</w:numbering>`;
90
+ return "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" + serializePartElement({
91
+ partPath: "word/numbering.xml",
92
+ rootName: "w:numbering",
93
+ baselinePrefixes: ["w"],
94
+ sourceBindings: void 0,
95
+ body: `${numbering.abstractNums.map(serializeAbstractNum).join("")}${numbering.nums.map(serializeNum).join("")}`
96
+ });
93
97
  }
94
98
  //#endregion
95
99
  export { serializeNumberingXml };
@@ -1,6 +1,6 @@
1
1
  import { document_d_exports } from "../../types/document.js";
2
2
  //#region src/docx/serializer/paragraphSerializer.d.ts
3
- declare function serializeParagraphFormatting(formatting: document_d_exports.ParagraphFormatting | undefined, propertyChanges?: document_d_exports.ParagraphPropertyChange[], pPrMark?: document_d_exports.ParagraphMarkChange): string;
3
+ declare function serializeParagraphFormatting(formatting: document_d_exports.ParagraphFormatting | undefined, propertyChanges?: document_d_exports.ParagraphPropertyChange[], paragraphMarkChange?: document_d_exports.ParagraphMarkChange): string;
4
4
  /**
5
5
  * Serialize a paragraph to OOXML XML (w:p)
6
6
  *
@@ -140,7 +140,7 @@ function serializeParagraphMarkChange(mark) {
140
140
  const attrs = serializeTrackedChangeAttrs(mark.info);
141
141
  return `<w:${mark.kind} ${attrs}/>`;
142
142
  }
143
- function serializeParagraphFormatting(formatting, propertyChanges, pPrMark) {
143
+ const serializeParagraphFormattingWithOptions = (formatting, { propertyChanges, paragraphMarkChange, sectionProperties } = {}) => {
144
144
  const parts = [];
145
145
  const pushToggle = (name, value) => {
146
146
  if (value === true) parts.push(`<w:${name}/>`);
@@ -150,12 +150,10 @@ function serializeParagraphFormatting(formatting, propertyChanges, pPrMark) {
150
150
  if (formatting.styleId) parts.push(`<w:pStyle w:val="${escapeXml(formatting.styleId)}"/>`);
151
151
  pushToggle("keepNext", formatting.keepNext);
152
152
  pushToggle("keepLines", formatting.keepLines);
153
- pushToggle("contextualSpacing", formatting.contextualSpacing);
154
153
  pushToggle("pageBreakBefore", formatting.pageBreakBefore);
155
154
  const frameXml = serializeFrameProperties(formatting.frame);
156
155
  if (frameXml) parts.push(frameXml);
157
156
  pushToggle("widowControl", formatting.widowControl);
158
- pushToggle("snapToGrid", formatting.snapToGrid);
159
157
  const numPrXml = formatting.numPrFromStyle != null && numPrEqual(formatting.numPr, formatting.numPrFromStyle) ? "" : serializeNumbering(formatting.numPr);
160
158
  if (numPrXml) parts.push(numPrXml);
161
159
  const bordersXml = serializeParagraphBorders(formatting.borders);
@@ -168,21 +166,31 @@ function serializeParagraphFormatting(formatting, propertyChanges, pPrMark) {
168
166
  pushToggle("suppressAutoHyphens", formatting.suppressAutoHyphens);
169
167
  pushToggle("kinsoku", formatting.kinsoku);
170
168
  pushToggle("overflowPunct", formatting.overflowPunctuation);
169
+ pushToggle("bidi", formatting.bidi);
170
+ pushToggle("snapToGrid", formatting.snapToGrid);
171
171
  const spacingXml = serializeSpacing(formatting);
172
172
  if (spacingXml) parts.push(spacingXml);
173
173
  const indXml = serializeIndentation(formatting);
174
174
  if (indXml) parts.push(indXml);
175
- pushToggle("bidi", formatting.bidi);
175
+ pushToggle("contextualSpacing", formatting.contextualSpacing);
176
176
  if (formatting.alignment) parts.push(`<w:jc w:val="${formatting.alignment}"/>`);
177
177
  if (formatting.outlineLevel !== void 0) parts.push(`<w:outlineLvl w:val="${formatting.outlineLevel}"/>`);
178
- if (pPrMark || formatting.runProperties || formatting.runInWithNext) {
179
- const fullInner = `${pPrMark ? serializeParagraphMarkChange(pPrMark) : ""}${formatting.runProperties ? extractRPrInner(serializeTextFormatting(formatting.runProperties)) : ""}${formatting.runInWithNext ? "<w:specVanish/>" : ""}`;
178
+ if (paragraphMarkChange || formatting.runProperties || formatting.runInWithNext) {
179
+ const fullInner = `${paragraphMarkChange ? serializeParagraphMarkChange(paragraphMarkChange) : ""}${formatting.runProperties ? extractRPrInner(serializeTextFormatting(formatting.runProperties)) : ""}${formatting.runInWithNext ? "<w:specVanish/>" : ""}`;
180
180
  if (fullInner.length > 0) parts.push(`<w:rPr>${fullInner}</w:rPr>`);
181
181
  }
182
- } else if (pPrMark) parts.push(`<w:rPr>${serializeParagraphMarkChange(pPrMark)}</w:rPr>`);
182
+ } else if (paragraphMarkChange) parts.push(`<w:rPr>${serializeParagraphMarkChange(paragraphMarkChange)}</w:rPr>`);
183
+ parts.push(serializeSectionProperties(sectionProperties));
183
184
  if (propertyChanges && propertyChanges.length > 0) parts.push(...propertyChanges.map((change) => serializeParagraphPropertyChange(change)));
184
- if (parts.length === 0) return "";
185
- return `<w:pPr>${parts.join("")}</w:pPr>`;
185
+ const inner = parts.join("");
186
+ if (inner.length === 0) return "";
187
+ return `<w:pPr>${inner}</w:pPr>`;
188
+ };
189
+ function serializeParagraphFormatting(formatting, propertyChanges, paragraphMarkChange) {
190
+ return serializeParagraphFormattingWithOptions(formatting, {
191
+ propertyChanges,
192
+ paragraphMarkChange
193
+ });
186
194
  }
187
195
  function extractPPrInner(pPrXml) {
188
196
  if (!pPrXml.startsWith("<w:pPr>") || !pPrXml.endsWith("</w:pPr>")) return "";
@@ -486,9 +494,11 @@ function serializeParagraph(paragraph) {
486
494
  if (paragraph.paraId) attrs.push(`w14:paraId="${escapeXml(paragraph.paraId)}"`);
487
495
  if (paragraph.textId) attrs.push(`w14:textId="${escapeXml(paragraph.textId)}"`);
488
496
  const attrsStr = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
489
- const pPrXml = serializeParagraphFormatting(paragraph.formatting, paragraph.propertyChanges, paragraph.pPrMark);
490
- const sectionPropertiesXml = serializeSectionProperties(paragraph.sectionProperties);
491
- if (pPrXml || sectionPropertiesXml) parts.push(`<w:pPr>${extractPPrInner(pPrXml)}${sectionPropertiesXml}</w:pPr>`);
497
+ parts.push(serializeParagraphFormattingWithOptions(paragraph.formatting, {
498
+ propertyChanges: paragraph.propertyChanges,
499
+ paragraphMarkChange: paragraph.pPrMark,
500
+ sectionProperties: paragraph.sectionProperties
501
+ }));
492
502
  const explicitCommentReferenceIds = /* @__PURE__ */ new Set();
493
503
  for (const content of paragraph.content) if (content.type === "commentReference") explicitCommentReferenceIds.add(content.id);
494
504
  let pendingRenderedPageBreak = paragraph.renderedPageBreakBefore === true;