@cj-tech-master/excelts 9.6.1 → 10.0.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 (207) hide show
  1. package/README.md +18 -3
  2. package/README_zh.md +18 -3
  3. package/dist/browser/modules/excel/cell.d.ts +4 -0
  4. package/dist/browser/modules/excel/note.js +5 -1
  5. package/dist/browser/modules/excel/row.js +35 -2
  6. package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +8 -1
  7. package/dist/browser/modules/excel/stream/workbook-writer.browser.js +22 -2
  8. package/dist/browser/modules/excel/types.d.ts +81 -0
  9. package/dist/browser/modules/excel/utils/drawing-utils.d.ts +8 -0
  10. package/dist/browser/modules/excel/utils/drawing-utils.js +19 -2
  11. package/dist/browser/modules/excel/workbook.browser.d.ts +16 -0
  12. package/dist/browser/modules/excel/workbook.browser.js +32 -2
  13. package/dist/browser/modules/excel/worksheet.d.ts +31 -1
  14. package/dist/browser/modules/excel/worksheet.js +83 -0
  15. package/dist/browser/modules/excel/xlsx/xform/comment/vml-shape-xform.d.ts +7 -0
  16. package/dist/browser/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
  17. package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
  18. package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
  19. package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
  20. package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +6 -0
  21. package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
  22. package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +1 -0
  23. package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
  24. package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +2 -0
  25. package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
  26. package/dist/browser/modules/excel/xlsx/xform/drawing/shape-xform.d.ts +47 -0
  27. package/dist/browser/modules/excel/xlsx/xform/drawing/shape-xform.js +109 -0
  28. package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
  29. package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
  30. package/dist/browser/modules/pdf/builder/document-builder.js +22 -49
  31. package/dist/browser/modules/pdf/builder/pdf-editor.js +1 -1
  32. package/dist/browser/modules/pdf/core/pdf-stream.d.ts +28 -1
  33. package/dist/browser/modules/pdf/core/pdf-stream.js +38 -2
  34. package/dist/browser/modules/pdf/font/font-manager.d.ts +26 -0
  35. package/dist/browser/modules/pdf/font/font-manager.js +35 -18
  36. package/dist/browser/modules/pdf/render/page-renderer.d.ts +51 -3
  37. package/dist/browser/modules/pdf/render/page-renderer.js +111 -18
  38. package/dist/browser/modules/word/advanced/field-engine.js +45 -20
  39. package/dist/browser/modules/word/advanced/glossary.d.ts +10 -36
  40. package/dist/browser/modules/word/advanced/glossary.js +8 -9
  41. package/dist/browser/modules/word/advanced/math-convert.js +94 -12
  42. package/dist/browser/modules/word/advanced/ole-objects.d.ts +28 -0
  43. package/dist/browser/modules/word/advanced/ole-objects.js +122 -19
  44. package/dist/browser/modules/word/advanced/style-map.js +31 -10
  45. package/dist/browser/modules/word/builder/run-builders.d.ts +7 -1
  46. package/dist/browser/modules/word/builder/run-builders.js +7 -1
  47. package/dist/browser/modules/word/constants.d.ts +4 -0
  48. package/dist/browser/modules/word/constants.js +5 -1
  49. package/dist/browser/modules/word/convert/docx-to-semantic.d.ts +2 -1
  50. package/dist/browser/modules/word/convert/docx-to-semantic.js +135 -1
  51. package/dist/browser/modules/word/convert/html/html-import.d.ts +32 -1
  52. package/dist/browser/modules/word/convert/html/html-import.js +167 -14
  53. package/dist/browser/modules/word/convert/html/html.d.ts +2 -2
  54. package/dist/browser/modules/word/convert/html/html.js +1 -1
  55. package/dist/browser/modules/word/convert/markdown/markdown-import.d.ts +48 -18
  56. package/dist/browser/modules/word/convert/markdown/markdown-import.js +279 -69
  57. package/dist/browser/modules/word/convert/markdown/markdown.d.ts +1 -1
  58. package/dist/browser/modules/word/convert/odt/odt.js +407 -56
  59. package/dist/browser/modules/word/html.d.ts +2 -2
  60. package/dist/browser/modules/word/html.js +1 -1
  61. package/dist/browser/modules/word/index.base.d.ts +3 -3
  62. package/dist/browser/modules/word/index.base.js +1 -1
  63. package/dist/browser/modules/word/layout/layout-full.js +326 -19
  64. package/dist/browser/modules/word/layout/render-page.js +35 -8
  65. package/dist/browser/modules/word/markdown.d.ts +1 -1
  66. package/dist/browser/modules/word/query/compat.d.ts +10 -2
  67. package/dist/browser/modules/word/query/compat.js +29 -21
  68. package/dist/browser/modules/word/reader/docx-reader.js +105 -2
  69. package/dist/browser/modules/word/reader/math-parser.js +8 -2
  70. package/dist/browser/modules/word/security/cfb-reader.js +5 -5
  71. package/dist/browser/modules/word/types.d.ts +96 -1
  72. package/dist/browser/modules/word/writer/docx-packager.js +108 -2
  73. package/dist/browser/modules/word/writer/glossary-writer.d.ts +28 -0
  74. package/dist/browser/modules/word/writer/glossary-writer.js +121 -0
  75. package/dist/browser/modules/word/writer/header-footer-writer.js +105 -20
  76. package/dist/browser/modules/word/writer/math-writer.js +7 -2
  77. package/dist/browser/utils/font-metrics.d.ts +8 -0
  78. package/dist/browser/utils/font-metrics.js +43 -0
  79. package/dist/browser/utils/theme-colors.js +4 -1
  80. package/dist/cjs/modules/excel/note.js +5 -1
  81. package/dist/cjs/modules/excel/row.js +35 -2
  82. package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +22 -2
  83. package/dist/cjs/modules/excel/utils/drawing-utils.js +19 -2
  84. package/dist/cjs/modules/excel/workbook.browser.js +31 -1
  85. package/dist/cjs/modules/excel/worksheet.js +83 -0
  86. package/dist/cjs/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
  87. package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
  88. package/dist/cjs/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
  89. package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
  90. package/dist/cjs/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
  91. package/dist/cjs/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
  92. package/dist/cjs/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
  93. package/dist/cjs/modules/excel/xlsx/xform/drawing/shape-xform.js +112 -0
  94. package/dist/cjs/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
  95. package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
  96. package/dist/cjs/modules/pdf/builder/document-builder.js +21 -48
  97. package/dist/cjs/modules/pdf/builder/pdf-editor.js +1 -1
  98. package/dist/cjs/modules/pdf/core/pdf-stream.js +38 -2
  99. package/dist/cjs/modules/pdf/font/font-manager.js +35 -18
  100. package/dist/cjs/modules/pdf/render/page-renderer.js +112 -18
  101. package/dist/cjs/modules/word/advanced/field-engine.js +45 -20
  102. package/dist/cjs/modules/word/advanced/glossary.js +8 -9
  103. package/dist/cjs/modules/word/advanced/math-convert.js +94 -12
  104. package/dist/cjs/modules/word/advanced/ole-objects.js +123 -19
  105. package/dist/cjs/modules/word/advanced/style-map.js +31 -10
  106. package/dist/cjs/modules/word/builder/run-builders.js +7 -1
  107. package/dist/cjs/modules/word/constants.js +5 -1
  108. package/dist/cjs/modules/word/convert/docx-to-semantic.js +135 -1
  109. package/dist/cjs/modules/word/convert/html/html-import.js +168 -14
  110. package/dist/cjs/modules/word/convert/html/html.js +2 -1
  111. package/dist/cjs/modules/word/convert/markdown/markdown-import.js +279 -69
  112. package/dist/cjs/modules/word/convert/odt/odt.js +407 -56
  113. package/dist/cjs/modules/word/html.js +2 -1
  114. package/dist/cjs/modules/word/index.base.js +4 -3
  115. package/dist/cjs/modules/word/layout/layout-full.js +325 -18
  116. package/dist/cjs/modules/word/layout/render-page.js +35 -8
  117. package/dist/cjs/modules/word/query/compat.js +29 -21
  118. package/dist/cjs/modules/word/reader/docx-reader.js +104 -1
  119. package/dist/cjs/modules/word/reader/math-parser.js +8 -2
  120. package/dist/cjs/modules/word/security/cfb-reader.js +5 -5
  121. package/dist/cjs/modules/word/writer/docx-packager.js +108 -2
  122. package/dist/cjs/modules/word/writer/glossary-writer.js +124 -0
  123. package/dist/cjs/modules/word/writer/header-footer-writer.js +105 -20
  124. package/dist/cjs/modules/word/writer/math-writer.js +7 -2
  125. package/dist/cjs/utils/font-metrics.js +44 -0
  126. package/dist/cjs/utils/theme-colors.js +4 -1
  127. package/dist/esm/modules/excel/note.js +5 -1
  128. package/dist/esm/modules/excel/row.js +35 -2
  129. package/dist/esm/modules/excel/stream/workbook-writer.browser.js +22 -2
  130. package/dist/esm/modules/excel/utils/drawing-utils.js +19 -2
  131. package/dist/esm/modules/excel/workbook.browser.js +32 -2
  132. package/dist/esm/modules/excel/worksheet.js +83 -0
  133. package/dist/esm/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
  134. package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
  135. package/dist/esm/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
  136. package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
  137. package/dist/esm/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
  138. package/dist/esm/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
  139. package/dist/esm/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
  140. package/dist/esm/modules/excel/xlsx/xform/drawing/shape-xform.js +109 -0
  141. package/dist/esm/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
  142. package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
  143. package/dist/esm/modules/pdf/builder/document-builder.js +22 -49
  144. package/dist/esm/modules/pdf/builder/pdf-editor.js +1 -1
  145. package/dist/esm/modules/pdf/core/pdf-stream.js +38 -2
  146. package/dist/esm/modules/pdf/font/font-manager.js +35 -18
  147. package/dist/esm/modules/pdf/render/page-renderer.js +111 -18
  148. package/dist/esm/modules/word/advanced/field-engine.js +45 -20
  149. package/dist/esm/modules/word/advanced/glossary.js +8 -9
  150. package/dist/esm/modules/word/advanced/math-convert.js +94 -12
  151. package/dist/esm/modules/word/advanced/ole-objects.js +122 -19
  152. package/dist/esm/modules/word/advanced/style-map.js +31 -10
  153. package/dist/esm/modules/word/builder/run-builders.js +7 -1
  154. package/dist/esm/modules/word/constants.js +5 -1
  155. package/dist/esm/modules/word/convert/docx-to-semantic.js +135 -1
  156. package/dist/esm/modules/word/convert/html/html-import.js +167 -14
  157. package/dist/esm/modules/word/convert/html/html.js +1 -1
  158. package/dist/esm/modules/word/convert/markdown/markdown-import.js +279 -69
  159. package/dist/esm/modules/word/convert/odt/odt.js +407 -56
  160. package/dist/esm/modules/word/html.js +1 -1
  161. package/dist/esm/modules/word/index.base.js +1 -1
  162. package/dist/esm/modules/word/layout/layout-full.js +326 -19
  163. package/dist/esm/modules/word/layout/render-page.js +35 -8
  164. package/dist/esm/modules/word/query/compat.js +29 -21
  165. package/dist/esm/modules/word/reader/docx-reader.js +105 -2
  166. package/dist/esm/modules/word/reader/math-parser.js +8 -2
  167. package/dist/esm/modules/word/security/cfb-reader.js +5 -5
  168. package/dist/esm/modules/word/writer/docx-packager.js +108 -2
  169. package/dist/esm/modules/word/writer/glossary-writer.js +121 -0
  170. package/dist/esm/modules/word/writer/header-footer-writer.js +105 -20
  171. package/dist/esm/modules/word/writer/math-writer.js +7 -2
  172. package/dist/esm/utils/font-metrics.js +43 -0
  173. package/dist/esm/utils/theme-colors.js +4 -1
  174. package/dist/iife/excelts.iife.js +496 -59
  175. package/dist/iife/excelts.iife.js.map +1 -1
  176. package/dist/iife/excelts.iife.min.js +39 -39
  177. package/dist/types/modules/excel/cell.d.ts +4 -0
  178. package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +8 -1
  179. package/dist/types/modules/excel/types.d.ts +81 -0
  180. package/dist/types/modules/excel/utils/drawing-utils.d.ts +8 -0
  181. package/dist/types/modules/excel/workbook.browser.d.ts +16 -0
  182. package/dist/types/modules/excel/worksheet.d.ts +31 -1
  183. package/dist/types/modules/excel/xlsx/xform/comment/vml-shape-xform.d.ts +7 -0
  184. package/dist/types/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +6 -0
  185. package/dist/types/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +1 -0
  186. package/dist/types/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +2 -0
  187. package/dist/types/modules/excel/xlsx/xform/drawing/shape-xform.d.ts +47 -0
  188. package/dist/types/modules/pdf/core/pdf-stream.d.ts +28 -1
  189. package/dist/types/modules/pdf/font/font-manager.d.ts +26 -0
  190. package/dist/types/modules/pdf/render/page-renderer.d.ts +51 -3
  191. package/dist/types/modules/word/advanced/glossary.d.ts +10 -36
  192. package/dist/types/modules/word/advanced/ole-objects.d.ts +28 -0
  193. package/dist/types/modules/word/builder/run-builders.d.ts +7 -1
  194. package/dist/types/modules/word/constants.d.ts +4 -0
  195. package/dist/types/modules/word/convert/docx-to-semantic.d.ts +2 -1
  196. package/dist/types/modules/word/convert/html/html-import.d.ts +32 -1
  197. package/dist/types/modules/word/convert/html/html.d.ts +2 -2
  198. package/dist/types/modules/word/convert/markdown/markdown-import.d.ts +48 -18
  199. package/dist/types/modules/word/convert/markdown/markdown.d.ts +1 -1
  200. package/dist/types/modules/word/html.d.ts +2 -2
  201. package/dist/types/modules/word/index.base.d.ts +3 -3
  202. package/dist/types/modules/word/markdown.d.ts +1 -1
  203. package/dist/types/modules/word/query/compat.d.ts +10 -2
  204. package/dist/types/modules/word/types.d.ts +96 -1
  205. package/dist/types/modules/word/writer/glossary-writer.d.ts +28 -0
  206. package/dist/types/utils/font-metrics.d.ts +8 -0
  207. package/package.json +3 -1
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { unzip } from "../../archive/read-archive.js";
8
8
  import { parseXml, findChild, textContent } from "../../xml/dom.js";
