reamkit 1.30.0 → 1.32.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 (147) hide show
  1. package/README.md +70 -24
  2. package/dist/esm/core/bidi/index.d.ts +10 -0
  3. package/dist/esm/core/bidi/index.js +18 -2
  4. package/dist/esm/core/converter/facade.js +10 -5
  5. package/dist/esm/core/converter/project.js +4 -1
  6. package/dist/esm/core/converter/ream.js +17 -6
  7. package/dist/esm/core/converter/sheet-images.d.ts +17 -0
  8. package/dist/esm/core/converter/sheet-images.js +33 -0
  9. package/dist/esm/core/crypto/offcrypto.js +1 -1
  10. package/dist/esm/core/document-model/index.d.ts +1 -1
  11. package/dist/esm/core/document-model/types.d.ts +175 -0
  12. package/dist/esm/core/drawingml/chart-geometry.d.ts +21 -8
  13. package/dist/esm/core/drawingml/chart-geometry.js +198 -108
  14. package/dist/esm/core/drawingml/chart-parser.js +73 -2
  15. package/dist/esm/core/drawingml/colors.d.ts +6 -0
  16. package/dist/esm/core/drawingml/colors.js +23 -3
  17. package/dist/esm/core/drawingml/shape-render.d.ts +3 -1
  18. package/dist/esm/core/drawingml/shape-render.js +4 -2
  19. package/dist/esm/core/font/index.d.ts +2 -0
  20. package/dist/esm/core/font/measure.d.ts +9 -0
  21. package/dist/esm/core/font/measure.js +9 -1
  22. package/dist/esm/core/font/ttf-build.d.ts +113 -0
  23. package/dist/esm/core/font/ttf-build.js +1224 -0
  24. package/dist/esm/core/font/ttf-subset.d.ts +19 -0
  25. package/dist/esm/core/font/ttf-subset.js +14 -1
  26. package/dist/esm/core/fonts/families.js +4 -2
  27. package/dist/esm/core/fonts/provider.d.ts +17 -0
  28. package/dist/esm/core/fonts/provider.js +27 -2
  29. package/dist/esm/core/fonts/remote-fonts.js +8 -1
  30. package/dist/esm/core/fonts/scripts.js +4 -2
  31. package/dist/esm/core/ir/flow.d.ts +77 -1
  32. package/dist/esm/core/ir/sheet.d.ts +43 -0
  33. package/dist/esm/core/number-format.js +16 -11
  34. package/dist/esm/core/numbering/index.d.ts +1 -1
  35. package/dist/esm/core/numbering/state.d.ts +11 -1
  36. package/dist/esm/core/numbering/state.js +10 -1
  37. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  38. package/dist/esm/core/spreadsheet-model/types.d.ts +96 -0
  39. package/dist/esm/core/style-cascade/resolver.js +39 -6
  40. package/dist/esm/core/style-cascade/types.d.ts +19 -1
  41. package/dist/esm/core/style-cascade/types.js +3 -0
  42. package/dist/esm/excel/column-bands.d.ts +39 -6
  43. package/dist/esm/excel/column-bands.js +41 -17
  44. package/dist/esm/excel/comments-parser.js +19 -0
  45. package/dist/esm/excel/conditional-format.d.ts +7 -3
  46. package/dist/esm/excel/conditional-format.js +77 -39
  47. package/dist/esm/excel/formula/context.d.ts +18 -0
  48. package/dist/esm/excel/formula/eval.js +51 -0
  49. package/dist/esm/excel/formula/index.d.ts +2 -2
  50. package/dist/esm/excel/formula/lexer.d.ts +1 -1
  51. package/dist/esm/excel/formula/lexer.js +41 -0
  52. package/dist/esm/excel/formula/parser.d.ts +25 -0
  53. package/dist/esm/excel/formula/parser.js +95 -0
  54. package/dist/esm/excel/print-model.d.ts +38 -1
  55. package/dist/esm/excel/print-model.js +522 -80
  56. package/dist/esm/excel/shared-strings-parser.d.ts +9 -0
  57. package/dist/esm/excel/shared-strings-parser.js +11 -1
  58. package/dist/esm/excel/sheet-drawing.d.ts +38 -2
  59. package/dist/esm/excel/sheet-drawing.js +47 -13
  60. package/dist/esm/excel/sheet-shape-parser.d.ts +6 -2
  61. package/dist/esm/excel/sheet-shape-parser.js +18 -6
  62. package/dist/esm/excel/sheet-to-flow.d.ts +17 -0
  63. package/dist/esm/excel/sheet-to-flow.js +434 -47
  64. package/dist/esm/excel/styles-parser.d.ts +9 -0
  65. package/dist/esm/excel/styles-parser.js +80 -26
  66. package/dist/esm/excel/table-parser.js +5 -0
  67. package/dist/esm/excel/table-style-presets.d.ts +26 -0
  68. package/dist/esm/excel/table-style-presets.js +1555 -0
  69. package/dist/esm/excel/table-style.d.ts +13 -0
  70. package/dist/esm/excel/table-style.js +75 -0
  71. package/dist/esm/excel/tint.d.ts +10 -0
  72. package/dist/esm/excel/tint.js +75 -0
  73. package/dist/esm/excel/vml-drawing.d.ts +41 -3
  74. package/dist/esm/excel/vml-drawing.js +143 -10
  75. package/dist/esm/excel/worksheet-parser.js +89 -15
  76. package/dist/esm/excel/xls/biff-reader.js +10 -2
  77. package/dist/esm/excel/xlsx-reader.js +195 -14
  78. package/dist/esm/excel/xlsx-writer.js +8 -7
  79. package/dist/esm/html/html-writer.js +202 -20
  80. package/dist/esm/index.d.ts +2 -2
  81. package/dist/esm/layout/fit-pages.d.ts +11 -0
  82. package/dist/esm/layout/fit-pages.js +86 -0
  83. package/dist/esm/layout/line-paint.d.ts +58 -0
  84. package/dist/esm/layout/line-paint.js +107 -0
  85. package/dist/esm/layout/page-doc.d.ts +61 -3
  86. package/dist/esm/layout/page-doc.js +141 -1
  87. package/dist/esm/layout/styled-layout.d.ts +55 -7
  88. package/dist/esm/layout/styled-layout.js +1154 -169
  89. package/dist/esm/layout/turned-section.d.ts +38 -0
  90. package/dist/esm/layout/turned-section.js +205 -0
  91. package/dist/esm/markdown/markdown-writer.js +17 -6
  92. package/dist/esm/pdf/styled-page-emitter.js +114 -60
  93. package/dist/esm/pdf/styled-page-renderer.js +1 -1
  94. package/dist/esm/pdf-reader/cff-outline.d.ts +27 -0
  95. package/dist/esm/pdf-reader/cff-outline.js +169 -21
  96. package/dist/esm/pdf-reader/content.d.ts +53 -0
  97. package/dist/esm/pdf-reader/content.js +9 -1
  98. package/dist/esm/pdf-reader/display.d.ts +36 -0
  99. package/dist/esm/pdf-reader/display.js +66 -1
  100. package/dist/esm/pdf-reader/document.js +5 -1
  101. package/dist/esm/pdf-reader/embedded-fonts.d.ts +2 -1
  102. package/dist/esm/pdf-reader/embedded-fonts.js +32 -2
  103. package/dist/esm/pdf-reader/encodings.d.ts +8 -0
  104. package/dist/esm/pdf-reader/encodings.js +25 -3
  105. package/dist/esm/pdf-reader/face-outlines.d.ts +78 -0
  106. package/dist/esm/pdf-reader/face-outlines.js +362 -0
  107. package/dist/esm/pdf-reader/figures.d.ts +52 -0
  108. package/dist/esm/pdf-reader/figures.js +433 -0
  109. package/dist/esm/pdf-reader/flow-build.d.ts +131 -5
  110. package/dist/esm/pdf-reader/flow-build.js +345 -26
  111. package/dist/esm/pdf-reader/font.js +321 -36
  112. package/dist/esm/pdf-reader/glyf-outline.d.ts +33 -0
  113. package/dist/esm/pdf-reader/glyf-outline.js +135 -1
  114. package/dist/esm/pdf-reader/glyph-names.js +154 -1
  115. package/dist/esm/pdf-reader/layout.d.ts +93 -0
  116. package/dist/esm/pdf-reader/layout.js +1703 -214
  117. package/dist/esm/pdf-reader/page-numbers.d.ts +53 -0
  118. package/dist/esm/pdf-reader/page-numbers.js +167 -0
  119. package/dist/esm/pdf-reader/tagged.js +182 -23
  120. package/dist/esm/pdf-reader/text.d.ts +6 -3
  121. package/dist/esm/pdf-reader/text.js +98 -9
  122. package/dist/esm/pdf-reader/type1-outline.d.ts +11 -0
  123. package/dist/esm/pdf-reader/type1-outline.js +63 -8
  124. package/dist/esm/pdf-reader/vector.js +71 -1
  125. package/dist/esm/svg/svg-text.d.ts +58 -0
  126. package/dist/esm/svg/svg-text.js +322 -0
  127. package/dist/esm/svg/svg-writer.js +135 -71
  128. package/dist/esm/word/doc/doc-reader.js +6 -2
  129. package/dist/esm/word/doc/doc-text.d.ts +6 -0
  130. package/dist/esm/word/doc/doc-text.js +19 -1
  131. package/dist/esm/word/document-parser.d.ts +2 -2
  132. package/dist/esm/word/document-parser.js +11 -1
  133. package/dist/esm/word/docx-reader.js +5 -3
  134. package/dist/esm/word/docx-writer.js +218 -23
  135. package/dist/esm/word/drawing-parser.d.ts +5 -3
  136. package/dist/esm/word/drawing-parser.js +49 -8
  137. package/dist/esm/word/font-embed.d.ts +30 -0
  138. package/dist/esm/word/font-embed.js +173 -0
  139. package/dist/esm/word/font-table.d.ts +10 -0
  140. package/dist/esm/word/font-table.js +13 -1
  141. package/dist/esm/word/index.js +1 -1
  142. package/dist/esm/word/numbering-parser.d.ts +3 -1
  143. package/dist/esm/word/numbering-parser.js +2 -1
  144. package/dist/esm/word/paragraph-properties.d.ts +7 -6
  145. package/dist/esm/word/paragraph-properties.js +14 -2
  146. package/dist/esm/word/run-properties.js +26 -0
  147. package/package.json +12 -3
