@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
@@ -422,7 +422,7 @@ function parseParagraph(el, styles, listContext) {
422
422
  if (listContext) {
423
423
  paraProps = {
424
424
  ...paraProps,
425
- numbering: { numId: 1, level: listContext.level }
425
+ numbering: { numId: listContext.numId, level: listContext.level }
426
426
  };
427
427
  }
428
428
  const children = [];
@@ -551,8 +551,12 @@ function parseDrawFrame(el) {
551
551
  };
552
552
  }
553
553
  /** Parse a text:list element. */
554
- function parseList(el, styles, parentLevel) {
554
+ function parseList(el, styles, parentLevel, registry, parentNumId) {
555
555
  const listStyleName = nsAttr(el, "text", "style-name");
556
+ // The outermost list determines the numId; nested lists inherit it so a
557
+ // single multi-level list resolves to one numbering definition. Only the
558
+ // top-level list (no parent numId) consults its own style name.
559
+ const numId = parentNumId ?? registry.numIdFor(listStyleName);
556
560
  const paragraphs = [];
557
561
  const level = parentLevel;
558
562
  const items = findNsChildren(el, "text", "list-item");
@@ -563,18 +567,19 @@ function parseList(el, styles, parentLevel) {
563
567
  }
564
568
  const local = getLocalName(child.name);
565
569
  if (local === "p" || local === "h") {
566
- paragraphs.push(parseParagraph(child, styles, { listStyleName, level }));
570
+ registry.noteLevel(numId, level);
571
+ paragraphs.push(parseParagraph(child, styles, { listStyleName, level, numId }));
567
572
  }
568
573
  else if (local === "list") {
569
- // Nested list
570
- paragraphs.push(...parseList(child, styles, level + 1));
574
+ // Nested list — inherit the enclosing list's numId.
575
+ paragraphs.push(...parseList(child, styles, level + 1, registry, numId));
571
576
  }
572
577
  }
573
578
  }
574
579
  return paragraphs;
575
580
  }
576
581
  /** Parse a table:table element into a Table. */
577
- function parseTable(el, styles) {
582
+ function parseTable(el, styles, registry) {
578
583
  const tableStyleName = nsAttr(el, "table", "style-name");
579
584
  const tableStyle = tableStyleName ? styles.get(tableStyleName) : undefined;
580
585
  // Parse column definitions for widths
@@ -625,10 +630,10 @@ function parseTable(el, styles) {
625
630
  cellContent.push(parseParagraph(cellChild, styles));
626
631
  }
627
632
  else if (cellChildLocal === "table") {
628
- cellContent.push(parseTable(cellChild, styles));
633
+ cellContent.push(parseTable(cellChild, styles, registry));
629
634
  }
630
635
  else if (cellChildLocal === "list") {
631
- cellContent.push(...parseList(cellChild, styles, 0));
636
+ cellContent.push(...parseList(cellChild, styles, 0, registry));
632
637
  }
633
638
  }
634
639
  // Ensure at least one paragraph per cell
@@ -659,7 +664,7 @@ function parseTable(el, styles) {
659
664
  };
660
665
  }
661
666
  /** Parse document body content from the office:body/office:text element. */