9
- import { RelType } from "../constants.js";
9
+ import { RelType, ContentType } from "../constants.js";
10
10
  import { utf8Decoder } from "../core/internal-utils.js";
11
11
  import { isRun } from "../core/text-utils.js";
12
12
  import { DocxError, DocxParseError, DocxMissingPartError, DocxEncryptedError, DocxLimitExceededError } from "../errors.js";
@@ -2457,6 +2457,107 @@ async function _readDocxInner(buffer, policy) {
2457
2457
  break;
2458
2458
  }
2459
2459
  }
2460
+ // Extract OLE embedded objects wired on document.xml.rels. We surface
2461
+ // them as structured `oleObjects` so callers can query/round-trip them
2462
+ // (getOleObjectData/extractOleObjects) without depending on the part's
2463
+ // own .rels (OLE binaries carry no .rels — their relationship lives on
2464
+ // document.xml.rels). The body still references each object through an
2465
+ // opaqueDrawing carrying the same r:id. Honours `preserveOleObjects`.
2466
+ let oleObjects;
2467
+ if (policy.preserveOleObjects) {
2468
+ const collected = [];
2469
+ // progId may be recoverable from the body's <o:OLEObject ProgID="…">.
2470
+ // The <w:object> markup is preserved either as a body-level opaqueDrawing
2471
+ // or (more commonly) as a run-level opaqueRun inside a paragraph. Collect
2472
+ // raw XML from both so the ProgID round-trips.
2473
+ const progIdByRId = new Map();
2474
+ const scanOleMarkup = (rawXml) => {
2475
+ // Match ProgID + r:id from within the same <o:OLEObject> element so a
2476
+ // preview <v:imagedata r:id="…"> earlier in the markup is not mistaken
2477
+ // for the OLE binary's relationship id.
2478
+ const oleTagRe = /<o:OLEObject\b[^>]*>/gi;
2479
+ let m;
2480
+ while ((m = oleTagRe.exec(rawXml)) !== null) {
2481
+ const tag = m[0];
2482
+ const progMatch = tag.match(/ProgID="([^"]+)"/i);
2483
+ const ridMatch = tag.match(/r:id="([^"]+)"/i);
2484
+ if (progMatch && ridMatch) {
2485
+ progIdByRId.set(ridMatch[1], progMatch[1]);
2486
+ }
2487
+ }
2488
+ };
2489
+ for (const item of body) {
2490
+ if (item.type === "opaqueDrawing") {
2491
+ scanOleMarkup(item.rawXml);
2492
+ }
2493
+ else if (item.type === "paragraph") {
2494
+ for (const child of item.children) {
2495
+ if (isRun(child)) {
2496
+ for (const rc of child.content) {
2497
+ if (rc.type === "opaqueRun") {
2498
+ scanOleMarkup(rc.rawXml);
2499
+ }
2500
+ }
2501
+ }
2502
+ }
2503
+ }
2504
+ }
2505
+ for (const rel of docRels) {
2506
+ if (rel.type !== RelType.Package) {
2507
+ continue;
2508
+ }
2509
+ const olePath = resolvePartPath(documentPartPath, rel.target);
2510
+ if (!olePath.startsWith("word/embeddings/")) {
2511
+ continue;
2512
+ }
2513
+ const oleData = entries.get(olePath);
2514
+ if (!oleData) {
2515
+ continue;
2516
+ }
2517
+ consumedPaths.add(olePath);
2518
+ collected.push({
2519
+ path: olePath,
2520
+ data: oleData,
2521
+ rId: rel.id,
2522
+ progId: progIdByRId.get(rel.id),
2523
+ contentType: ContentType.OleObject
2524
+ });
2525
+ }
2526
+ if (collected.length > 0) {
2527
+ oleObjects = collected;
2528
+ }
2529
+ }
2530
+ // Glossary document (Building Blocks). The glossary is a self-contained
2531
+ // sub-document: word/glossary/document.xml plus its own companion parts
2532
+ // (styles/settings/webSettings/fontTable) referenced from
2533
+ // word/glossary/_rels/document.xml.rels. We carry document.xml verbatim as
2534
+ // `rawXml` and ALL sibling glossary parts (including the .rels) verbatim in
2535
+ // `rawParts`, so a read→write round-trip re-emits a complete, Word-valid
2536
+ // glossary rather than dropping it (opaqueParts would lose both the
2537
+ // glossaryDocument relationship and the companion .rels). The structured
2538
+ // `blocks` are not reverse-parsed.
2539
+ let glossary;
2540
+ for (const rel of docRels) {
2541
+ if (rel.type !== RelType.Glossary) {
2542
+ continue;
2543
+ }
2544
+ const glossaryPath = resolvePartPath(documentPartPath, rel.target);
2545
+ const glossaryData = entries.get(glossaryPath);
2546
+ if (glossaryData) {
2547
+ // Collect every word/glossary/** part (document.xml, its .rels and all
2548
+ // companions) verbatim, and mark them consumed so they are not also
2549
+ // emitted via opaqueParts.
2550
+ const rawParts = new Map();
2551
+ for (const [path, data] of entries) {
2552
+ if (path.startsWith("word/glossary/")) {
2553
+ rawParts.set(path, data);
2554
+ consumedPaths.add(path);
2555
+ }
2556
+ }
2557
+ glossary = { blocks: [], rawXml: decoder.decode(glossaryData), rawParts };
2558
+ }
2559
+ break;
2560
+ }
2460
2561
  // Resolve altChunk data: body elements of type "altChunk" reference a rId.