@@ -40,6 +40,9 @@ var SPRM_P_DYA_BEFORE = 42003;
40
40
  var SPRM_P_DYA_AFTER = 42004;
41
41
  var SPRM_P_F_IN_TABLE = 9238;
42
42
  var SPRM_P_F_TTP = 9239;
43
+ var SPRM_P_F_WIDOW_CONTROL = 9265;
44
+ var SPRM_P_F_KEEP = 9221;
45
+ var SPRM_P_F_KEEP_FOLLOW = 9222;
43
46
  var SPRM_T_DEF_TABLE = 54792;
44
47
  var SPRM_T_DEF_TABLE_SHD = 54802;
45
48
  var MAX_ROW_CELLS = 256;
@@ -433,6 +436,9 @@ function decodePapxGrpprl(d) {
433
436
  let cellShadings;
434
437
  let listIlfo;
435
438
  let listIlvl;
439
+ let widowControl;
440
+ let keepNext;
441
+ let keepLines;
436
442
  for (const s of sprms(d)) switch (s.sprm) {
437
443
  case SPRM_P_JC:
438
444
  jc = d[s.op];
@@ -458,6 +464,15 @@ function decodePapxGrpprl(d) {
458
464
  case SPRM_P_F_TTP:
459
465
  rowEnd = d[s.op] !== 0;
460
466
  break;
467
+ case SPRM_P_F_WIDOW_CONTROL:
468
+ widowControl = d[s.op] !== 0;
469
+ break;
470
+ case SPRM_P_F_KEEP_FOLLOW:
471
+ keepNext = d[s.op] !== 0;
472
+ break;
473
+ case SPRM_P_F_KEEP:
474
+ keepLines = d[s.op] !== 0;
475
+ break;
461
476
  case SPRM_T_DEF_TABLE: {
462
477
  const cols = d[s.op] ?? 0;
463
478
  const edges = [];
@@ -498,7 +513,10 @@ function decodePapxGrpprl(d) {
498
513
  ...cellDescriptors ? { cellDescriptors } : {},
499
514
  ...cellShadings ? { cellShadings } : {},
500
515
  ...listIlfo !== void 0 ? { listIlfo } : {},
501
- ...listIlvl !== void 0 ? { listIlvl } : {}
516
+ ...listIlvl !== void 0 ? { listIlvl } : {},
517
+ ...widowControl !== void 0 ? { widowControl } : {},
518
+ ...keepNext !== void 0 ? { keepNext } : {},
519
+ ...keepLines !== void 0 ? { keepLines } : {}
502
520
  };
503
521
  }
504
522
  function* sprms(d) {
@@ -80,8 +80,8 @@ export interface ParseContext {
80
80
  readonly openCommentRanges?: Set<string>;
81
81
  /**
82
82
  * §17.3.1.1/§17.3.1.3 — what `w:beforeAutospacing`/`w:afterAutospacing`
83
- * resolve to for THIS document: 14pt when it states no compatibility mode,
84
- * and nothing (the default) for every Word 2007-or-later one.
83
+ * resolve to: HTML's 14pt for a Word document, nothing (the default) where
84
+ * the caller gives none.
85
85
  */
86
86
  readonly autoSpacingPt?: number;
87
87
  }
@@ -9,6 +9,7 @@ import { parseRunProperties } from "./run-properties.js";
9
9
  import { parseParagraphProperties } from "./paragraph-properties.js";
10
10
  import { poElementToFlat } from "./po-to-flat.js";
11
11
  import { parseBorders, parseTable } from "./table-parser.js";
12
+ import { FORMATS } from "./numbering-parser.js";
12
13
  import { XMLParser } from "fast-xml-parser";
13
14
  //#region src/word/document-parser.ts
14
15
  var decoder = new TextDecoder("utf-8");
@@ -199,11 +200,13 @@ function parseSectPrNode(sectPr) {
199
200
  let margins;
200
201
  let titlePg = false;
201
202
  let pageNumberStart;
203
+ let pageNumberFormat;
202
204
  let lineNumbering;
203
205
  let columns;
204
206
  let sectionStart;
205
207
  let pageBorders;
206
208
  let gridLinePitchPt;
209
+ let textDirection;
207
210
  const headers = [];
208
211
  const footers = [];
209
212
  for (const child of poChildren(sectPr)) if (poIs(child, "w:pgSz")) {
@@ -251,6 +254,8 @@ function parseSectPrNode(sectPr) {
251
254
  } else if (poIs(child, "w:pgNumType")) {
252
255
  const start = poIntAttr(child, "start");
253
256
  if (start !== void 0) pageNumberStart = start;
257
+ const fmt = poAttr(child, "fmt");
258
+ if (fmt !== void 0 && FORMATS.has(fmt) && fmt !== "decimal") pageNumberFormat = fmt;
254
259
  } else if (poIs(child, "w:cols")) columns = parseColumns(child);
255
260
  else if (poIs(child, "w:pgBorders")) {
256
261
  const borders = parseBorders(child);
@@ -265,6 +270,9 @@ function parseSectPrNode(sectPr) {
265
270
  } else if (poIs(child, "w:type")) {
266
271
  const val = poAttr(child, "val");
267
272
  sectionStart = val === "continuous" || val === "oddPage" || val === "evenPage" ? val : "nextPage";
273
+ } else if (poIs(child, "w:textDirection")) {
274
+ const val = poAttr(child, "val");
275
+ if (val === "tbRl" || val === "btLr") textDirection = val;
268
276
  }
269
277
  return {
270
278
  pageSize,
@@ -273,11 +281,13 @@ function parseSectPrNode(sectPr) {
273
281
  footers,
274
282
  ...titlePg ? { titlePg: true } : {},
275
283
  ...pageNumberStart !== void 0 ? { pageNumberStart } : {},
284
+ ...pageNumberFormat !== void 0 ? { pageNumberFormat } : {},
276
285
  ...lineNumbering ? { lineNumbering } : {},
277
286
  ...columns ? { columns } : {},
278
287
  ...sectionStart ? { sectionStart } : {},
279
288
  ...pageBorders ? { pageBorders } : {},
280
- ...gridLinePitchPt !== void 0 ? { gridLinePitchPt } : {}
289
+ ...gridLinePitchPt !== void 0 ? { gridLinePitchPt } : {},
290
+ ...textDirection ? { textDirection } : {}
281
291
  };
282
292
  }
283
293
  function parseColumns(cols) {
@@ -15,10 +15,10 @@ import { OpcPackage } from "../core/opc/package.js";
15
15
  import { parseCoreProperties } from "../core/opc/core-properties.js";
16
16
  import "../core/opc/index.js";
17
17
  import { parseXml } from "../pptx/pptx-reader.js";
18
+ import { EMPTY_NUMBERING, parseNumbering } from "./numbering-parser.js";
18
19
  import { EMPTY_SECTION, applyAuthorIds, bodyIndexForBlock, newBlockCounter, parseBackgroundColor, parseBackgroundFill, parseCommentThreads, parseDocument, parseHeaderFooter, parseNotes, parsePeople, parseSections } from "./document-parser.js";
19
20
  import { parseStyles } from "./styles-parser.js";
20
21
  import { loadEmbeddedFonts } from "./font-table.js";
21
- import { EMPTY_NUMBERING, parseNumbering } from "./numbering-parser.js";
22
22
  import { EMPTY_SETTINGS, parseSettings } from "./settings-parser.js";
23
23
  import "./index.js";
24
24
  //#region src/word/docx-reader.ts
@@ -66,7 +66,7 @@ function readDocx(docx) {
66
66
  const settings = settingsData ? parseSettings(settingsData) : EMPTY_SETTINGS;
67
67
  const ctx = {
68
68
  resolveColor,
69
- ...settings.compatibilityMode === void 0 ? { autoSpacingPt: 14 } : {},
69
+ autoSpacingPt: 14,
70
70
  ...themeLineWidths && themeLineWidths.length > 0 ? { themeLineWidths } : {},
71
71
  ...themeStyles ? { themeStyles } : {},
72
72
  ...themeFonts ? { themeFonts } : {},
@@ -142,7 +142,9 @@ function readDocx(docx) {
142
142
  ...settings.doNotExpandShiftReturn ? { doNotExpandShiftReturn: true } : {},
143
143
  ...settings.displayBackgroundShape && backgroundFill ? { pageBackgroundFill: backgroundFill } : {},
144
144
  ...settings.displayBackgroundShape && backgroundColorHex ? { pageBackgroundColorHex: backgroundColorHex } : {},
145
- ...settings.gutterAtTop ? { gutterAtTop: true } : {}
145
+ ...settings.gutterAtTop ? { gutterAtTop: true } : {},
146
+ ...settings.compatibilityMode !== void 0 ? { compatibilityMode: settings.compatibilityMode } : {},
147
+ typesetBy: "word"
146
148
  },
147
149
  losses
148
150
  };
@@ -6,6 +6,7 @@ import { buildOpcPackage } from "../core/opc/opc-writer.js";
6
6
  import "../core/opc/index.js";
7
7
  import { omathXml } from "./omml-serializer.js";
8
8
  import { chartSpaceXml } from "../core/drawingml/chart-serializer.js";
9
+ import { OBFUSCATED_FONT_CONTENT_TYPE, embedFaces } from "./font-embed.js";
9
10
  //#region src/word/docx-writer.ts
10
11
  var encoder = new TextEncoder();
11
12
  var DEFAULT_RUN = resolveRunProperties({}, {}, EMPTY_STYLE_SHEET);
@@ -20,6 +21,12 @@ var NUMBERING_PART = "word/numbering.xml";
20
21
  var REL_FONT_TABLE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable";
21
22
  var FONT_TABLE_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml";
22
23
  var FONT_TABLE_PART = "word/fontTable.xml";
24
+ var REL_SETTINGS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings";
25
+ var SETTINGS_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml";
26
+ var SETTINGS_PART = "word/settings.xml";
27
+ var REL_STYLES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
28
+ var STYLES_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml";
29
+ var STYLES_PART = "word/styles.xml";
23
30
  var REL_FOOTNOTES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes";
24
31
  var REL_ENDNOTES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes";
25
32
  var FOOTNOTES_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml";
@@ -33,6 +40,20 @@ var REL_COMMENTS_EXTENDED = "http://schemas.microsoft.com/office/2011/relationsh
33
40
  var COMMENTS_EXTENDED_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml";
34
41
  var COMMENTS_EXTENDED_PART = "word/commentsExtended.xml";
35
42
  var W14_NS = "http://schemas.microsoft.com/office/word/2010/wordml";
43
+ var MC_NS = "http://schemas.openxmlformats.org/markup-compatibility/2006";
44
+ /**
45
+ * The namespaces a part's root declares: WordprocessingML's and the
46
+ * relationships', and — where the part uses one — Word 2010's `w14`, which a
47
+ * reader that does not know it is told to pass over (ECMA-376 Part 3
48
+ * `mc:Ignorable`) rather than to refuse the file.
49
+ *
50
+ * @param inner The part's content, to see whether it uses `w14`.
51
+ * @returns The attributes, each with its leading space.
52
+ */
53
+ function rootNamespaces(inner) {
54
+ const base = " xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"";
55
+ return inner.includes("<w14:") ? `${base} xmlns:w14="${W14_NS}" xmlns:mc="${MC_NS}" mc:Ignorable="w14"` : base;
56
+ }
36
57
  var W15_NS = "http://schemas.microsoft.com/office/word/2012/wordml";
37
58
  var REL_CHART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart";
38
59
  var CHART_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml";
@@ -133,7 +154,10 @@ function writeDocx(flow) {
133
154
  ...flow.charts ? { charts: flow.charts } : {},
134
155
  ...flow.faceFamilies ? { faceFamilies: flow.faceFamilies } : {},
135
156
  familiesUsed: /* @__PURE__ */ new Map(),
136
- zRanks: zRanksOf(flow)
157
+ ...flow.faceOutlines ? { faceOutlines: flow.faceOutlines } : {},
158
+ facesUsed: /* @__PURE__ */ new Set(),
159
+ zRanks: zRanksOf(flow),
160
+ overlaid: /* @__PURE__ */ new Set()
137
161
  };
138
162
  const docScope = newScope();
139
163
  const extraParts = [];
@@ -153,7 +177,8 @@ function writeDocx(flow) {
153
177
  });
154
178
  emitBody(body, flow.body, losses, state, docScope, sectPrByClosingIndex);
155
179
  if (finalSectPr) body.push(finalSectPr);
156
- const documentXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><w:body>${body.join("")}</w:body></w:document>`;
180
+ const bodyXml = body.join("");
181
+ const documentXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document${rootNamespaces(bodyXml)}><w:body>${bodyXml}</w:body></w:document>`;
157
182
  const numberingPart = flow.body.some((el) => el.kind === "paragraph" && el.paragraph.properties.numbering !== void 0) && flow.numbering ? {
158
183
  path: NUMBERING_PART,
159
184
  data: encoder.encode(numberingXml(flow.numbering)),
@@ -185,9 +210,14 @@ function writeDocx(flow) {
185
210
  }, state, losses, docScope, extraParts, extraPartRels);
186
211
  const commentParaIds = emitComments(flow.comments, state, losses, docScope, extraParts, extraPartRels);
187
212
  emitCommentsExtended(flow.comments, commentParaIds, docScope, extraParts);
213
+ const embedded = state.faceOutlines && state.faceFamilies ? embedFaces(state.facesUsed, state.faceOutlines, state.faceFamilies, losses) : void 0;
214
+ if (embedded && embedded.relationships.length > 0) extraPartRels.push({
215
+ sourcePart: FONT_TABLE_PART,
216
+ relationships: [...embedded.relationships]
217
+ });
188
218
  const fontTablePart = state.familiesUsed.size > 0 ? {
189
219
  path: FONT_TABLE_PART,
190
- data: encoder.encode(fontTableXml(state.familiesUsed)),
220
+ data: encoder.encode(fontTableXml(state.familiesUsed, embedded?.elements)),
191
221
  contentType: FONT_TABLE_CONTENT_TYPE
192
222
  } : void 0;
193
223
  if (fontTablePart) docScope.rels.push({
@@ -196,6 +226,29 @@ function writeDocx(flow) {
196
226
  target: "fontTable.xml",
197
227
  targetMode: "Internal"
198
228
  });
229
+ const settings = settingsXml(flow, sections, (embedded?.parts.length ?? 0) > 0);
230
+ const settingsPart = settings !== void 0 ? {
231
+ path: SETTINGS_PART,
232
+ data: encoder.encode(settings),
233
+ contentType: SETTINGS_CONTENT_TYPE
234
+ } : void 0;
235
+ if (settingsPart) docScope.rels.push({
236
+ id: `rId${++docScope.relSeq}`,
237
+ type: REL_SETTINGS,
238
+ target: "settings.xml",
239
+ targetMode: "Internal"
240
+ });
241
+ const stylesPart = {
242
+ path: STYLES_PART,
243
+ data: encoder.encode(stylesXml()),
244
+ contentType: STYLES_CONTENT_TYPE
245
+ };
246
+ docScope.rels.push({
247
+ id: `rId${++docScope.relSeq}`,
248
+ type: REL_STYLES,
249
+ target: "styles.xml",
250
+ targetMode: "Internal"
251
+ });
199
252
  const partRelationships = [...docScope.rels.length > 0 ? [{
200
253
  sourcePart: "word/document.xml",
201
254
  relationships: docScope.rels
@@ -208,12 +261,16 @@ function writeDocx(flow) {
208
261
  data: encoder.encode(documentXml),
209
262
  contentType: DOC_CONTENT_TYPE
210
263
  },
264
+ stylesPart,
211
265
  ...numberingPart ? [numberingPart] : [],
212
266
  ...fontTablePart ? [fontTablePart] : [],
267
+ ...settingsPart ? [settingsPart] : [],
213
268
  ...extraParts,
214
269
  ...state.chartParts,
215
- ...state.mediaParts
270
+ ...state.mediaParts,
271
+ ...embedded?.parts ?? []
216
272
  ],
273
+ ...(embedded?.parts.length ?? 0) > 0 ? { defaultsByExtension: { odttf: OBFUSCATED_FONT_CONTENT_TYPE } } : {},
217
274
  rootRelationships: [{
218
275
  id: "rId1",
219
276
  type: REL_OFFICE_DOCUMENT,
@@ -225,8 +282,50 @@ function writeDocx(flow) {
225
282
  losses
226
283
  };
227
284
  }
228
- function fontTableXml(families) {
229
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:fonts xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">${[...families.values()].map((f) => `<w:font w:name="${escapeAttr(f.family)}"><w:family w:val="${f.generic}"/><w:pitch w:val="${f.generic === "modern" ? "fixed" : "variable"}"/></w:font>`).join("")}</w:fonts>`;
285
+ /**
286
+ * §17.15.1.78 `w:settings` — the document-wide settings the model carries, in
287
+ * the order CT_Settings declares them: `w:embedTrueTypeFonts` and
288
+ * `w:saveSubsetFonts` (§17.15.1.42, .74) where the package embeds its faces,
289
+ * `w:gutterAtTop` (§17.15.1.49), `w:evenAndOddHeaders` (§17.15.1.36, which a
290
+ * section carries in the model), and inside `w:compat` §17.15.3.4's
291
+ * `w:doNotExpandShiftReturn` before the version of Word the document is laid
292
+ * out for ([MS-DOCX] `w:compatSetting` `compatibilityMode`), which CT_Compat
293
+ * keeps last.
294
+ *
295
+ * @param flow The document.
296
+ * @param sections The sections being written.
297
+ * @param embedsFonts Whether the package embeds fonts — which then stay
298
+ * embedded, as subsets, when the document is saved again.
299
+ * @returns The part's XML, or undefined where the document states none.
300
+ */
301
+ function settingsXml(flow, sections, embedsFonts) {
302
+ const parts = [];
303
+ if (embedsFonts) parts.push("<w:embedTrueTypeFonts/><w:saveSubsetFonts/>");
304
+ if (flow.gutterAtTop === true) parts.push("<w:gutterAtTop/>");
305
+ if (sections.some((sec) => sec.properties.evenAndOddHeaders === true)) parts.push("<w:evenAndOddHeaders/>");
306
+ const compat = [...flow.doNotExpandShiftReturn === true ? ["<w:doNotExpandShiftReturn/>"] : [], ...flow.compatibilityMode !== void 0 ? [`<w:compatSetting w:name="compatibilityMode" w:uri="http://schemas.microsoft.com/office/word" w:val="${String(flow.compatibilityMode)}"/>`] : []];
307
+ if (compat.length > 0) parts.push(`<w:compat>${compat.join("")}</w:compat>`);
308
+ if (parts.length === 0) return void 0;
309
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:settings xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">${parts.join("")}</w:settings>`;
310
+ }
311
+ /**
312
+ * §17.7.2 `w:styles` — what a property the document states nowhere IS: the
313
+ * values the model resolves an empty sheet to (`DEFAULT_RUN`, `DEFAULT_PARA`
314
+ * — the very values a run or paragraph is written without), as §17.7.5
315
+ * `w:docDefaults`, under an empty default paragraph style (§17.7.4.17).
316
+ *
317
+ * A package with no styles part leaves them to its reader, and Word fills
318
+ * them from its own template's Normal: 8pt after every paragraph, lines 1.08
319
+ * apart, runs in 12pt — so every paragraph of a reconstruction stood 8pt
320
+ * further down than its page set it, and a run of the model's own 11pt, which
321
+ * states no size, was set in 12.
322
+ */
323
+ function stylesXml() {
324
+ const size = Math.round(DEFAULT_RUN.fontSizePt * 2);
325
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:docDefaults><w:rPrDefault><w:rPr><w:sz w:val="${size}"/><w:szCs w:val="${size}"/></w:rPr></w:rPrDefault><w:pPrDefault><w:pPr>${DEFAULT_PARA.widowControl ? "<w:widowControl/>" : "<w:widowControl w:val=\"0\"/>"}${`<w:spacing w:before="${twips(DEFAULT_PARA.spacingBefore)}" w:after="${twips(DEFAULT_PARA.spacingAfter)}" w:line="${twips(DEFAULT_PARA.spacingLine)}" w:lineRule="${DEFAULT_PARA.spacingLineRule}"/>`}</w:pPr></w:pPrDefault></w:docDefaults><w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/></w:style></w:styles>`;
326
+ }
327
+ function fontTableXml(families, embeds) {
328
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:fonts xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">${[...families.values()].map((f) => `<w:font w:name="${escapeAttr(f.family)}"><w:family w:val="${f.generic}"/><w:pitch w:val="${f.generic === "modern" ? "fixed" : "variable"}"/>${embeds?.get(f.family) ?? ""}</w:font>`).join("")}</w:fonts>`;
230
329
  }
231
330
  /**
232
331
  * §20.4.2.3 `relativeHeight` — a float's z-order, written the way Word writes
@@ -257,7 +356,10 @@ function zRanksOf(flow) {
257
356
  for (const el of blocks) {
258
357
  const z = el.kind === "image" ? el.image.float?.zOrder : el.kind === "shape" ? el.shape.float?.zOrder : el.kind === "chart" ? el.chart.float?.zOrder : void 0;
259
358
  if (z !== void 0 && Number.isFinite(z)) seen.add(z);
260
- if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
359
+ if (el.kind === "table") {
360
+ for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
361
+ visit(el.table.overlay?.drawings ?? []);
362
+ }
261
363
  }
262
364
  };
263
365
  visit(flow.body);
@@ -277,7 +379,8 @@ function emitNotes(notes, cfg, state, losses, docScope, extraParts, extraPartRel
277
379
  for (const el of content) emitBlock(inner, el, losses, state, scope);
278
380
  noteXmls.push(`<${cfg.noteTag} w:id="${escapeAttr(id)}">${inner.join("") || "<w:p/>"}</${cfg.noteTag}>`);
279
381
  }
280
- const xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><${cfg.rootTag} xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">` + stub("separator", -1, "<w:separator/>") + stub("continuationSeparator", 0, "<w:continuationSeparator/>") + noteXmls.join("") + `</${cfg.rootTag}>`;
382
+ const notesXml = noteXmls.join("");
383
+ const xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><${cfg.rootTag}${rootNamespaces(notesXml)}>` + stub("separator", -1, "<w:separator/>") + stub("continuationSeparator", 0, "<w:continuationSeparator/>") + notesXml + `</${cfg.rootTag}>`;
281
384
  extraParts.push({
282
385
  path: cfg.partPath,
283
386
  data: encoder.encode(xml),
@@ -383,7 +486,8 @@ function emitHeadersFooters(flow, section, state, docScope, extraParts, extraPar
383
486
  const scope = newScope();
384
487
  const inner = [];
385
488
  for (const el of content) emitBlock(inner, el, losses, state, scope);
386
- const xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><${root} xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">${inner.join("")}</${root}>`;
489
+ const bandXml = inner.join("");
490
+ const xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><${root}${rootNamespaces(bandXml)}>${bandXml}</${root}>`;
387
491
  extraParts.push({
388
492
  path,
389
493
  data: encoder.encode(xml),
@@ -427,7 +531,7 @@ function emitHeadersFooters(flow, section, state, docScope, extraParts, extraPar
427
531
  var docxWriter = {
428
532
  id: "docx",
429
533
  consumes: "flow",
430
- supports: new Set([FEATURES.text]),
534
+ supports: new Set([FEATURES.text, FEATURES.fontsEmbedding]),
431
535
  write: (doc) => writeDocx(doc)
432
536
  };
433
537
  /**
@@ -493,6 +597,11 @@ function emitBlock(out, el, losses, state, scope, closingSectPr) {
493
597
  }
494
598
  if (el.kind === "table") {
495
599
  out.push(tableXml(el.table, losses, state, scope));
600
+ for (const drawing of el.table.overlay?.drawings ?? []) {
601
+ if (state.overlaid.has(drawing)) continue;
602
+ state.overlaid.add(drawing);
603
+ emitBlock(out, drawing, losses, state, scope);
604
+ }
496
605
  if (closingSectPr) out.push(`<w:p><w:pPr>${closingSectPr}</w:pPr></w:p>`);
497
606
  return;
498
607
  }
@@ -636,13 +745,76 @@ function shapeDrawingXml(shape, losses, state, scope) {
636
745
  const cy = Math.round(shape.height * EMU_PER_PT);
637
746
  const id = ++state.drawingSeq;
638
747
  const descr = shape.altText ? ` descr="${escapeAttr(shape.altText)}"` : "";
639
- const spPr = `<wps:spPr>${xfrmXml(shape.transform, cx, cy)}${geomXml(shape.geometry)}${fillXml(shape.fill)}${shape.line ? lineXml(shape.line) : ""}</wps:spPr>`;
640
- const txbx = shape.text ? txbxXml(shape.text, losses, state, scope) : "";
641
- const graphic = `<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="${WPS_URI}"><wps:wsp xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape"><wps:cNvSpPr/>` + spPr + txbx + bodyPrXml(shape.text) + "</wps:wsp></a:graphicData></a:graphic>";
748
+ const members = shape.children ?? [];
749
+ const graphic = "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" + (members.length > 0 ? `<a:graphicData uri="${WPG_URI}">` + groupXml("wpg:wgp", members, {
750
+ x: 0,
751
+ y: 0,
752
+ cx,
753
+ cy
754
+ }, losses, state, scope) : `<a:graphicData uri="${WPS_URI}">` + wspXml(shape, cx, cy, void 0, losses, state, scope)) + "</a:graphicData></a:graphic>";
642
755
  return drawingFrame(shape.float, cx, cy, `<wp:docPr id="${id}" name="Shape ${id}"${descr}/>`, graphic, state, shape.transform?.rotation60k);
643
756
  }
644
- function xfrmXml(t, cx, cy) {
645
- return `<a:xfrm${t?.rotation60k !== void 0 ? ` rot="${t.rotation60k}"` : ""}${t?.flipH ? " flipH=\"1\"" : ""}${t?.flipV ? " flipV=\"1\"" : ""}><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm>`;
757
+ /**
758
+ * `wps:wsp` — one shape: its box, geometry, fill and outline, and the text it
759
+ * holds. `id` is its own `wps:cNvPr` inside a group, where the members name
760
+ * themselves; a shape alone is named by the frame's `wp:docPr`.
761
+ */
762
+ function wspXml(shape, cx, cy, member, losses, state, scope) {
763
+ const spPr = `<wps:spPr>${xfrmXml(shape.transform, cx, cy, member)}${geomXml(shape.geometry)}${fillXml(shape.fill)}${shape.line ? lineXml(shape.line) : ""}</wps:spPr>`;
764
+ const txbx = shape.text ? txbxXml(shape.text, losses, state, scope) : "";
765
+ return `<wps:wsp xmlns:wps="${WPS_URI}">` + (member ? `<wps:cNvPr id="${member.id}" name="Shape ${member.id}"/>` : "") + "<wps:cNvSpPr/>" + spPr + txbx + bodyPrXml(shape.text) + "</wps:wsp>";
766
+ }
767
+ var WPG_URI = "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup";
768
+ /**
769
+ * §20.5.2.17 `wpg:wgp` / `wpg:grpSp` — a group: its box, and its members in
770
+ * the order they are painted. The members are placed in a child space the
771
+ * same as the box, so a member's offset from the group's corner is its offset
772
+ * in the file. The inverse of drawing-parser's `groupChildren`.
773
+ *
774
+ * Written as one shape, the group came back an empty box: a figure's paths
775
+ * and labels were thrown away with it.
776
+ */
777
+ function groupXml(tag, members, box, losses, state, scope) {
778
+ return `<${tag}${tag === "wpg:wgp" ? ` xmlns:wpg="${WPG_URI}"` : ""}><wpg:cNvGrpSpPr/><wpg:grpSpPr>${`<a:xfrm><a:off x="${box.x}" y="${box.y}"/><a:ext cx="${box.cx}" cy="${box.cy}"/><a:chOff x="0" y="0"/><a:chExt cx="${box.cx}" cy="${box.cy}"/></a:xfrm>`}</wpg:grpSpPr>${members.map((m) => memberXml(m, losses, state, scope)).join("")}</${tag}>`;
779
+ }
780
+ /**
781
+ * One member of a group: a group of its own, a picture (`pic:pic`, a shape
782
+ * whose fill is the picture — how the reader brings one in), or a shape.
783
+ */
784
+ function memberXml(member, losses, state, scope) {
785
+ const s = member.shape;
786
+ const x = Math.round(member.xPt * EMU_PER_PT);
787
+ const y = Math.round(member.yPt * EMU_PER_PT);
788
+ const cx = Math.round(s.width * EMU_PER_PT);
789
+ const cy = Math.round(s.height * EMU_PER_PT);
790
+ if (s.children !== void 0 && s.children.length > 0) return groupXml("wpg:grpSp", s.children, {
791
+ x,
792
+ y,
793
+ cx,
794
+ cy
795
+ }, losses, state, scope);
796
+ const id = ++state.drawingSeq;
797
+ if (s.fill.kind !== "picture") return wspXml(s, cx, cy, {
798
+ id,
799
+ x,
800
+ y
801
+ }, losses, state, scope);
802
+ const relId = s.fill.imageResource !== void 0 ? mediaRelId(s.fill.imageResource, state, scope) : void 0;
803
+ if (relId === void 0) {
804
+ losses.push({
805
+ severity: "dropped",
806
+ feature: FEATURES.images,
807
+ detail: imageRefusal(s.fill.imageResource, state)
808
+ });
809
+ return "";
810
+ }
811
+ return `<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="${id}" name="Image ${id}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${relId}">${s.fill.alpha !== void 0 && s.fill.alpha < 1 ? `<a:alphaModFix amt="${String(Math.round(Math.max(0, s.fill.alpha) * 1e5))}"/>` : ""}</a:blip>${srcRectXml(s.fill.imageCrop)}<a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr>${xfrmXml(s.transform, cx, cy, {
812
+ x,
813
+ y
814
+ })}<a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic>`;
815
+ }
816
+ function xfrmXml(t, cx, cy, at) {
817
+ return `<a:xfrm${t?.rotation60k !== void 0 ? ` rot="${t.rotation60k}"` : ""}${t?.flipH ? " flipH=\"1\"" : ""}${t?.flipV ? " flipV=\"1\"" : ""}><a:off x="${at?.x ?? 0}" y="${at?.y ?? 0}"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm>`;
646
818
  }
647
819
  /**
648
820
  * The factor that carries a path into whole units, and the space it lands in.
@@ -668,7 +840,7 @@ function pathScale(g) {
668
840
  else if (c.cmd === "quad") values.push(c.x1, c.y1, c.x, c.y);
669
841
  else if (c.cmd === "arc") values.push(c.wR, c.hR);
670
842
  if (values.every((v) => Number.isInteger(v))) return 1;
671
- const span = Math.max(...values.map((v) => Number.isFinite(v) ? Math.abs(v) : 0), 1);
843
+ const span = values.reduce((most, v) => Number.isFinite(v) ? Math.max(most, Math.abs(v)) : most, 1);
672
844
  return Math.max(1, Math.floor(PATH_SPACE / span));
673
845
  }
674
846
  function geomXml(g) {
@@ -736,7 +908,7 @@ function bodyPrXml(text) {
736
908
  if (!text) return "<wps:bodyPr/>";
737
909
  const ins = (v, name) => v !== void 0 ? ` ${name}="${Math.round(v * EMU_PER_PT)}"` : "";
738
910
  const anchor = text.anchor ? ` anchor="${text.anchor}"` : "";
739
- return "<wps:bodyPr" + ins(text.insetLeft, "lIns") + ins(text.insetTop, "tIns") + ins(text.insetRight, "rIns") + ins(text.insetBottom, "bIns") + anchor + "/>";
911
+ return "<wps:bodyPr" + (text.noWrap === true ? " wrap=\"none\"" : "") + ins(text.insetLeft, "lIns") + ins(text.insetTop, "tIns") + ins(text.insetRight, "rIns") + ins(text.insetBottom, "bIns") + anchor + "/>";
740
912
  }
741
913
  function mediaRelId(resource, state, scope) {
742
914
  const existingRel = scope.relIdByResource.get(resource);
@@ -787,6 +959,7 @@ function tblPrXml(p) {
787
959
  out.push(`<w:tblW w:w="${w}" w:type="${p.widthType}"/>`);
788
960
  }
789
961
  if (p.alignment && p.alignment !== "left") out.push(`<w:jc w:val="${p.alignment}"/>`);
962
+ if (p.indentPt !== void 0 && p.indentPt !== 0) out.push(`<w:tblInd w:w="${twips(p.indentPt)}" w:type="dxa"/>`);
790
963
  const borders = bordersXml("w:tblBorders", p.borders);
791
964
  if (borders) out.push(borders);
792
965
  if (p.layout === "fixed") out.push("<w:tblLayout w:type=\"fixed\"/>");
@@ -839,11 +1012,14 @@ function bordersXml(tag, borders) {
839
1012
  if (tag === "w:pBdr" && key === "insideV") return "";
840
1013
  const el = tag === "w:pBdr" && key === "insideH" ? "w:between" : name;
841
1014
  const sz = b.width !== void 0 ? ` w:sz="${Math.round(b.width * 8)}"` : "";
1015
+ const space = b.spacePt !== void 0 && Math.round(b.spacePt) > 0 ? ` w:space="${Math.min(Math.round(b.spacePt), MOST_BORDER_SPACE_PT)}"` : "";
842
1016
  const color = b.colorHex !== void 0 ? ` w:color="${b.colorHex}"` : "";
843
- return `<${el} w:val="${b.style}"${sz}${color}/>`;
1017
+ return `<${el} w:val="${b.style}"${sz}${space}${color}/>`;
844
1018
  }).join("");
845
1019
  return sides ? `<${tag}>${sides}</${tag}>` : "";
846
1020
  }
1021
+ /** §17.3.4 — the farthest a border may stand off its text, in points. */
1022
+ var MOST_BORDER_SPACE_PT = 31;
847
1023
  function cellMarginsXml(tag, margins) {
848
1024
  if (!margins) return "";
849
1025
  const inner = [
@@ -914,7 +1090,8 @@ function runXml(run, state, scope) {
914
1090
  return piece("<w:fldChar w:fldCharType=\"begin\"/>") + piece(`<w:instrText xml:space="preserve"> ${run.field} </w:instrText>`) + piece("<w:fldChar w:fldCharType=\"separate\"/>") + piece(`<w:t xml:space="preserve">${escapeXml(run.text)}</w:t>`) + piece("<w:fldChar w:fldCharType=\"end\"/>") + brk;
915
1091
  }
916
1092
  if (run.text === "") return brk ? `<w:r>${rPr}${brk}</w:r>` : "";
917
- return `<w:r>${rPr}${run.text.split(" ").map((piece) => piece === "" ? "" : `<w:t xml:space="preserve">${escapeXml(piece)}</w:t>`).join("<w:tab/>")}${brk}</w:r>`;
1093
+ const lineBreak = run.columnBreak ? "<w:br w:type=\"column\"/>" : "<w:br/>";
1094
+ return `<w:r>${rPr}${run.text.split(" ").map((piece) => piece.split("\n").map((line) => line === "" ? "" : `<w:t xml:space="preserve">${escapeXml(line)}</w:t>`).join(lineBreak)).join("<w:tab/>")}${brk}</w:r>`;
918
1095
  }
919
1096
  function rPrXml(r, state) {
920
1097
  const out = [];
@@ -925,6 +1102,15 @@ function rPrXml(r, state) {
925
1102
  if (states("italic")) out.push(toggle("w:i", r.italic), toggle("w:iCs", r.italic));
926
1103
  if (states("strike")) out.push(toggle("w:strike", r.strike));
927
1104
  if (states("colorHex")) out.push(`<w:color w:val="${r.colorHex}"/>`);
1105
+ if (r.letterSpacingPt !== void 0 && r.letterSpacingPt !== 0) {
1106
+ const twips = Math.round(r.letterSpacingPt * 20);
1107
+ if (twips !== 0) out.push(`<w:spacing w:val="${String(twips)}"/>`);
1108
+ }
1109
+ if (r.widthScale !== void 0) {
1110
+ const percent = Math.min(600, Math.max(1, Math.round(r.widthScale * 100)));
1111
+ if (percent !== 100) out.push(`<w:w w:val="${String(percent)}"/>`);
1112
+ }
1113
+ if (r.kerningMinPt !== void 0 && states("kerningMinPt")) out.push(`<w:kern w:val="${Math.round(r.kerningMinPt * 2)}"/>`);
928
1114
  if (states("fontSizePt")) {
929
1115
  const half = Math.round(r.fontSizePt * 2);
930
1116
  out.push(`<w:sz w:val="${half}"/>`, `<w:szCs w:val="${half}"/>`);
@@ -934,6 +1120,7 @@ function rPrXml(r, state) {
934
1120
  if (states("verticalAlign")) out.push(`<w:vertAlign w:val="${r.verticalAlign}"/>`);
935
1121
  if (states("rtl")) out.push(toggle("w:rtl", r.rtl));
936
1122
  if (r.lang !== void 0) out.push(`<w:lang w:val="${escapeAttr(r.lang)}"/>`);
1123
+ if (r.ligatures !== void 0 && states("ligatures")) out.push(`<w14:ligatures w14:val="${r.ligatures}"/>`);
937
1124
  return out.length > 0 ? `<w:rPr>${out.join("")}</w:rPr>` : "";
938
1125
  }
939
1126
  function underlineXml(r) {
@@ -945,10 +1132,14 @@ function runShdXml(fill) {
945
1132
  }
946
1133
  function pPrBody(p) {
947
1134
  const out = [];
1135
+ if (p.keepNext) out.push("<w:keepNext/>");
1136
+ if (p.keepLines) out.push("<w:keepLines/>");
948
1137
  if (p.pageBreakBefore) out.push("<w:pageBreakBefore/>");
1138
+ if (p.widowControl === false) out.push("<w:widowControl w:val=\"0\"/>");
949
1139
  if (p.numbering) out.push(`<w:numPr><w:ilvl w:val="${p.numbering.ilvl}"/><w:numId w:val="${escapeAttr(p.numbering.numId)}"/></w:numPr>`);
950
1140
  const pBdr = bordersXml("w:pBdr", p.borders);
951
1141
  if (pBdr) out.push(pBdr);
1142
+ if (p.shading) out.push(`<w:shd w:val="clear" w:color="auto" w:fill="${p.shading.colorHex}"/>`);
952
1143
  const tabs = p.tabs ?? [];
953
1144
  if (tabs.length > 0) {
954
1145
  const stops = tabs.map((t) => {
@@ -1005,10 +1196,7 @@ function spacingXml(p) {
1005
1196
  const attrs = [];
1006
1197
  if (stated(p.spacingBefore) && p.spacingBefore !== DEFAULT_PARA.spacingBefore) attrs.push(`w:before="${twips(p.spacingBefore)}"`);
1007
1198
  if (stated(p.spacingAfter) && p.spacingAfter !== DEFAULT_PARA.spacingAfter) attrs.push(`w:after="${twips(p.spacingAfter)}"`);
1008
- if ((p.spacingLineRule !== DEFAULT_PARA.spacingLineRule || p.spacingLine !== DEFAULT_PARA.spacingLine) && p.spacingLine > 0) {
1009
- const lineVal = p.spacingLineRule === "auto" ? Math.round(p.spacingLine * 12) : twips(p.spacingLine);
1010
- attrs.push(`w:line="${lineVal}"`, `w:lineRule="${p.spacingLineRule}"`);
1011
- }
1199
+ if ((p.spacingLineRule !== DEFAULT_PARA.spacingLineRule || p.spacingLine !== DEFAULT_PARA.spacingLine) && p.spacingLine > 0) attrs.push(`w:line="${twips(p.spacingLine)}"`, `w:lineRule="${p.spacingLineRule}"`);
1012
1200
  return attrs.length > 0 ? `<w:spacing ${attrs.join(" ")}/>` : "";
1013
1201
  }
1014
1202
  function rFontsXml(fonts, state) {
@@ -1018,6 +1206,7 @@ function rFontsXml(fonts, state) {
1018
1206
  const known = state?.faceFamilies?.get(name);
1019
1207
  if (known === void 0) return name;
1020
1208
  state?.familiesUsed.set(known.family, known);
1209
+ if (state?.faceOutlines?.has(name) === true) state.facesUsed.add(name);
1021
1210
  return known.family;
1022
1211
  };
1023
1212
  const attrs = [];
@@ -1104,8 +1293,14 @@ function sectPrXml(s, hf) {
1104
1293
  const footer = m.footer !== void 0 ? ` w:footer="${twips(m.footer)}"` : "";
1105
1294
  parts.push(`<w:pgMar w:top="${twips(m.top)}" w:right="${twips(m.right)}" w:bottom="${twips(m.bottom)}" w:left="${twips(m.left)}"${header}${footer}/>`);
1106
1295
  }
1296
+ if (s.pageNumberFormat !== void 0 || s.pageNumberStart !== void 0) {
1297
+ const fmt = s.pageNumberFormat !== void 0 ? ` w:fmt="${s.pageNumberFormat}"` : "";
1298
+ const start = s.pageNumberStart !== void 0 ? ` w:start="${String(s.pageNumberStart)}"` : "";
1299
+ parts.push(`<w:pgNumType${fmt}${start}/>`);
1300
+ }
1107
1301
  if (s.columns) parts.push(colsXml(s.columns));
1108
1302
  if (s.titlePg) parts.push("<w:titlePg/>");
1303
+ if (s.textDirection) parts.push(`<w:textDirection w:val="${s.textDirection}"/>`);
1109
1304
  if (parts.length === 0) return "";
1110
1305
  return `<w:sectPr>${parts.join("")}</w:sectPr>`;
1111
1306
  }
@@ -104,7 +104,9 @@ export type DrawingContent = {
104
104
  * ECMA-376 Part 3 (Markup Compatibility) — resolve an `<mc:AlternateContent>` to
105
105
  * the children of the first `<mc:Choice>` whose `Requires` lists only namespaces
106
106
  * we understand, else the `<mc:Fallback>` children, else nothing. (`Requires`
107
- * holds space-separated namespace prefixes as declared in the document.)
107
+ * holds space-separated namespace prefixes as declared in the document.) The
108
+ * elements are known by their local names, whatever prefix the producer bound
109
+ * the namespace to: Word 2008 for Mac wrote `ve:`.
108
110
  *
109
111
  * @param altContent The `mc:AlternateContent` node.
110
112
  * @returns The chosen branch's children.
@@ -126,12 +128,12 @@ export declare function expandMcChildren(children: ReadonlyArray<PoNode>): Array
126
128
  * SmartArt diagram, or — falling through — an embedded picture from
127
129
  * `a:blip @r:embed`.
128
130
  *
129
- * @param drawing The `w:drawing` node.
131
+ * @param drawingNode The `w:drawing` node.
130
132
  * @param resolveColor Resolver for theme/scheme colours used by shape fills/lines.
131
133
  * @param parseBody Optional body parser for a shape's text box (omitted ⇒ no text).
132
134
  * @returns The parsed content, or `null` when no anchor / recognizable graphic is found.
133
135
  */
134
- export declare function parseDrawing(drawing: PoNode, resolveColor: ColorResolver, parseBody?: ParseBody, resolveImage?: (relId: string) => ResourceId | undefined, resolveChartPart?: (relId: string) => string | undefined, themeLineWidths?: ReadonlyArray<number>, themeStyles?: ThemeStyles, parseDrawingText?: ParseDrawingText): DrawingContent | null;
136
+ export declare function parseDrawing(drawingNode: PoNode, resolveColor: ColorResolver, parseBody?: ParseBody, resolveImage?: (relId: string) => ResourceId | undefined, resolveChartPart?: (relId: string) => string | undefined, themeLineWidths?: ReadonlyArray<number>, themeStyles?: ThemeStyles, parseDrawingText?: ParseDrawingText): DrawingContent | null;
135
137
  /**
136
138
  * Parse a legacy `<w:pict>`/`<w:object>` VML picture (ISO/IEC 29500-1 §14, VML
137
139
  * transitional) into an `image` {@link DrawingContent}. Modern files use