662
- function parseDocumentBody(bodyEl, styles) {
667
+ function parseDocumentBody(bodyEl, styles, registry) {
663
668
  const content = [];
664
669
  for (const child of bodyEl.children) {
665
670
  if (child.type !== "element") {
@@ -670,14 +675,14 @@ function parseDocumentBody(bodyEl, styles) {
670
675
  content.push(parseParagraph(child, styles));
671
676
  }
672
677
  else if (local === "table") {
673
- content.push(parseTable(child, styles));
678
+ content.push(parseTable(child, styles, registry));
674
679
  }
675
680
  else if (local === "list") {
676
- content.push(...parseList(child, styles, 0));
681
+ content.push(...parseList(child, styles, 0, registry));
677
682
  }
678
683
  else if (local === "section") {
679
684
  // Sections in ODF are logical containers; flatten their content
680
- content.push(...parseDocumentBody(child, styles));
685
+ content.push(...parseDocumentBody(child, styles, registry));
681
686
  }
682
687
  }
683
688
  return content;
@@ -815,59 +820,248 @@ function convertStylesToStyleDefs(styles) {
815
820
  return defs;
816
821
  }
817
822
  // =============================================================================
818
- // ODT list numbering definition (numId=1)
823
+ // ODT list numbering definitions (bullet & numbered, multi-level)
819
824
  // =============================================================================
825
+ /** Bullet glyphs per nesting level, cycling every three levels. */
826
+ const ODT_BULLET_CHARS = ["•", "◦", "▪", "•", "◦", "▪", "•", "◦", "▪"];
827
+ /** Default ODF `style:num-format` value emitted for each docx NumberFormat. */
828
+ function numberFormatToOdf(format) {
829
+ switch (format) {
830
+ case "decimal":
831
+ case "decimalZero":
832
+ return "1";
833
+ case "lowerLetter":
834
+ return "a";
835
+ case "upperLetter":
836
+ return "A";
837
+ case "lowerRoman":
838
+ return "i";
839
+ case "upperRoman":
840
+ return "I";
841
+ default:
842
+ // ODF has no equivalent for many Word-specific formats; fall back to
843
+ // decimal so the list still renders as an ordered list (its ordered
844
+ // nature is preserved even when the exact glyph set is not).
845
+ return "1";
846
+ }
847
+ }
848
+ /** Map an ODF `style:num-format` value to the closest docx NumberFormat. */
849
+ function odfNumFormatToDocx(numFormat) {
850
+ switch (numFormat) {
851
+ case "1":
852
+ return "decimal";
853
+ case "a":
854
+ return "lowerLetter";
855
+ case "A":
856
+ return "upperLetter";
857
+ case "i":
858
+ return "lowerRoman";
859
+ case "I":
860
+ return "upperRoman";
861
+ default:
862
+ return "decimal";
863
+ }
864
+ }
820
865
  /**
821
- * Walk the converted body and, if any paragraph references `numbering.numId`,
822
- * synthesize the corresponding `abstractNumberings` and `numberingInstances`
823
- * so Word renders bullet/number markers. Without this the list paragraphs
824
- * carry a `numId` that resolves to nothing in the produced document and the
825
- * markers are invisible.
866
+ * Parse a single `text:list-style` element into its per-level formats.
826
867
  *
827
- * The reader currently only emits a single bulleted abstract definition for
828
- * `numId === 1` (matching parseParagraph above). Numbered/multi-level ODT
829
- * lists fall back to bullets for now preserving the visible structure is
830
- * the primary goal.
868
+ * Each `text:list-level-style-bullet` becomes a bullet level; each
869
+ * `text:list-level-style-number` becomes a numbered level whose docx format
870
+ * is derived from `style:num-format`. Levels are indexed by `text:level`
871
+ * (1-based in ODF) and stored 0-based.
831
872
  */
832
- function buildOdtNumberingDefs(body) {
833
- if (!hasListParagraph(body)) {
834
- return {};
873
+ function parseOdfListStyle(el) {
874
+ const levels = new Map();
875
+ for (const child of el.children) {
876
+ if (child.type !== "element") {
877
+ continue;
878
+ }
879
+ const local = getLocalName(child.name);
880
+ const levelAttr = parseInt(nsAttr(child, "text", "level") ?? "1", 10);
881
+ const level = Math.max(0, levelAttr - 1);
882
+ if (local === "list-level-style-bullet") {
883
+ levels.set(level, {
884
+ format: "bullet",
885
+ bulletChar: nsAttr(child, "text", "bullet-char") ?? ODT_BULLET_CHARS[level] ?? "•"
886
+ });
887
+ }
888
+ else if (local === "list-level-style-number") {
889
+ levels.set(level, {
890
+ format: odfNumFormatToDocx(nsAttr(child, "style", "num-format"))
891
+ });
892
+ }
835
893
  }
894
+ return levels;
895
+ }
896
+ /**
897
+ * Index every `text:list-style` found in the ODF automatic/named styles by
898
+ * name. Used by the reader to recover the original bullet-vs-number format of
899
+ * each list so the produced DOCX renders the correct markers.
900
+ */
901
+ function collectOdfListStyles(roots) {
902
+ const out = new Map();
903
+ const visit = (el) => {
904
+ for (const child of el.children) {
905
+ if (child.type !== "element") {
906
+ continue;
907
+ }
908
+ if (getLocalName(child.name) === "list-style") {
909
+ const name = nsAttr(child, "style", "name");
910
+ if (name) {
911
+ out.set(name, parseOdfListStyle(child));
912
+ }
913
+ }
914
+ else {
915
+ visit(child);
916
+ }
917
+ }
918
+ };
919
+ for (const root of roots) {
920
+ visit(root);
921
+ }
922
+ return out;
923
+ }
924
+ /**
925
+ * Registry that assigns a stable `numId` to each distinct ODF list-style name
926
+ * encountered while parsing the body, and produces the matching docx
927
+ * `abstractNumberings` / `numberingInstances` on demand.
928
+ *
929
+ * A registry instance is threaded through `parseList` → `parseParagraph` so
930
+ * list paragraphs reference the same `numId` that ultimately resolves to the
931
+ * synthesized numbering definition. Lists without an explicit style name (or
932
+ * referencing an unknown style) fall back to a default bulleted definition.
933
+ */
934
+ class OdtNumberingRegistry {
935
+ constructor(listStyles) {
936
+ this.numIdByStyle = new Map();
937
+ // The format chosen at each level for a given numId (level 0..8).
938
+ this.levelsByNumId = new Map();
939
+ this.nextNumId = 1;
940
+ this.listStyles = listStyles;
941
+ }
942
+ /** Resolve (and lazily register) the numId for a given list-style name. */
943
+ numIdFor(styleName) {
944
+ const key = styleName ?? "";
945
+ const existing = this.numIdByStyle.get(key);
946
+ if (existing !== undefined) {
947
+ return existing;
948
+ }
949
+ const numId = this.nextNumId++;
950
+ this.numIdByStyle.set(key, numId);
951
+ this.levelsByNumId.set(numId, this.listStyles.get(key) ?? new Map());
952
+ return numId;
953
+ }
954
+ /**
955
+ * Record the level a paragraph used for this numId so the synthesized
956
+ * abstract numbering covers at least the deepest level actually referenced
957
+ * even when the ODF list-style omitted some levels.
958
+ */
959
+ noteLevel(numId, level) {
960
+ let levels = this.levelsByNumId.get(numId);
961
+ if (!levels) {
962
+ levels = new Map();
963
+ this.levelsByNumId.set(numId, levels);
964
+ }
965
+ if (!levels.has(level)) {
966
+ // Unknown level: default to bullet so the marker is still visible.
967
+ levels.set(level, { format: "bullet", bulletChar: ODT_BULLET_CHARS[level] ?? "•" });
968
+ }
969
+ }
970
+ /** Whether any list paragraph was registered. */
971
+ get isEmpty() {
972
+ return this.numIdByStyle.size === 0;
973
+ }
974
+ /** Build the docx numbering definitions for every registered list. */
975
+ build() {
976
+ if (this.isEmpty) {
977
+ return {};
978
+ }
979
+ const abstractNumberings = [];
980
+ const numberingInstances = [];
981
+ for (const numId of this.levelsByNumId.keys()) {
982
+ const odfLevels = this.levelsByNumId.get(numId) ?? new Map();
983
+ const levels = [];
984
+ for (let level = 0; level < 9; level++) {
985
+ const info = odfLevels.get(level) ??
986
+ { format: "bullet", bulletChar: ODT_BULLET_CHARS[level] ?? "•" };
987
+ levels.push(makeNumberingLevel(level, info));
988
+ }
989
+ // abstractNumId mirrors numId for a clean 1:1 mapping.
990
+ abstractNumberings.push({ abstractNumId: numId, levels });
991
+ numberingInstances.push({ numId, abstractNumId: numId });
992
+ }
993
+ return { abstractNumberings, numberingInstances };
994
+ }
995
+ }
996
+ /** Build a single docx NumberingLevel from a parsed ODF level. */
997
+ function makeNumberingLevel(level, info) {
836
998
  const tabSuffix = "tab";
837
- const bulletChars = ["•", "◦", "▪", "•", "◦", "▪", "•", "◦", "▪"];
838
- const levels = bulletChars.map((ch, level) => ({
999
+ const indent = { left: 720 * (level + 1), hanging: 360 };
1000
+ if (info.format === "bullet") {
1001
+ return {
1002
+ level,
1003
+ format: "bullet",
1004
+ text: info.bulletChar ?? ODT_BULLET_CHARS[level] ?? "•",
1005
+ start: 1,
1006
+ paragraphProperties: { indent },
1007
+ suffix: tabSuffix
1008
+ };
1009
+ }
1010
+ return {
839
1011
  level,
840
- format: "bullet",
841
- text: ch,
1012
+ format: info.format,
1013
+ // Word level text uses %N placeholders; "%<level+1>." renders "1." etc.
1014
+ text: `%${level + 1}.`,
842
1015
  start: 1,
843
- paragraphProperties: {
844
- indent: { left: 720 * (level + 1), hanging: 360 }
845
- },
1016
+ paragraphProperties: { indent },
846
1017
  suffix: tabSuffix
847
- }));
848
- return {
849
- abstractNumberings: [{ abstractNumId: 1, levels }],
850
- numberingInstances: [{ numId: 1, abstractNumId: 1 }]
851
1018
  };
852
1019
  }
853
- function hasListParagraph(blocks) {
1020
+ // =============================================================================
1021
+ // Writer-side list numbering helpers
1022
+ // =============================================================================
1023
+ /** Deterministic ODF `text:list-style` name for a given docx numId. */
1024
+ function listStyleNameForNumId(numId) {
1025
+ return `L${numId}`;
1026
+ }
1027
+ /** Collect every distinct numId referenced by list paragraphs in the body. */
1028
+ function collectListNumIds(blocks, out) {
854
1029
  for (const block of blocks) {
855
1030
  if (block.type === "paragraph") {
856
- if (block.properties?.numbering) {
857
- return true;
1031
+ const numId = block.properties?.numbering?.numId;
1032
+ if (typeof numId === "number") {
1033
+ out.add(numId);
858
1034
  }
859
1035
  }
860
1036
  else if (block.type === "table") {
861
1037
  for (const row of block.rows) {
862
1038
  for (const cell of row.cells) {
863
- if (hasListParagraph(cell.content)) {
864
- return true;
865
- }
1039
+ collectListNumIds(cell.content, out);
866
1040
  }
867
1041
  }
868
1042
  }
869
1043
  }
870
- return false;
1044
+ }
1045
+ /**
1046
+ * Resolve the per-level NumberFormat for a numId from the document's numbering
1047
+ * definitions. Follows numId → numberingInstance → abstractNumbering → levels.
1048
+ * Levels missing a definition default to bullet so a marker is still emitted.
1049
+ */
1050
+ function resolveNumIdLevelFormats(doc, numId) {
1051
+ const formats = new Array(9).fill("bullet");
1052
+ const inst = doc.numberingInstances?.find(n => n.numId === numId);
1053
+ const abstractId = inst?.abstractNumId;
1054
+ const abstract = abstractId !== undefined
1055
+ ? doc.abstractNumberings?.find(a => a.abstractNumId === abstractId)
1056
+ : undefined;
1057
+ if (abstract) {
1058
+ for (const lvl of abstract.levels) {
1059
+ if (lvl.level >= 0 && lvl.level < 9) {
1060
+ formats[lvl.level] = lvl.format;
1061
+ }
1062
+ }
1063
+ }
1064
+ return formats;
871
1065
  }
872
1066
  // =============================================================================
873
1067
  // readOdt — Main Entry Point
@@ -916,10 +1110,12 @@ export async function readOdt(buffer) {
916
1110
  // Collect all styles (from both content.xml and styles.xml)
917
1111
  const allStyles = new Map();
918
1112
  let pageLayoutProps;
1113
+ let stylesRootEl;
919
1114
  // Parse styles from styles.xml
920
1115
  if (stylesXml) {
921
1116
  const stylesDoc = parseXml(stylesXml);
922
1117
  const stylesRoot = stylesDoc.root;
1118
+ stylesRootEl = stylesRoot;
923
1119
  // Named styles (office:styles)
924
1120
  const officeStylesEl = findNsChild(stylesRoot, "office", "styles");
925
1121
  if (officeStylesEl) {
@@ -999,7 +1195,16 @@ export async function readOdt(buffer) {
999
1195
  if (!textEl) {
1000
1196
  throw new DocxParseError("Invalid ODT: missing office:text element");
1001
1197
  }
1002
- const body = parseDocumentBody(textEl, allStyles);
1198
+ // Index every `text:list-style` (from styles.xml and content.xml) so the
1199
+ // body parser can recover each list's bullet-vs-number format. The registry
1200
+ // assigns a stable numId per distinct list style and produces the matching
1201
+ // numbering definitions below.
1202
+ const listStyleRoots = [contentDoc.root];
1203
+ if (stylesRootEl) {
1204
+ listStyleRoots.push(stylesRootEl);
1205
+ }
1206
+ const registry = new OdtNumberingRegistry(collectOdfListStyles(listStyleRoots));
1207
+ const body = parseDocumentBody(textEl, allStyles, registry);
1003
1208
  // Convert styles to StyleDef[]
1004
1209
  const styleDefs = convertStylesToStyleDefs(allStyles);
1005
1210
  // Parse section properties from page layout
@@ -1055,7 +1260,7 @@ export async function readOdt(buffer) {
1055
1260
  ...(styleDefs.length > 0 ? { styles: styleDefs } : {}),
1056
1261
  ...(coreProperties ? { coreProperties } : {}),
1057
1262
  ...(images.length > 0 ? { images } : {}),
1058
- ...buildOdtNumberingDefs(body)
1263
+ ...registry.build()
1059
1264
  };
1060
1265
  return doc;
1061
1266
  }
@@ -1284,9 +1489,7 @@ function generateContentXml(doc, imagePaths, imageMap) {
1284
1489
  const bodyWriter = new XmlWriter();
1285
1490
  bodyWriter.openNode("office:body");
1286
1491
  bodyWriter.openNode("office:text");
1287
- for (const block of doc.body) {
1288
- writeBodyContent(bodyWriter, block, doc, imagePaths, ctx);
1289
- }
1492
+ writeBlocks(bodyWriter, doc.body, doc, imagePaths, ctx);
1290
1493
  bodyWriter.closeNode(); // office:text
1291
1494
  bodyWriter.closeNode(); // office:body
1292
1495
  const bodyXml = bodyWriter.xml;
@@ -1333,6 +1536,54 @@ function writeAutoStyles(w, doc) {
1333
1536
  writeStyleDef(w, styleDef);
1334
1537
  }
1335
1538
  }
1539
+ // Emit one `text:list-style` per distinct numId referenced by the body so
1540
+ // bullet vs numbered (and multi-level mixes) round-trip with the correct
1541
+ // markers. Numbers are sorted for deterministic output.
1542
+ const numIds = new Set();
1543
+ collectListNumIds(doc.body, numIds);
1544
+ for (const numId of [...numIds].sort((a, b) => a - b)) {
1545
+ writeListStyleDef(w, numId, resolveNumIdLevelFormats(doc, numId));
1546
+ }
1547
+ }
1548
+ /**
1549
+ * Write the automatic `text:list-style` for a numId.
1550
+ *
1551
+ * Each of the nine ODF list levels is emitted as either a
1552
+ * `text:list-level-style-bullet` or `text:list-level-style-number` based on
1553
+ * the document's per-level NumberFormat, giving compliant readers (Word,
1554
+ * LibreOffice) the right markers and letting the reader recover the format on
1555
+ * the way back in.
1556
+ */
1557
+ function writeListStyleDef(w, numId, formats) {
1558
+ w.openNode("text:list-style", { "style:name": listStyleNameForNumId(numId) });
1559
+ for (let idx = 0; idx < 9; idx++) {
1560
+ const level = idx + 1; // ODF list levels are 1-based.
1561
+ const format = formats[idx] ?? "bullet";
1562
+ if (format === "bullet") {
1563
+ w.openNode("text:list-level-style-bullet", {
1564
+ "text:level": String(level),
1565
+ "text:bullet-char": ODT_BULLET_CHARS[idx] ?? "•"
1566
+ });
1567
+ }
1568
+ else {
1569
+ w.openNode("text:list-level-style-number", {
1570
+ "text:level": String(level),
1571
+ "style:num-format": numberFormatToOdf(format),
1572
+ "style:num-suffix": "."
1573
+ });
1574
+ }
1575
+ w.openNode("style:list-level-properties", {
1576
+ "text:list-level-position-and-space-mode": "label-alignment"
1577
+ });
1578
+ w.leafNode("style:list-level-label-alignment", {
1579
+ "text:label-followed-by": "listtab",
1580
+ "fo:text-indent": "-0.25in",
1581
+ "fo:margin-left": `${0.25 * level}in`
1582
+ });
1583
+ w.closeNode(); // style:list-level-properties
1584
+ w.closeNode(); // text:list-level-style-{bullet,number}
1585
+ }
1586
+ w.closeNode(); // text:list-style
1336
1587
  }
1337
1588
  /** Write a single StyleDef as an ODF style. */
1338
1589
  function writeStyleDef(w, def) {
@@ -1472,6 +1723,113 @@ function alignmentToOdf(alignment) {
1472
1723
  return "start";
1473
1724
  }
1474
1725
  }
1726
+ /**
1727
+ * Determine whether a body block is a list paragraph (carries numbering).
1728
+ *
1729
+ * The ODT reader maps list-item paragraphs back to `numbering.numId`, so the
1730
+ * writer must round-trip them as `text:list` structures rather than bare
1731
+ * `text:p` (which would silently drop the list semantics).
1732
+ */
1733
+ function isListParagraph(block) {
1734
+ return block.type === "paragraph" && block.properties?.numbering !== undefined;
1735
+ }
1736
+ /** numId of a list paragraph (falls back to 1 if somehow absent). */
1737
+ function paragraphNumId(block) {
1738
+ return block.type === "paragraph" ? (block.properties?.numbering?.numId ?? 1) : 1;
1739
+ }
1740
+ /**
1741
+ * Write a sequence of block-level content elements, grouping runs of
1742
+ * consecutive list paragraphs that share a numId into nested `text:list`
1743
+ * structures.
1744
+ *
1745
+ * The ODF list model nests one `text:list` per indentation level, so a
1746
+ * paragraph at `numbering.level === N` is wrapped in `N + 1` nested lists.
1747
+ * Tracking an explicit level stack lets a single run contain mixed levels
1748
+ * (e.g. a sub-bullet between two top-level bullets) and still produce valid,
1749
+ * round-trippable markup that matches what `parseList` expects. Breaking on
1750
+ * numId changes keeps each `text:list` pointed at a single list-style.
1751
+ */
1752
+ function writeBlocks(w, blocks, doc, imagePaths, ctx) {
1753
+ let i = 0;
1754
+ while (i < blocks.length) {
1755
+ const block = blocks[i];
1756
+ if (isListParagraph(block)) {
1757
+ // Consume the maximal run of consecutive list paragraphs sharing numId.
1758
+ const numId = paragraphNumId(block);
1759
+ let j = i;
1760
+ while (j < blocks.length &&
1761
+ isListParagraph(blocks[j]) &&
1762
+ paragraphNumId(blocks[j]) === numId) {
1763
+ j++;
1764
+ }
1765
+ writeListGroup(w, blocks.slice(i, j), numId, doc, imagePaths, ctx);
1766
+ i = j;
1767
+ }
1768
+ else {
1769
+ writeBodyContent(w, block, doc, imagePaths, ctx);
1770
+ i++;
1771
+ }
1772
+ }
1773
+ }
1774
+ /**
1775
+ * Emit a run of list paragraphs (all sharing `numId`) as nested `text:list`
1776
+ * elements.
1777
+ *
1778
+ * ODF nests one `text:list` per indentation level, and every nested list lives
1779
+ * inside a `text:list-item` of its parent. We therefore track, for each open
1780
+ * list level, whether a `text:list-item` is currently open at that level
1781
+ * (`itemOpen[d]`). Raising the level opens wrapper items plus nested lists;
1782
+ * lowering it closes them in the correct order so the markup stays balanced
1783
+ * and matches what `parseList` expects on the way back in.
1784
+ */
1785
+ function writeListGroup(w, paras, numId, doc, imagePaths, ctx) {
1786
+ const styleName = listStyleNameForNumId(numId);
1787
+ // `itemOpen[d]` is true when a `text:list-item` is open inside the list at
1788
+ // depth `d` (0-based). Its length equals the number of open `text:list`s.
1789
+ const itemOpen = [];
1790
+ const openList = () => {
1791
+ w.openNode("text:list", { "text:style-name": styleName });
1792
+ itemOpen.push(false);
1793
+ };
1794
+ const closeList = () => {
1795
+ const depth = itemOpen.length - 1;
1796
+ if (itemOpen[depth]) {
1797
+ w.closeNode(); // text:list-item
1798
+ }
1799
+ itemOpen.pop();
1800
+ w.closeNode(); // text:list
1801
+ };
1802
+ for (const para of paras) {
1803
+ const level = Math.max(0, para.properties?.numbering?.level ?? 0);
1804
+ const wantLists = level + 1;
1805
+ // Close lists until we are at or below the desired depth.
1806
+ while (itemOpen.length > wantLists) {
1807
+ closeList();
1808
+ }
1809
+ // Open nested lists until we reach the desired depth. Each nested list
1810
+ // must sit inside an open `text:list-item` of its parent list.
1811
+ while (itemOpen.length < wantLists) {
1812
+ const depth = itemOpen.length - 1;
1813
+ if (depth >= 0 && !itemOpen[depth]) {
1814
+ w.openNode("text:list-item");
1815
+ itemOpen[depth] = true;
1816
+ }
1817
+ openList();
1818
+ }
1819
+ // At the target depth start a fresh list-item for this paragraph.
1820
+ const depth = itemOpen.length - 1;
1821
+ if (itemOpen[depth]) {
1822
+ w.closeNode(); // previous text:list-item at this depth
1823
+ }
1824
+ w.openNode("text:list-item");
1825
+ itemOpen[depth] = true;
1826
+ writeParagraph(w, para, doc, imagePaths, ctx);
1827
+ }
1828
+ // Unwind any lists still open.
1829
+ while (itemOpen.length > 0) {
1830
+ closeList();
1831
+ }
1832
+ }
1475
1833
  /** Write a block-level content element to the XML writer. */
1476
1834
  function writeBodyContent(w, block, doc, imagePaths, ctx) {
1477
1835
  switch (block.type) {
@@ -1738,14 +2096,7 @@ function writeTableCell(w, cell, doc, imagePaths, ctx) {
1738
2096
  return;
1739
2097
  }
1740
2098
  w.openNode("table:table-cell", attrs);
1741
- for (const content of cell.content) {
1742
- if (content.type === "paragraph") {
1743
- writeParagraph(w, content, doc, imagePaths, ctx);
1744
- }
1745
- else if (content.type === "table") {
1746
- writeTable(w, content, doc, imagePaths, ctx);
1747
- }
1748
- }
2099
+ writeBlocks(w, cell.content, doc, imagePaths, ctx);
1749
2100
  w.closeNode();
1750
2101
  }
1751
2102
  // =============================================================================
@@ -10,5 +10,5 @@
10
10
  * import { renderToHtml, htmlToDocxBody } from "excelts/word/html";
11
11
  * ```
12
12
  */
13
- export { renderToHtml, htmlToDocxBody } from "./convert/html/html.js";
14
- export type { HtmlRenderOptions, HtmlRenderResult, HtmlImportOptions } from "./convert/html/html.js";
13
+ export { renderToHtml, htmlToDocxBody, htmlToDocx } from "./convert/html/html.js";
14
+ export type { HtmlRenderOptions, HtmlRenderResult, HtmlImportOptions, HtmlToDocxResult } from "./convert/html/html.js";
@@ -10,4 +10,4 @@
10
10
  * import { renderToHtml, htmlToDocxBody } from "excelts/word/html";
11
11
  * ```
12
12
  */
13
- export { renderToHtml, htmlToDocxBody } from "./convert/html/html.js";
13
+ export { renderToHtml, htmlToDocxBody, htmlToDocx } from "./convert/html/html.js";
@@ -22,7 +22,7 @@
22
22
  /** @stability stable */
23
23
  export type { HalfPoint, Twips, Emu, EighthPoint, HexColor, ShadingType, BorderStyle, PageOrientation, PageSize, PageMargins, ColumnDef, SectionColumns, SectionBreakType, PageNumberFormat, HeaderFooterType, HeaderFooterRef, PageVerticalAlign, DocumentGridType, Border, ArtBorderType, PageBorders, SectionProperties, UnderlineStyle, VerticalAlign, HighlightColor, FontSpec, Shading, UnderlineSpec, TextEffect, EmphasisMarkType, EastAsianCombineBrackets, EastAsianLayoutSpec, ColorSpec, RunProperties, RunContent, TextContent, BreakContent, TabContent, PositionalTabContent, RubyContent, RubyAlign, RubyProperties, SymbolContent, FootnoteRefContent, EndnoteRefContent, FieldContent, InlineImageContent, CarriageReturnContent, NoBreakHyphenContent, SoftHyphenContent, LastRenderedPageBreakContent, AnnotationReferenceContent, PageNumberType, DateFieldContent, Run, RevisionInfo, InsertedRun, DeletedRun, MovedFromRun, MovedToRun, MoveRangeMarker, CustomXmlTrackingMarker, ParagraphPropertyChange, RunPropertyChange, SectionPropertyChange, TableRowRevision, CommentDef, CommentRangeStart, CommentRangeEnd, CommentReference, Alignment, LineSpacingRule, LineSpacing, Indentation, TabStopType, TabStopLeader, TabStop, PositionalTabAlignment, PositionalTabRelativeTo, PositionalTabLeader, ParagraphBorders, NumberingRef, FrameAnchorType, DropCapType, ParagraphFrame, ParagraphProperties, Paragraph, Hyperlink, BookmarkStart, BookmarkEnd, ParagraphChild, TableWidth, TableBorders, TableCellMargins, TableLayout, TableLook, TableFloat, TableProperties, RowHeightRule, TableRowProperties, VerticalCellAlign, TextDirection, VerticalMerge, TableCellProperties, TableCell, TableRow, Table, HorizontalPositionRelative, VerticalPositionRelative, WrapStyle, WrapTextSide, WrapMargins, FloatingImage, DrawingShape, PresetShapeType, Chart, ChartType, ChartSeries, ChartLegendPosition, ChartAxis, ChartContent, ChartExContent, ChartExData, ChartExSeriesData, ChartTrendline, ChartTrendlineType, ChartErrorBars, ChartErrorBarDirection, ChartErrorBarType, ChartDataLabels, ChartDataLabelPosition, AltChunk, WebSettings, PersonInfo, NoteType, FootnoteProperties, EndnoteProperties, NoteNumberFormat, NoteNumberRestart, TablePropertyChange, TableRowPropertyChange, TableCellPropertyChange, CellMergeRevision, DocxDocumentType, TableOfContents, MathRun, MathFraction, MathSuperScript, MathSubScript, MathSubSuperScript, MathPreSubSuperScript, MathPhantom, MathGroupChar, MathBorderBox, MathRadical, MathDelimiter, MathNary, MathFunction, MathLimit, MathMatrix, MathAccent, MathBar, MathBox, MathEquationArray, MathContent, MathBlock, TextBox, CheckBox, SdtListItem, SdtDateProperties, SdtProperties, SdtDataBinding, SdtCheckboxProperties, SdtRepeatingSectionProperties, StructuredDocumentTag, CustomXmlPart, FormFieldType, FormField, TextFormField, CheckBoxFormField, DropDownFormField, Watermark, TextWatermark, ImageWatermark, BodyContent, StyleType, TableStyleConditionType, TableStyleConditionalFormat, StyleDef, DocDefaults, NumberFormat, LevelJustification, LevelSuffix, NumberingLevel, MultiLevelType, AbstractNumbering, LevelOverride, NumberingInstance, NumPicBullet, HeaderFooterContent, HeaderDef, FooterDef, FootnoteDef, EndnoteDef, ImageMediaType, ImageDef, FontFamily, FontPitch, FontDef, EmbeddedFont, ProtectionType, HyphenationSettings, DocumentSettings, CompatSetting, CompatFlag, DocumentBackground, DocumentTheme, ThemeColorScheme, ThemeFontScheme, ThemeFont, ThemeFormatScheme, ThemeColorName, CustomPropertyValue, CustomProperty, CoreProperties, AppProperties, DocxDocument, DocxOptions } from "./types.js";
24
24
  /** @stability preserve-only */
25
- export type { OpaquePart, OpaqueRelationship, OpaqueDrawing, OpaqueRunContent, OpaqueParagraphChild } from "./types.js";
25
+ export type { OpaquePart, OpaqueRelationship, OpaqueDrawing, OpaqueRunContent, OpaqueParagraphChild, OleObjectPart } from "./types.js";
26
26
  /** @stability stable */
27
27
  export { DocxError, DocxParseError, DocxWriteError, DocxMissingPartError, DocxInvalidStructureError, DocxUnsupportedFeatureError, DocxEncryptedError, DocxDecryptionError, DocxLimitExceededError, isDocxError } from "./errors.js";
28
28
  /** @stability stable */
@@ -110,9 +110,9 @@ export { embedFont, embedFontFamily, addEmbeddedFonts, subsetFont } from "./font
110
110
  /** @stability stable */
111
111
  export type { FontEmbedStyle, EmbedFontOptions, EmbedFontResult } from "./font/font-embed.js";
112
112
  /** @stability experimental */
113
- export { extractOleObjects, hasOleObjects, getOleObjectData, createOleEmbedding } from "./advanced/ole-objects.js";
113
+ export { extractOleObjects, hasOleObjects, getOleObjectData, createOleEmbedding, addOleObject } from "./advanced/ole-objects.js";
114
114
  /** @stability experimental */
115
- export type { OleObject, OleObjectType, OleDisplayAs, OleExtractionResult } from "./advanced/ole-objects.js";
115
+ export type { OleObject, OleObjectType, OleDisplayAs, OleExtractionResult, OleEmbeddingResult } from "./advanced/ole-objects.js";
116
116
  /** @stability experimental */
117
117
  export { createBuildingBlock, createGlossaryDocument, findBuildingBlock, listBuildingBlocks, getAutoTextEntries, getQuickParts } from "./advanced/glossary.js";
118
118
  /** @stability experimental */
@@ -98,7 +98,7 @@ export { validateDocument } from "./advanced/validation.js";
98
98
  export { embedFont, embedFontFamily, addEmbeddedFonts, subsetFont } from "./font/font-embed.js";
99
99
  // --- Experimental API ---
100
100
  /** @stability experimental */
101
- export { extractOleObjects, hasOleObjects, getOleObjectData, createOleEmbedding } from "./advanced/ole-objects.js";
101
+ export { extractOleObjects, hasOleObjects, getOleObjectData, createOleEmbedding, addOleObject } from "./advanced/ole-objects.js";
102
102
  /** @stability experimental */
103
103
  export { createBuildingBlock, createGlossaryDocument, findBuildingBlock, listBuildingBlocks, getAutoTextEntries, getQuickParts } from "./advanced/glossary.js";
104
104
  // Document protection