2461
2562
  // The target file is stored in docRels + entries. We populate the altChunk
2462
2563
  // body item with its data here AND mark the target path as consumed so the
@@ -2575,6 +2676,8 @@ async function _readDocxInner(buffer, policy) {
2575
2676
  theme,
2576
2677
  watermark,
2577
2678
  opaqueParts: opaqueParts.length > 0 ? opaqueParts : undefined,
2578
- vbaProject
2679
+ vbaProject,
2680
+ oleObjects,
2681
+ glossary
2579
2682
  };
2580
2683
  }
@@ -122,8 +122,14 @@ function parseMathContent(el) {
122
122
  const v = mathAttrVal(el, "val");
123
123
  return v !== "0" && v !== "false";
124
124
  };
125
- if (boolAttr("show")) {
126
- ph.show = true;
125
+ // `show` is tri-state: it may be explicitly off (`m:val="0"`), which
126
+ // is what makes a phantom invisible, explicitly on, or absent
127
+ // (defaults to on). Preserve an explicit value so a round-trip keeps
128
+ // the invisibility, but leave it unset when the element is absent.
129
+ const showEl = findMathChild(phantPrEl, "show");
130
+ if (showEl) {
131
+ const v = mathAttrVal(showEl, "val");
132
+ ph.show = v !== "0" && v !== "false";
127
133
  }
128
134
  if (boolAttr("zeroWid")) {
129
135
  ph.zeroWidth = true;
@@ -336,9 +336,8 @@ export function writeCfb(entries) {
336
336
  const miniNodes = streamNodes.filter(n => n.size > 0 && n.size < MINI_STREAM_CUTOFF);
337
337
  const regularNodes = streamNodes.filter(n => n.size >= MINI_STREAM_CUTOFF);
338
338
  // Assemble the mini-stream and the mini-FAT.
339
- let miniStream = new Uint8Array(0);
340
339
  const miniFat = [];
341
- {
340
+ const miniStream = (() => {
342
341
  const parts = [];
343
342
  let miniSectorIdx = 0;
344
343
  let totalLen = 0;
@@ -354,13 +353,14 @@ export function writeCfb(entries) {
354
353
  parts.push(padded);
355
354
  totalLen += padded.length;
356
355
  }
357
- miniStream = new Uint8Array(totalLen);
356
+ const stream = new Uint8Array(totalLen);
358
357
  let off = 0;
359
358
  for (const p of parts) {
360
- miniStream.set(p, off);
359
+ stream.set(p, off);
361
360
  off += p.length;
362
361
  }
363
- }
362
+ return stream;
363
+ })();
364
364
  root.size = miniStream.length;
365
365
  // ---------------------------------------------------------------------------
366
366
  // 4. Lay out regular sectors.
@@ -20,6 +20,7 @@ import { renderComments, renderCommentsExtended } from "./comment-writer.js";
20
20
  import { createContentTypes, addContentTypeDefault, addContentTypeOverride, addImageContentTypeDefaults, renderContentTypes } from "./content-types.js";
21
21
  import { renderDocument } from "./document-writer.js";
22
22
  import { renderFootnotes, renderEndnotes } from "./footnote-writer.js";
23
+ import { renderGlossaryDocument } from "./glossary-writer.js";
23
24
  import { renderHeader, renderFooter, renderWatermarkHeader } from "./header-footer-writer.js";
24
25
  import { renderNumbering } from "./numbering-writer.js";
25
26
  import { renderSettings, renderFontTable, renderCoreProperties, renderAppProperties, renderCustomProperties, renderWebSettings, renderPeople, renderTheme } from "./parts-writer.js";
@@ -1083,7 +1084,13 @@ async function _packageDocxInner(doc, options) {
1083
1084
  // word/styles.xml
1084
1085
  archive.add(PartPath.Styles, renderXml(xml => renderStyles(xml, doc.docDefaults, doc.styles)));
1085
1086
  // word/settings.xml
1086
- archive.add(PartPath.Settings, renderXml(xml => renderSettings(xml, doc.settings, rawXmlPolicy)));
1087
+ // When the document defines a page background, Word only paints it if
1088
+ // <w:displayBackgroundShape/> is present in settings. Inject it
1089
+ // automatically so setBackground() actually shows up on screen.
1090
+ const settingsForRender = doc.background && !doc.settings?.displayBackgroundShape
1091
+ ? { ...(doc.settings ?? {}), displayBackgroundShape: true }
1092
+ : doc.settings;
1093
+ archive.add(PartPath.Settings, renderXml(xml => renderSettings(xml, settingsForRender, rawXmlPolicy)));
1087
1094
  // word/fontTable.xml
1088
1095
  archive.add(PartPath.FontTable, renderXml(xml => renderFontTable(xml, doc.fonts)));
1089
1096
  // word/fonts/*.odttf (embedded fonts)
@@ -1345,6 +1352,81 @@ async function _packageDocxInner(doc, options) {
1345
1352
  addRelationship(documentRels, RelType.VbaProject, "vbaProject.bin");
1346
1353
  addContentTypeOverride(contentTypes, "/word/vbaProject.bin", ContentType.VbaProject);
1347
1354
  }
1355
+ // OLE embedded objects (word/embeddings/*.bin + optional preview media).
1356
+ // Each object is registered with the *exact* rId referenced from the body
1357
+ // `<w:object r:id="…">` markup, so the reference always resolves. The
1358
+ // `preserveOleObjects` security policy can still strip the binaries below.
1359
+ if (doc.oleObjects && securityPolicy.preserveOleObjects) {
1360
+ for (const ole of doc.oleObjects) {
1361
+ archive.add(ole.path, ole.data);
1362
+ // Target is relative to word/ (document.xml lives in word/).
1363
+ const oleTarget = ole.path.replace(/^word\//, "");
1364
+ addRelationshipWithId(documentRels, ole.rId, RelType.Package, oleTarget);
1365
+ addContentTypeOverride(contentTypes, `/${ole.path}`, ole.contentType ?? ContentType.OleObject);
1366
+ if (ole.previewPath && ole.previewData && ole.previewRId) {
1367
+ archive.add(ole.previewPath, ole.previewData);
1368
+ addRelationshipWithId(documentRels, ole.previewRId, RelType.Image, ole.previewPath.replace(/^word\//, ""));
1369
+ if (ole.previewContentType) {
1370
+ addContentTypeOverride(contentTypes, `/${ole.previewPath}`, ole.previewContentType);
1371
+ }
1372
+ else {
1373
+ const ext = getFileExt(ole.previewPath);
1374
+ const inferred = ext ? inferContentType(ext) : undefined;
1375
+ if (inferred) {
1376
+ addContentTypeOverride(contentTypes, `/${ole.previewPath}`, inferred);
1377
+ }
1378
+ }
1379
+ }
1380
+ }
1381
+ }
1382
+ // word/glossary/document.xml (Building Blocks / AutoText / Quick Parts).
1383
+ //
1384
+ // A glossary is a self-contained sub-document: Word expects it to carry its
1385
+ // own styles / settings / webSettings / fontTable parts (referenced from
1386
+ // word/glossary/_rels/document.xml.rels) rather than sharing the main
1387
+ // document's. Omitting them makes Word discard the whole glossary on open.
1388
+ //
1389
+ // - Round-tripped glossary (`rawParts` set): re-emit every captured
1390
+ // word/glossary/** part verbatim (document.xml, its .rels, companions).
1391
+ // - Freshly-built glossary: synthesise document.xml + the companion parts
1392
+ // + the glossary's own .rels, reusing the main document's styles/fonts so
1393
+ // block content (e.g. Heading1) resolves.
1394
+ if (doc.glossary && (doc.glossary.rawXml || doc.glossary.blocks.length > 0)) {
1395
+ addRelationship(documentRels, RelType.Glossary, "glossary/document.xml");
1396
+ if (doc.glossary.rawParts && doc.glossary.rawParts.size > 0) {
1397
+ for (const [path, data] of doc.glossary.rawParts) {
1398
+ archive.add(path, data);
1399
+ if (path.endsWith(".rels")) {
1400
+ continue; // .rels parts are typed by the Default rels content type
1401
+ }
1402
+ const ct = path === "word/glossary/document.xml"
1403
+ ? ContentType.Glossary
1404
+ : inferContentType(getFileExt(path));
1405
+ if (ct) {
1406
+ addContentTypeOverride(contentTypes, `/${path}`, ct);
1407
+ }
1408
+ }
1409
+ }
1410
+ else {
1411
+ archive.add("word/glossary/document.xml", renderGlossaryDocument(doc.glossary));
1412
+ addContentTypeOverride(contentTypes, "/word/glossary/document.xml", ContentType.Glossary);
1413
+ // Synthesise the companion sub-document parts + the glossary's own rels.
1414
+ const glossaryRels = createRelationships();
1415
+ addRelationshipWithId(glossaryRels, "rId1", RelType.Styles, "styles.xml");
1416
+ addRelationshipWithId(glossaryRels, "rId2", RelType.Settings, "settings.xml");
1417
+ addRelationshipWithId(glossaryRels, "rId3", RelType.WebSettings, "webSettings.xml");
1418
+ addRelationshipWithId(glossaryRels, "rId4", RelType.FontTable, "fontTable.xml");
1419
+ archive.add("word/glossary/_rels/document.xml.rels", renderXml(xml => renderRelationships(glossaryRels, xml)));
1420
+ archive.add("word/glossary/styles.xml", renderXml(xml => renderStyles(xml, doc.docDefaults, doc.styles)));
1421
+ archive.add("word/glossary/settings.xml", renderXml(xml => renderSettings(xml, undefined, rawXmlPolicy)));
1422
+ archive.add("word/glossary/webSettings.xml", renderXml(xml => renderWebSettings(xml, undefined, rawXmlPolicy)));
1423
+ archive.add("word/glossary/fontTable.xml", renderXml(xml => renderFontTable(xml, doc.fonts)));
1424
+ addContentTypeOverride(contentTypes, "/word/glossary/styles.xml", ContentType.Styles);
1425
+ addContentTypeOverride(contentTypes, "/word/glossary/settings.xml", ContentType.Settings);
1426
+ addContentTypeOverride(contentTypes, "/word/glossary/webSettings.xml", ContentType.WebSettings);
1427
+ addContentTypeOverride(contentTypes, "/word/glossary/fontTable.xml", ContentType.FontTable);
1428
+ }
1429
+ }
1348
1430
  // Write opaque (unrecognized) parts for round-trip preservation.
1349
1431
  //
1350
1432
  // Opaque parts are written as-is into the ZIP. Their paths must not
@@ -1385,8 +1467,23 @@ async function _packageDocxInner(doc, options) {
1385
1467
  PartPath.CustomProps,
1386
1468
  PartPath.Thumbnail,
1387
1469
  "word/vbaProject.bin",
1388
- "word/_rels/vbaProject.bin.rels"
1470
+ "word/_rels/vbaProject.bin.rels",
1471
+ "word/glossary/document.xml",
1472
+ "word/glossary/_rels/document.xml.rels"
1389
1473
  ]);
1474
+ // Glossary sub-document parts (emitted either verbatim from rawParts or
1475
+ // synthesised); reserve them so opaqueParts can't collide.
1476
+ if (doc.glossary && (doc.glossary.rawXml || doc.glossary.blocks.length > 0)) {
1477
+ reservedExact.add("word/glossary/styles.xml");
1478
+ reservedExact.add("word/glossary/settings.xml");
1479
+ reservedExact.add("word/glossary/webSettings.xml");
1480
+ reservedExact.add("word/glossary/fontTable.xml");
1481
+ if (doc.glossary.rawParts) {
1482
+ for (const path of doc.glossary.rawParts.keys()) {
1483
+ reservedExact.add(path);
1484
+ }
1485
+ }
1486
+ }
1390
1487
  // Headers/footers we are emitting in this run.
1391
1488
  if (doc.headers) {
1392
1489
  let i = 1;
@@ -1430,6 +1527,15 @@ async function _packageDocxInner(doc, options) {
1430
1527
  }
1431
1528
  }
1432
1529
  }
1530
+ // OLE embedded objects emitted by this run.
1531
+ if (doc.oleObjects) {
1532
+ for (const ole of doc.oleObjects) {
1533
+ reservedExact.add(ole.path);
1534
+ if (ole.previewPath) {
1535
+ reservedExact.add(ole.previewPath);
1536
+ }
1537
+ }
1538
+ }
1433
1539
  for (const part of doc.opaqueParts) {
1434
1540
  if (reservedExact.has(part.path)) {
1435
1541
  throw new DocxWriteError(`Opaque part path "${part.path}" conflicts with a part the packager ` +
@@ -0,0 +1,121 @@
1
+ /**
2
+ * DOCX Module — Glossary (Building Blocks) Part Writer
3
+ *
4
+ * Serialises a {@link GlossaryDocument} into the canonical
5
+ * `word/glossary/document.xml` OOXML form:
6
+ *
7
+ * ```xml
8
+ * <w:glossaryDocument>
9
+ * <w:docParts>
10
+ * <w:docPart>
11
+ * <w:docPartPr>
12
+ * <w:name w:val="…"/>
13
+ * <w:category><w:name w:val="…"/><w:gallery w:val="…"/></w:category>
14
+ * <w:behaviors><w:behavior w:val="content"/></w:behaviors>
15
+ * <w:guid w:val="{…}"/>
16
+ * </w:docPartPr>
17
+ * <w:docPartBody>… body content …</w:docPartBody>
18
+ * </w:docPart>
19
+ * </w:docParts>
20
+ * </w:glossaryDocument>
21
+ * ```
22
+ *
23
+ * Lives in the writer layer (not `advanced/`) so the packager can depend on
24
+ * it without creating a `advanced/ → writer/` import cycle.
25
+ */
26
+ import { XmlWriter } from "../../xml/writer.js";
27
+ import { DOCUMENT_NAMESPACES, STD_DOC_ATTRIBUTES } from "../constants.js";
28
+ import { renderBodyContent } from "./document-writer.js";
29
+ /**
30
+ * Map the friendly {@link BuildingBlockGallery} token to the OOXML
31
+ * `ST_DocPartGallery` value used in `<w:gallery w:val="…">` (ECMA-376
32
+ * §17.18.23). Values MUST be exact enum members — Word rejects (and silently
33
+ * discards) the entire glossary if it sees an out-of-enum gallery value.
34
+ * There is no plain "quickParts" value; Quick Parts map to "custQuickParts".
35
+ */
36
+ const GALLERY_TO_OOXML = {
37
+ autoText: "autoTxt",
38
+ quickParts: "custQuickParts",
39
+ coverPages: "coverPg",
40
+ tableOfContents: "tblOfContents",
41
+ headers: "hdrs",
42
+ footers: "ftrs",
43
+ pageNumbers: "pgNum",
44
+ tables: "tbls",
45
+ textBoxes: "txtBox",
46
+ watermarks: "watermarks",
47
+ equations: "eq",
48
+ bibliographies: "bib",
49
+ custom1: "custom1",
50
+ custom2: "custom2",
51
+ custom3: "custom3",
52
+ custom4: "custom4",
53
+ custom5: "custom5"
54
+ };
55
+ /** Render a {@link GlossaryDocument} to a `word/glossary/document.xml` string. */
56
+ export function renderGlossaryDocument(glossary) {
57
+ // Byte-faithful round-trip: a glossary read from an existing document is
58
+ // carried as verbatim XML and re-emitted unchanged.
59
+ if (glossary.rawXml) {
60
+ return glossary.rawXml;
61
+ }
62
+ const writer = new XmlWriter();
63
+ renderGlossary(writer, glossary);
64
+ return writer.xml;
65
+ }
66
+ function renderGlossary(xml, glossary) {
67
+ xml.openXml(STD_DOC_ATTRIBUTES);
68
+ xml.openNode("w:glossaryDocument", DOCUMENT_NAMESPACES);
69
+ xml.openNode("w:docParts");
70
+ for (const block of glossary.blocks) {
71
+ renderDocPart(xml, block);
72
+ }
73
+ xml.closeNode(); // w:docParts
74
+ xml.closeNode(); // w:glossaryDocument
75
+ }
76
+ function renderDocPart(xml, block) {
77
+ xml.openNode("w:docPart");
78
+ // CT_DocPartPr — child order is fixed by the schema (ECMA-376 §17.12.1):
79
+ // name → style → category → types → behaviors → description → guid.
80
+ // Emitting these out of order makes Word reject the package on open.
81
+ xml.openNode("w:docPartPr");
82
+ xml.leafNode("w:name", { "w:val": block.name });
83
+ xml.openNode("w:category");
84
+ xml.leafNode("w:name", { "w:val": block.category ?? "General" });
85
+ xml.leafNode("w:gallery", { "w:val": GALLERY_TO_OOXML[block.gallery] ?? "placeholder" });
86
+ xml.closeNode(); // w:category
87
+ // `<w:types>` is optional; we omit it so we never emit an out-of-enum
88
+ // ST_DocPartType value. A docPart placed in the body inserts its content.
89
+ xml.openNode("w:behaviors");
90
+ xml.leafNode("w:behavior", { "w:val": "content" });
91
+ xml.closeNode(); // w:behaviors
92
+ if (block.description) {
93
+ xml.leafNode("w:description", { "w:val": block.description });
94
+ }
95
+ if (block.guid) {
96
+ xml.leafNode("w:guid", { "w:val": normaliseGuid(block.guid) });
97
+ }
98
+ xml.closeNode(); // w:docPartPr
99
+ // docPartBody — reuse the main body renderer. A fresh render context keeps
100
+ // id counters local; building-block content here is plain text/tables so it
101
+ // does not need image/chart rId remapping.
102
+ xml.openNode("w:docPartBody");
103
+ for (const item of block.content) {
104
+ renderBodyContent(xml, item);
105
+ }
106
+ // CT_Body must end with a paragraph; emit one if the block had no content.
107
+ if (block.content.length === 0) {
108
+ xml.openNode("w:p");
109
+ xml.closeNode();
110
+ }
111
+ xml.closeNode(); // w:docPartBody
112
+ xml.closeNode(); // w:docPart
113
+ }
114
+ /** Word expects the docPart guid wrapped in braces: `{XXXXXXXX-…}`. */
115
+ function normaliseGuid(guid) {
116
+ const trimmed = guid.trim();
117
+ if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
118
+ return trimmed;
119
+ }
120
+ return `{${trimmed}}`;
121
+ }
@@ -66,12 +66,17 @@ export function renderWatermarkHeader(xml, watermark, imageRId) {
66
66
  "xmlns:o": NS_O,
67
67
  "xmlns:w10": NS_W10
68
68
  });
69
- // Watermark goes in a paragraph with a single pict run. Skip pPr
70
- // entirely emitting an empty <w:pStyle/> without @w:val is a hard
71
- // schema violation Word rejects, and the default style is good enough
72
- // for an auto-generated watermark header.
69
+ // Watermark lives in a paragraph styled as a Header, matching what
70
+ // Microsoft Word emits. The run carries <w:noProof/> so the WordArt
71
+ // text is not spell-checked.
73
72
  xml.openNode("w:p");
73
+ xml.openNode("w:pPr");
74
+ xml.leafNode("w:pStyle", { "w:val": "Header" });
75
+ xml.closeNode(); // pPr
74
76
  xml.openNode("w:r");
77
+ xml.openNode("w:rPr");
78
+ xml.leafNode("w:noProof");
79
+ xml.closeNode(); // rPr
75
80
  xml.openNode("w:pict");
76
81
  if (watermark.type === "text") {
77
82
  renderTextWatermarkVml(xml, watermark);
@@ -87,49 +92,130 @@ export function renderWatermarkHeader(xml, watermark, imageRId) {
87
92
  function renderTextWatermarkVml(xml, wm) {
88
93
  const color = wm.color ?? "C0C0C0";
89
94
  const font = wm.font ?? "Calibri";
90
- const fontSize = wm.fontSize ?? 1; // half-points; Word uses pt string in style
91
- const fontPt = fontSize / 2;
92
95
  const rotation = wm.rotation ?? -45;
93
- const opacity = wm.semiTransparent !== false ? ".5" : "1";
94
- // VML shape for text watermark (PowerWash / WASHOUT style)
95
- xml.leafNode("v:shapetype", {
96
+ // Full WordArt shapetype definition (t136) exactly as Microsoft Word
97
+ // emits it. Word for Mac will NOT render the text path unless the
98
+ // shapetype carries the formula/path/textpath/handles/lock children —
99
+ // an empty <v:shapetype/> produces an invisible watermark.
100
+ xml.openNode("v:shapetype", {
96
101
  id: "_x0000_t136",
97
102
  coordsize: "21600,21600",
98
103
  "o:spt": "136",
104
+ adj: "10800",
99
105
  path: "m@7,l@8,m@5,21600l@6,21600e"
100
106
  });
107
+ xml.openNode("v:formulas");
108
+ for (const eqn of [
109
+ "sum #0 0 10800",
110
+ "prod #0 2 1",
111
+ "sum 21600 0 @1",
112
+ "sum 0 0 @2",
113
+ "sum 21600 0 @3",
114
+ "if @0 @3 0",
115
+ "if @0 21600 @1",
116
+ "if @0 0 @2",
117
+ "if @0 @4 21600",
118
+ "mid @5 @6",
119
+ "mid @8 @5",
120
+ "mid @7 @8",
121
+ "mid @6 @7",
122
+ "sum @6 0 @5"
123
+ ]) {
124
+ xml.leafNode("v:f", { eqn });
125
+ }
126
+ xml.closeNode(); // formulas
127
+ xml.leafNode("v:path", {
128
+ textpathok: "t",
129
+ "o:connecttype": "custom",
130
+ "o:connectlocs": "@9,0;@10,10800;@11,21600;@12,10800",
131
+ "o:connectangles": "270,180,90,0"
132
+ });
133
+ xml.leafNode("v:textpath", { on: "t", fitshape: "t" });
134
+ xml.openNode("v:handles");
135
+ xml.leafNode("v:h", { position: "#0,bottomRight", xrange: "6629,14971" });
136
+ xml.closeNode(); // handles
137
+ xml.leafNode("o:lock", { "v:ext": "edit", text: "t", shapetype: "t" });
138
+ xml.closeNode(); // shapetype
139
+ // The watermark shape itself. Word fixes font-size at 1pt and relies on
140
+ // fitshape="t" to scale the text to fill the shape box; rotation is
141
+ // applied on the shape via the style string.
101
142
  const style = `position:absolute;margin-left:0;margin-top:0;width:468pt;height:234pt;` +
102
- `rotation:${rotation};z-index:-251658752;mso-position-horizontal:center;` +
103
- `mso-position-horizontal-relative:margin;mso-position-vertical:center;` +
104
- `mso-position-vertical-relative:margin`;
143
+ `${rotation ? `rotation:${rotation};` : ""}z-index:-251658752;` +
144
+ `mso-position-horizontal:center;mso-position-horizontal-relative:margin;` +
145
+ `mso-position-vertical:center;mso-position-vertical-relative:margin`;
105
146
  xml.openNode("v:shape", {
106
147
  id: "PowerPlusWaterMarkObject",
107
148
  "o:spid": "_x0000_s2049",
108
149
  type: "#_x0000_t136",
150
+ alt: "",
109
151
  style,
110
152
  "o:allowincell": "f",
111
153
  fillcolor: `#${color}`,
112
154
  stroked: "f"
113
155
  });
114
- xml.leafNode("v:fill", { opacity });
156
+ if (wm.semiTransparent !== false) {
157
+ xml.leafNode("v:fill", { opacity: ".5" });
158
+ }
115
159
  xml.leafNode("v:textpath", {
116
- style: `font-family:&quot;${font}&quot;;font-size:${fontPt}pt`,
160
+ style: `font-family:"${font}";font-size:1pt`,
117
161
  string: wm.text
118
162
  });
119
- xml.leafNode("w10:wrap", { anchorx: "margin", anchory: "margin" });
120
163
  xml.closeNode(); // v:shape
121
164
  }
122
165
  function renderImageWatermarkVml(xml, wm, rId) {
123
- const scale = wm.scale ?? 100;
124
166
  const rid = rId ?? wm.rId;
125
- const style = `position:absolute;margin-left:0;margin-top:0;width:0;height:0;` +
167
+ // Default to a large area covering most of the body so the picture is
168
+ // actually visible. width:0;height:0 produces an invisible dot.
169
+ const widthPt = wm.widthPt ?? 415.2;
170
+ const heightPt = wm.heightPt ?? 233.5;
171
+ // Picture-frame shapetype (t75) as Microsoft Word emits it. Without a
172
+ // proper shapetype + non-zero size the image watermark will not render.
173
+ xml.openNode("v:shapetype", {
174
+ id: "_x0000_t75",
175
+ coordsize: "21600,21600",
176
+ "o:spt": "75",
177
+ "o:preferrelative": "t",
178
+ path: "m@4@5l@4@11@9@11@9@5xe",
179
+ filled: "f",
180
+ stroked: "f"
181
+ });
182
+ xml.openNode("v:stroke", { joinstyle: "miter" });
183
+ xml.closeNode();
184
+ xml.openNode("v:formulas");
185
+ for (const eqn of [
186
+ "if lineDrawn pixelLineWidth 0",
187
+ "sum @0 1 0",
188
+ "sum 0 0 @1",
189
+ "prod @2 1 2",
190
+ "prod @3 21600 pixelWidth",
191
+ "prod @3 21600 pixelHeight",
192
+ "sum @0 0 1",
193
+ "prod @6 1 2",
194
+ "prod @7 21600 pixelWidth",
195
+ "sum @8 21600 0",
196
+ "prod @7 21600 pixelHeight",
197
+ "sum @10 21600 0"
198
+ ]) {
199
+ xml.leafNode("v:f", { eqn });
200
+ }
201
+ xml.closeNode(); // formulas
202
+ xml.leafNode("v:path", {
203
+ "o:extrusionok": "f",
204
+ gradientshapeok: "t",
205
+ "o:connecttype": "rect"
206
+ });
207
+ xml.leafNode("o:lock", { "v:ext": "edit", aspectratio: "t" });
208
+ xml.closeNode(); // shapetype
209
+ const style = `position:absolute;margin-left:0;margin-top:0;` +
210
+ `width:${widthPt}pt;height:${heightPt}pt;` +
126
211
  `z-index:-251658752;mso-position-horizontal:center;` +
127
212
  `mso-position-horizontal-relative:margin;mso-position-vertical:center;` +
128
213
  `mso-position-vertical-relative:margin`;
129
214
  xml.openNode("v:shape", {
130
215
  id: "PowerPlusWaterMarkObject",
131
216
  "o:spid": "_x0000_s2050",
132
- type: "",
217
+ type: "#_x0000_t75",
218
+ alt: "",
133
219
  style,
134
220
  "o:allowincell": "f"
135
221
  });
@@ -139,8 +225,7 @@ function renderImageWatermarkVml(xml, wm, rId) {
139
225
  "r:id": rid,
140
226
  "o:title": "",
141
227
  gain,
142
- blacklevel,
143
- ...(scale !== 100 ? { "o:detectmouseclick": "t" } : {})
228
+ blacklevel
144
229
  });
145
230
  xml.leafNode("w10:wrap", { anchorx: "margin", anchory: "margin" });
146
231
  xml.closeNode(); // v:shape
@@ -175,8 +175,13 @@ function renderMathPhantom(xml, p) {
175
175
  p.transparent !== undefined;
176
176
  if (hasProps) {
177
177
  xml.openNode("m:phantPr");
178
- if (p.show) {
179
- xml.leafNode("m:show", { "m:val": "1" });
178
+ // `m:show` defaults to ON in OOXML (the phantom base is still drawn). To
179
+ // make a phantom "occupy space but stay invisible" the producer must
180
+ // emit `<m:show m:val="0"/>` explicitly — simply omitting it leaves the
181
+ // content visible. So serialize `show` whenever it is defined, mapping
182
+ // false → "0" and true → "1".
183
+ if (p.show !== undefined) {
184
+ xml.leafNode("m:show", { "m:val": p.show ? "1" : "0" });
180
185
  }
181
186
  if (p.zeroWidth) {
182
187
  xml.leafNode("m:zeroWid", { "m:val": "1" });