@cj-tech-master/excelts 9.5.4 → 9.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (767) hide show
  1. package/dist/browser/modules/archive/compression/streaming-compress.browser.js +29 -0
  2. package/dist/browser/modules/archive/compression/streaming-compress.js +9 -0
  3. package/dist/browser/modules/archive/compression/worker-pool/pool.browser.js +26 -1
  4. package/dist/browser/modules/archive/fs/archive-file.d.ts +8 -5
  5. package/dist/browser/modules/archive/fs/archive-file.js +78 -16
  6. package/dist/browser/modules/archive/unzip/stream.browser.js +43 -2
  7. package/dist/browser/modules/excel/chart/chart-ex-builder.js +7 -2
  8. package/dist/browser/modules/excel/chart/chart-ex-renderer.js +4 -9
  9. package/dist/browser/modules/excel/chart/chart-ex-types.d.ts +0 -12
  10. package/dist/browser/modules/excel/chart/chart.d.ts +1 -5
  11. package/dist/browser/modules/excel/chart/chart.js +1 -7
  12. package/dist/browser/modules/excel/chart/types.d.ts +0 -6
  13. package/dist/browser/modules/excel/stream/workbook-reader.browser.js +25 -1
  14. package/dist/browser/modules/excel/stream/workbook-reader.js +9 -0
  15. package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +40 -0
  16. package/dist/browser/modules/excel/stream/workbook-writer.browser.js +228 -13
  17. package/dist/browser/modules/excel/utils/string-buf.d.ts +5 -26
  18. package/dist/browser/modules/excel/utils/string-buf.js +4 -81
  19. package/dist/browser/modules/excel/workbook.browser.js +135 -25
  20. package/dist/browser/modules/excel/xlsx/xform/chart/chart-space-xform.js +6 -20
  21. package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +19 -9
  22. package/dist/browser/modules/excel/xlsx/xlsx.browser.js +32 -8
  23. package/dist/browser/modules/excel/xlsx/xlsx.d.ts +10 -2
  24. package/dist/browser/modules/excel/xlsx/xlsx.js +9 -1
  25. package/dist/browser/modules/pdf/excel-bridge.d.ts +30 -1
  26. package/dist/browser/modules/pdf/excel-bridge.js +32 -0
  27. package/dist/browser/modules/pdf/font/metrics.d.ts +3 -52
  28. package/dist/browser/modules/pdf/font/metrics.js +3 -237
  29. package/dist/browser/modules/pdf/index.d.ts +1 -1
  30. package/dist/browser/modules/pdf/index.js +1 -1
  31. package/dist/browser/modules/pdf/render-layout-to-pdf.d.ts +66 -0
  32. package/dist/browser/modules/pdf/render-layout-to-pdf.js +647 -0
  33. package/dist/browser/modules/pdf/word-bridge.d.ts +80 -12
  34. package/dist/browser/modules/pdf/word-bridge.js +122 -274
  35. package/dist/browser/modules/stream/index.base.d.ts +2 -0
  36. package/dist/browser/modules/stream/index.base.js +2 -1
  37. package/dist/browser/modules/stream/internal/sink-adapter.d.ts +65 -0
  38. package/dist/browser/modules/stream/internal/sink-adapter.js +198 -0
  39. package/dist/browser/modules/stream/pull-stream.d.ts +19 -2
  40. package/dist/browser/modules/stream/pull-stream.js +51 -5
  41. package/dist/browser/modules/stream/types.d.ts +13 -1
  42. package/dist/browser/modules/word/advanced/diff.d.ts +61 -0
  43. package/dist/browser/modules/word/advanced/diff.js +167 -0
  44. package/dist/browser/modules/word/advanced/drawing-shapes.d.ts +269 -0
  45. package/dist/browser/modules/word/advanced/drawing-shapes.js +268 -0
  46. package/dist/browser/modules/word/advanced/field-engine.d.ts +43 -0
  47. package/dist/browser/modules/word/advanced/field-engine.js +1225 -0
  48. package/dist/browser/modules/word/advanced/glossary.d.ts +86 -0
  49. package/dist/browser/modules/word/advanced/glossary.js +79 -0
  50. package/dist/browser/modules/word/advanced/math-convert.d.ts +30 -0
  51. package/dist/browser/modules/word/advanced/math-convert.js +595 -0
  52. package/dist/browser/modules/word/advanced/ole-objects.d.ts +115 -0
  53. package/dist/browser/modules/word/advanced/ole-objects.js +271 -0
  54. package/dist/browser/modules/word/advanced/style-map.d.ts +105 -0
  55. package/dist/browser/modules/word/advanced/style-map.js +322 -0
  56. package/dist/browser/modules/word/advanced/validation.d.ts +56 -0
  57. package/dist/browser/modules/word/advanced/validation.js +1065 -0
  58. package/dist/browser/modules/word/advanced/vba-project.d.ts +91 -0
  59. package/dist/browser/modules/word/advanced/vba-project.js +265 -0
  60. package/dist/browser/modules/word/bridge/excel-bridge.d.ts +127 -0
  61. package/dist/browser/modules/word/bridge/excel-bridge.js +980 -0
  62. package/dist/browser/modules/word/builder/document-handle.d.ts +151 -0
  63. package/dist/browser/modules/word/builder/document-handle.js +664 -0
  64. package/dist/browser/modules/word/builder/paragraph-builders.d.ts +61 -0
  65. package/dist/browser/modules/word/builder/paragraph-builders.js +90 -0
  66. package/dist/browser/modules/word/builder/run-builders.d.ts +374 -0
  67. package/dist/browser/modules/word/builder/run-builders.js +600 -0
  68. package/dist/browser/modules/word/builder/table-builders.d.ts +23 -0
  69. package/dist/browser/modules/word/builder/table-builders.js +45 -0
  70. package/dist/browser/modules/word/constants.d.ts +39 -1
  71. package/dist/browser/modules/word/constants.js +109 -1
  72. package/dist/browser/modules/word/convert/conversion-ir.d.ts +210 -0
  73. package/dist/browser/modules/word/convert/conversion-ir.js +31 -0
  74. package/dist/browser/modules/word/convert/docx-to-semantic.d.ts +39 -0
  75. package/dist/browser/modules/word/convert/docx-to-semantic.js +499 -0
  76. package/dist/browser/modules/word/convert/flat-opc.d.ts +44 -0
  77. package/dist/browser/modules/word/convert/flat-opc.js +385 -0
  78. package/dist/browser/modules/word/convert/html/html-import.d.ts +50 -0
  79. package/dist/browser/modules/word/convert/html/html-import.js +1907 -0
  80. package/dist/{types/modules/word → browser/modules/word/convert/html}/html-renderer.d.ts +14 -1
  81. package/dist/{esm/modules/word → browser/modules/word/convert/html}/html-renderer.js +420 -69
  82. package/dist/browser/modules/word/convert/html/html.d.ts +15 -0
  83. package/dist/browser/modules/word/convert/html/html.js +15 -0
  84. package/dist/browser/modules/word/convert/markdown/markdown-import.d.ts +68 -0
  85. package/dist/browser/modules/word/convert/markdown/markdown-import.js +1325 -0
  86. package/dist/browser/modules/word/convert/markdown/markdown-renderer.d.ts +25 -0
  87. package/dist/browser/modules/word/convert/markdown/markdown-renderer.js +634 -0
  88. package/dist/browser/modules/word/convert/markdown/markdown.d.ts +15 -0
  89. package/dist/browser/modules/word/convert/markdown/markdown.js +15 -0
  90. package/dist/browser/modules/word/convert/odt/odt.d.ts +41 -0
  91. package/dist/browser/modules/word/convert/odt/odt.js +1932 -0
  92. package/dist/browser/modules/word/{color-utils.d.ts → core/color-utils.d.ts} +8 -1
  93. package/dist/browser/modules/word/core/color-utils.js +43 -0
  94. package/dist/browser/modules/word/core/internal-utils.d.ts +90 -0
  95. package/dist/browser/modules/word/core/internal-utils.js +209 -0
  96. package/dist/browser/modules/word/core/mapper.d.ts +44 -0
  97. package/dist/browser/modules/word/core/mapper.js +427 -0
  98. package/dist/browser/modules/word/core/opc-paths.d.ts +33 -0
  99. package/dist/browser/modules/word/core/opc-paths.js +48 -0
  100. package/dist/browser/modules/word/core/text-utils.d.ts +38 -0
  101. package/dist/browser/modules/word/core/text-utils.js +202 -0
  102. package/dist/browser/modules/word/core/walker.d.ts +119 -0
  103. package/dist/browser/modules/word/core/walker.js +570 -0
  104. package/dist/browser/modules/word/crypto.d.ts +14 -9
  105. package/dist/browser/modules/word/crypto.js +13 -7
  106. package/dist/browser/modules/word/document-io.d.ts +59 -27
  107. package/dist/browser/modules/word/document-io.js +80 -197
  108. package/dist/browser/modules/word/errors.d.ts +44 -1
  109. package/dist/browser/modules/word/errors.js +54 -2
  110. package/dist/browser/modules/word/excel.d.ts +14 -0
  111. package/dist/browser/modules/word/excel.js +13 -0
  112. package/dist/browser/modules/word/font/font-embed.d.ts +112 -0
  113. package/dist/browser/modules/word/font/font-embed.js +646 -0
  114. package/dist/{esm/modules/word → browser/modules/word/font}/font-obfuscation.js +4 -9
  115. package/dist/browser/modules/word/font/hyphenation.d.ts +65 -0
  116. package/dist/browser/modules/word/font/hyphenation.js +4210 -0
  117. package/dist/browser/modules/word/font/text-shaping.d.ts +58 -0
  118. package/dist/browser/modules/word/font/text-shaping.js +635 -0
  119. package/dist/browser/modules/word/html.d.ts +7 -6
  120. package/dist/browser/modules/word/html.js +6 -5
  121. package/dist/browser/modules/word/incremental-edit.d.ts +123 -0
  122. package/dist/browser/modules/word/incremental-edit.js +361 -0
  123. package/dist/browser/modules/word/index.base.d.ts +194 -10
  124. package/dist/browser/modules/word/index.base.js +138 -29
  125. package/dist/browser/modules/word/layout/layout-constants.d.ts +17 -0
  126. package/dist/browser/modules/word/layout/layout-constants.js +17 -0
  127. package/dist/browser/modules/word/layout/layout-full.d.ts +53 -0
  128. package/dist/browser/modules/word/layout/layout-full.js +1696 -0
  129. package/dist/browser/modules/word/layout/layout-model.d.ts +344 -0
  130. package/dist/browser/modules/word/layout/layout-model.js +16 -0
  131. package/dist/browser/modules/word/layout/layout.d.ts +63 -0
  132. package/dist/browser/modules/word/layout/layout.js +1167 -0
  133. package/dist/browser/modules/word/layout/render-page.d.ts +57 -0
  134. package/dist/browser/modules/word/layout/render-page.js +1238 -0
  135. package/dist/browser/modules/word/markdown.d.ts +14 -0
  136. package/dist/browser/modules/word/markdown.js +13 -0
  137. package/dist/browser/modules/word/patcher.d.ts +62 -0
  138. package/dist/browser/modules/word/patcher.js +537 -0
  139. package/dist/browser/modules/word/query/compat.d.ts +25 -0
  140. package/dist/browser/modules/word/query/compat.js +58 -0
  141. package/dist/browser/modules/word/query/data-binding.d.ts +22 -0
  142. package/dist/browser/modules/word/query/data-binding.js +392 -0
  143. package/dist/browser/modules/word/query/form-fields.d.ts +41 -0
  144. package/dist/browser/modules/word/query/form-fields.js +268 -0
  145. package/dist/browser/modules/word/query/format-search.d.ts +99 -0
  146. package/dist/browser/modules/word/query/format-search.js +329 -0
  147. package/dist/browser/modules/word/query/mail-merge.d.ts +25 -0
  148. package/dist/browser/modules/word/query/mail-merge.js +111 -0
  149. package/dist/browser/modules/word/query/merge.d.ts +50 -0
  150. package/dist/browser/modules/word/query/merge.js +617 -0
  151. package/dist/browser/modules/word/query/replace.d.ts +47 -0
  152. package/dist/browser/modules/word/query/replace.js +301 -0
  153. package/dist/browser/modules/word/query/revisions.d.ts +67 -0
  154. package/dist/browser/modules/word/query/revisions.js +879 -0
  155. package/dist/browser/modules/word/query/search.d.ts +129 -0
  156. package/dist/browser/modules/word/query/search.js +346 -0
  157. package/dist/browser/modules/word/query/split.d.ts +44 -0
  158. package/dist/browser/modules/word/query/split.js +135 -0
  159. package/dist/browser/modules/word/query/style-resolve.d.ts +104 -0
  160. package/dist/browser/modules/word/query/style-resolve.js +368 -0
  161. package/dist/browser/modules/word/reader/chart-parser.d.ts +20 -0
  162. package/dist/browser/modules/word/reader/chart-parser.js +810 -0
  163. package/dist/browser/modules/word/reader/comments-parser.d.ts +26 -0
  164. package/dist/browser/modules/word/reader/comments-parser.js +92 -0
  165. package/dist/browser/modules/word/reader/doc-props-parsers.d.ts +15 -0
  166. package/dist/browser/modules/word/reader/doc-props-parsers.js +190 -0
  167. package/dist/browser/modules/word/reader/docx-reader.d.ts +27 -0
  168. package/dist/browser/modules/word/reader/docx-reader.js +2557 -0
  169. package/dist/browser/modules/word/reader/drawing-helpers.d.ts +27 -0
  170. package/dist/browser/modules/word/reader/drawing-helpers.js +84 -0
  171. package/dist/browser/modules/word/reader/form-field-parser.d.ts +21 -0
  172. package/dist/browser/modules/word/reader/form-field-parser.js +82 -0
  173. package/dist/browser/modules/word/reader/image-parsers.d.ts +11 -0
  174. package/dist/browser/modules/word/reader/image-parsers.js +291 -0
  175. package/dist/browser/modules/word/reader/math-parser.d.ts +12 -0
  176. package/dist/browser/modules/word/reader/math-parser.js +422 -0
  177. package/dist/browser/modules/word/reader/metadata-parsers.d.ts +17 -0
  178. package/dist/browser/modules/word/reader/metadata-parsers.js +87 -0
  179. package/dist/browser/modules/word/reader/numbering-parser.d.ts +13 -0
  180. package/dist/browser/modules/word/reader/numbering-parser.js +166 -0
  181. package/dist/browser/modules/word/reader/paragraph-section-parsers.d.ts +12 -0
  182. package/dist/browser/modules/word/reader/paragraph-section-parsers.js +503 -0
  183. package/dist/browser/modules/word/reader/parse-utils.d.ts +91 -0
  184. package/dist/browser/modules/word/reader/parse-utils.js +249 -0
  185. package/dist/browser/modules/word/reader/properties-parsers.d.ts +21 -0
  186. package/dist/browser/modules/word/reader/properties-parsers.js +332 -0
  187. package/dist/browser/modules/word/reader/reader-context.d.ts +69 -0
  188. package/dist/browser/modules/word/reader/reader-context.js +61 -0
  189. package/dist/browser/modules/word/reader/sdt-helpers.d.ts +29 -0
  190. package/dist/browser/modules/word/reader/sdt-helpers.js +111 -0
  191. package/dist/browser/modules/word/reader/settings-parser.d.ts +8 -0
  192. package/dist/browser/modules/word/reader/settings-parser.js +263 -0
  193. package/dist/browser/modules/word/reader/styles-parser.d.ts +12 -0
  194. package/dist/browser/modules/word/reader/styles-parser.js +147 -0
  195. package/dist/browser/modules/word/reader/table-properties-parsers.d.ts +12 -0
  196. package/dist/browser/modules/word/reader/table-properties-parsers.js +234 -0
  197. package/dist/browser/modules/word/reader/theme-parser.d.ts +8 -0
  198. package/dist/browser/modules/word/reader/theme-parser.js +167 -0
  199. package/dist/browser/modules/word/reader/watermark-parser.d.ts +15 -0
  200. package/dist/browser/modules/word/reader/watermark-parser.js +110 -0
  201. package/dist/browser/modules/word/security/cfb-reader.d.ts +37 -0
  202. package/dist/browser/modules/word/security/cfb-reader.js +410 -0
  203. package/dist/browser/modules/word/{digital-signatures.d.ts → security/digital-signatures.d.ts} +19 -11
  204. package/dist/browser/modules/word/{digital-signatures.js → security/digital-signatures.js} +34 -34
  205. package/dist/browser/modules/word/security/document-protection.d.ts +93 -0
  206. package/dist/browser/modules/word/security/document-protection.js +201 -0
  207. package/dist/{types/modules/word → browser/modules/word/security}/encryption.d.ts +51 -4
  208. package/dist/browser/modules/word/security/encryption.js +602 -0
  209. package/dist/browser/modules/word/security/policy.d.ts +80 -0
  210. package/dist/browser/modules/word/security/policy.js +102 -0
  211. package/dist/browser/modules/word/template/template-chart.d.ts +56 -0
  212. package/dist/browser/modules/word/template/template-chart.js +167 -0
  213. package/dist/browser/modules/word/template/template-datasource.d.ts +154 -0
  214. package/dist/browser/modules/word/template/template-datasource.js +541 -0
  215. package/dist/browser/modules/word/template/template-engine.d.ts +121 -0
  216. package/dist/browser/modules/word/template/template-engine.js +1435 -0
  217. package/dist/browser/modules/word/types.d.ts +224 -25
  218. package/dist/browser/modules/word/units.d.ts +26 -0
  219. package/dist/browser/modules/word/units.js +43 -14
  220. package/dist/browser/modules/word/{writers → writer}/chart-writer.js +164 -23
  221. package/dist/browser/modules/word/writer/checkbox-writer.d.ts +17 -0
  222. package/dist/browser/modules/word/writer/checkbox-writer.js +79 -0
  223. package/dist/{types/modules/word/writers → browser/modules/word/writer}/comment-writer.d.ts +2 -1
  224. package/dist/browser/modules/word/{writers → writer}/comment-writer.js +8 -6
  225. package/dist/browser/modules/word/writer/common-parts.d.ts +57 -0
  226. package/dist/browser/modules/word/writer/common-parts.js +101 -0
  227. package/dist/{types/modules/word → browser/modules/word/writer}/content-types.d.ts +2 -2
  228. package/dist/{esm/modules/word → browser/modules/word/writer}/content-types.js +14 -6
  229. package/dist/browser/modules/word/writer/document-writer.d.ts +24 -0
  230. package/dist/browser/modules/word/writer/document-writer.js +473 -0
  231. package/dist/browser/modules/word/writer/docx-packager.d.ts +35 -0
  232. package/dist/browser/modules/word/writer/docx-packager.js +1515 -0
  233. package/dist/{types/modules/word/writers → browser/modules/word/writer}/footnote-writer.d.ts +3 -2
  234. package/dist/{esm/modules/word/writers → browser/modules/word/writer}/footnote-writer.js +13 -10
  235. package/dist/{types/modules/word/writers → browser/modules/word/writer}/header-footer-writer.d.ts +3 -2
  236. package/dist/{esm/modules/word/writers → browser/modules/word/writer}/header-footer-writer.js +39 -21
  237. package/dist/{types/modules/word/writers → browser/modules/word/writer}/image-writer.d.ts +1 -1
  238. package/dist/browser/modules/word/{writers → writer}/image-writer.js +11 -7
  239. package/dist/browser/modules/word/writer/math-writer.d.ts +20 -0
  240. package/dist/{esm/modules/word/writers → browser/modules/word/writer}/math-writer.js +21 -1
  241. package/dist/browser/modules/word/{writers → writer}/numbering-writer.d.ts +1 -1
  242. package/dist/{esm/modules/word/writers → browser/modules/word/writer}/numbering-writer.js +11 -4
  243. package/dist/browser/modules/word/{writers → writer}/paragraph-writer.d.ts +2 -1
  244. package/dist/browser/modules/word/{writers → writer}/paragraph-writer.js +73 -38
  245. package/dist/browser/modules/word/{writers → writer}/parts-writer.d.ts +3 -3
  246. package/dist/{esm/modules/word/writers → browser/modules/word/writer}/parts-writer.js +91 -12
  247. package/dist/browser/modules/word/writer/reference-scanners.d.ts +42 -0
  248. package/dist/browser/modules/word/writer/reference-scanners.js +111 -0
  249. package/dist/browser/modules/word/writer/relationships.d.ts +52 -0
  250. package/dist/browser/modules/word/writer/relationships.js +117 -0
  251. package/dist/browser/modules/word/writer/render-context.d.ts +124 -0
  252. package/dist/browser/modules/word/writer/render-context.js +46 -0
  253. package/dist/browser/modules/word/{writers → writer}/run-writer.d.ts +10 -1
  254. package/dist/{esm/modules/word/writers → browser/modules/word/writer}/run-writer.js +126 -24
  255. package/dist/browser/modules/word/writer/sdt-writer.d.ts +25 -0
  256. package/dist/browser/modules/word/writer/sdt-writer.js +189 -0
  257. package/dist/browser/modules/word/writer/stream-buf.d.ts +37 -0
  258. package/dist/browser/modules/word/writer/stream-buf.js +73 -0
  259. package/dist/browser/modules/word/writer/streaming-writer.d.ts +344 -0
  260. package/dist/browser/modules/word/writer/streaming-writer.js +1382 -0
  261. package/dist/browser/modules/word/writer/string-buf.d.ts +8 -0
  262. package/dist/browser/modules/word/writer/string-buf.js +7 -0
  263. package/dist/browser/modules/word/{writers → writer}/styles-writer.js +32 -1
  264. package/dist/browser/modules/word/{writers → writer}/table-writer.d.ts +2 -1
  265. package/dist/browser/modules/word/{writers → writer}/table-writer.js +94 -11
  266. package/dist/browser/modules/xml/types.d.ts +22 -0
  267. package/dist/browser/utils/crypto.browser.d.ts +3 -1
  268. package/dist/browser/utils/crypto.browser.js +3 -1
  269. package/dist/browser/utils/crypto.d.ts +4 -1
  270. package/dist/browser/utils/crypto.js +4 -1
  271. package/dist/browser/utils/font-metrics.d.ts +63 -0
  272. package/dist/browser/utils/font-metrics.js +293 -0
  273. package/dist/browser/utils/string-buf.d.ts +42 -0
  274. package/dist/browser/utils/string-buf.js +89 -0
  275. package/dist/browser/utils/theme-colors.d.ts +55 -0
  276. package/dist/browser/utils/theme-colors.js +120 -0
  277. package/dist/cjs/modules/archive/compression/streaming-compress.browser.js +29 -0
  278. package/dist/cjs/modules/archive/compression/streaming-compress.js +9 -0
  279. package/dist/cjs/modules/archive/compression/worker-pool/pool.browser.js +26 -1
  280. package/dist/cjs/modules/archive/fs/archive-file.js +78 -16
  281. package/dist/cjs/modules/archive/unzip/stream.browser.js +43 -2
  282. package/dist/cjs/modules/excel/chart/chart-ex-builder.js +7 -2
  283. package/dist/cjs/modules/excel/chart/chart-ex-renderer.js +4 -9
  284. package/dist/cjs/modules/excel/chart/chart.js +1 -7
  285. package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +25 -1
  286. package/dist/cjs/modules/excel/stream/workbook-reader.js +9 -0
  287. package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +228 -13
  288. package/dist/cjs/modules/excel/utils/string-buf.js +5 -81
  289. package/dist/cjs/modules/excel/workbook.browser.js +135 -25
  290. package/dist/cjs/modules/excel/xlsx/xform/chart/chart-space-xform.js +6 -20
  291. package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +32 -8
  292. package/dist/cjs/modules/excel/xlsx/xlsx.js +9 -1
  293. package/dist/cjs/modules/pdf/excel-bridge.js +33 -0
  294. package/dist/cjs/modules/pdf/font/metrics.js +11 -244
  295. package/dist/cjs/modules/pdf/index.js +2 -1
  296. package/dist/cjs/modules/pdf/render-layout-to-pdf.js +651 -0
  297. package/dist/cjs/modules/pdf/word-bridge.js +155 -274
  298. package/dist/cjs/modules/stream/index.base.js +4 -2
  299. package/dist/cjs/modules/stream/internal/sink-adapter.js +202 -0
  300. package/dist/cjs/modules/stream/pull-stream.js +51 -5
  301. package/dist/cjs/modules/word/advanced/diff.js +170 -0
  302. package/dist/cjs/modules/word/advanced/drawing-shapes.js +279 -0
  303. package/dist/cjs/modules/word/advanced/field-engine.js +1229 -0
  304. package/dist/cjs/modules/word/advanced/glossary.js +87 -0
  305. package/dist/cjs/modules/word/advanced/math-convert.js +599 -0
  306. package/dist/cjs/modules/word/advanced/ole-objects.js +277 -0
  307. package/dist/cjs/modules/word/advanced/style-map.js +329 -0
  308. package/dist/cjs/modules/word/advanced/validation.js +1068 -0
  309. package/dist/cjs/modules/word/advanced/vba-project.js +274 -0
  310. package/dist/cjs/modules/word/bridge/excel-bridge.js +1020 -0
  311. package/dist/cjs/modules/word/builder/document-handle.js +667 -0
  312. package/dist/cjs/modules/word/builder/paragraph-builders.js +109 -0
  313. package/dist/cjs/modules/word/builder/run-builders.js +676 -0
  314. package/dist/cjs/modules/word/builder/table-builders.js +53 -0
  315. package/dist/cjs/modules/word/constants.js +111 -2
  316. package/dist/cjs/modules/word/convert/conversion-ir.js +34 -0
  317. package/dist/cjs/modules/word/convert/docx-to-semantic.js +502 -0
  318. package/dist/cjs/modules/word/convert/flat-opc.js +390 -0
  319. package/dist/cjs/modules/word/convert/html/html-import.js +1910 -0
  320. package/dist/cjs/modules/word/{html-renderer.js → convert/html/html-renderer.js} +420 -69
  321. package/dist/cjs/modules/word/convert/html/html.js +20 -0
  322. package/dist/cjs/modules/word/convert/markdown/markdown-import.js +1329 -0
  323. package/dist/cjs/modules/word/convert/markdown/markdown-renderer.js +637 -0
  324. package/dist/cjs/modules/word/convert/markdown/markdown.js +21 -0
  325. package/dist/cjs/modules/word/convert/odt/odt.js +1936 -0
  326. package/dist/cjs/modules/word/core/color-utils.js +47 -0
  327. package/dist/cjs/modules/word/core/internal-utils.js +219 -0
  328. package/dist/cjs/modules/word/core/mapper.js +430 -0
  329. package/dist/cjs/modules/word/core/opc-paths.js +53 -0
  330. package/dist/cjs/modules/word/core/text-utils.js +210 -0
  331. package/dist/cjs/modules/word/core/walker.js +577 -0
  332. package/dist/cjs/modules/word/crypto.js +19 -8
  333. package/dist/cjs/modules/word/document-io.js +117 -197
  334. package/dist/cjs/modules/word/errors.js +59 -13
  335. package/dist/cjs/modules/word/excel.js +22 -0
  336. package/dist/cjs/modules/word/font/font-embed.js +652 -0
  337. package/dist/cjs/modules/word/{font-obfuscation.js → font/font-obfuscation.js} +4 -9
  338. package/dist/cjs/modules/word/font/hyphenation.js +4216 -0
  339. package/dist/cjs/modules/word/font/text-shaping.js +640 -0
  340. package/dist/cjs/modules/word/html.js +9 -7
  341. package/dist/cjs/modules/word/incremental-edit.js +366 -0
  342. package/dist/cjs/modules/word/index.base.js +370 -137
  343. package/dist/cjs/modules/word/layout/layout-constants.js +20 -0
  344. package/dist/cjs/modules/word/layout/layout-full.js +1699 -0
  345. package/dist/cjs/modules/word/layout/layout-model.js +17 -0
  346. package/dist/cjs/modules/word/layout/layout.js +1170 -0
  347. package/dist/cjs/modules/word/layout/render-page.js +1243 -0
  348. package/dist/cjs/modules/word/markdown.js +19 -0
  349. package/dist/cjs/modules/word/patcher.js +539 -0
  350. package/dist/cjs/modules/word/query/compat.js +61 -0
  351. package/dist/cjs/modules/word/query/data-binding.js +395 -0
  352. package/dist/cjs/modules/word/query/form-fields.js +272 -0
  353. package/dist/cjs/modules/word/query/format-search.js +334 -0
  354. package/dist/cjs/modules/word/query/mail-merge.js +114 -0
  355. package/dist/cjs/modules/word/query/merge.js +620 -0
  356. package/dist/cjs/modules/word/query/replace.js +304 -0
  357. package/dist/cjs/modules/word/query/revisions.js +885 -0
  358. package/dist/cjs/modules/word/query/search.js +361 -0
  359. package/dist/cjs/modules/word/query/split.js +138 -0
  360. package/dist/cjs/modules/word/query/style-resolve.js +374 -0
  361. package/dist/cjs/modules/word/reader/chart-parser.js +814 -0
  362. package/dist/cjs/modules/word/reader/comments-parser.js +96 -0
  363. package/dist/cjs/modules/word/reader/doc-props-parsers.js +194 -0
  364. package/dist/cjs/modules/word/reader/docx-reader.js +2560 -0
  365. package/dist/cjs/modules/word/reader/drawing-helpers.js +90 -0
  366. package/dist/cjs/modules/word/reader/form-field-parser.js +85 -0
  367. package/dist/cjs/modules/word/reader/image-parsers.js +293 -0
  368. package/dist/cjs/modules/word/reader/math-parser.js +424 -0
  369. package/dist/cjs/modules/word/reader/metadata-parsers.js +93 -0
  370. package/dist/cjs/modules/word/reader/numbering-parser.js +168 -0
  371. package/dist/cjs/modules/word/reader/paragraph-section-parsers.js +505 -0
  372. package/dist/cjs/modules/word/reader/parse-utils.js +271 -0
  373. package/dist/cjs/modules/word/reader/properties-parsers.js +338 -0
  374. package/dist/cjs/modules/word/reader/reader-context.js +66 -0
  375. package/dist/cjs/modules/word/reader/sdt-helpers.js +114 -0
  376. package/dist/cjs/modules/word/reader/settings-parser.js +265 -0
  377. package/dist/cjs/modules/word/reader/styles-parser.js +149 -0
  378. package/dist/cjs/modules/word/reader/table-properties-parsers.js +237 -0
  379. package/dist/cjs/modules/word/reader/theme-parser.js +169 -0
  380. package/dist/cjs/modules/word/reader/watermark-parser.js +113 -0
  381. package/dist/cjs/modules/word/security/cfb-reader.js +414 -0
  382. package/dist/cjs/modules/word/{digital-signatures.js → security/digital-signatures.js} +34 -34
  383. package/dist/cjs/modules/word/security/document-protection.js +208 -0
  384. package/dist/cjs/modules/word/security/encryption.js +612 -0
  385. package/dist/cjs/modules/word/security/policy.js +106 -0
  386. package/dist/cjs/modules/word/template/template-chart.js +170 -0
  387. package/dist/cjs/modules/word/template/template-datasource.js +549 -0
  388. package/dist/cjs/modules/word/template/template-engine.js +1430 -0
  389. package/dist/cjs/modules/word/units.js +44 -14
  390. package/dist/cjs/modules/word/{writers → writer}/chart-writer.js +163 -22
  391. package/dist/cjs/modules/word/writer/checkbox-writer.js +82 -0
  392. package/dist/cjs/modules/word/{writers → writer}/comment-writer.js +8 -6
  393. package/dist/cjs/modules/word/writer/common-parts.js +104 -0
  394. package/dist/cjs/modules/word/{content-types.js → writer/content-types.js} +14 -6
  395. package/dist/cjs/modules/word/writer/document-writer.js +478 -0
  396. package/dist/cjs/modules/word/writer/docx-packager.js +1551 -0
  397. package/dist/cjs/modules/word/{writers → writer}/footnote-writer.js +13 -10
  398. package/dist/cjs/modules/word/{writers → writer}/header-footer-writer.js +38 -20
  399. package/dist/cjs/modules/word/{writers → writer}/image-writer.js +11 -7
  400. package/dist/cjs/modules/word/{writers → writer}/math-writer.js +21 -1
  401. package/dist/cjs/modules/word/{writers → writer}/numbering-writer.js +11 -4
  402. package/dist/cjs/modules/word/{writers → writer}/paragraph-writer.js +72 -37
  403. package/dist/cjs/modules/word/{writers → writer}/parts-writer.js +91 -12
  404. package/dist/cjs/modules/word/writer/reference-scanners.js +120 -0
  405. package/dist/cjs/modules/word/writer/relationships.js +124 -0
  406. package/dist/cjs/modules/word/writer/render-context.js +51 -0
  407. package/dist/cjs/modules/word/{writers → writer}/run-writer.js +127 -24
  408. package/dist/cjs/modules/word/writer/sdt-writer.js +192 -0
  409. package/dist/cjs/modules/word/writer/stream-buf.js +76 -0
  410. package/dist/cjs/modules/word/writer/streaming-writer.js +1387 -0
  411. package/dist/cjs/modules/word/writer/string-buf.js +11 -0
  412. package/dist/cjs/modules/word/{writers → writer}/styles-writer.js +32 -1
  413. package/dist/cjs/modules/word/{writers → writer}/table-writer.js +94 -11
  414. package/dist/cjs/utils/crypto.browser.js +3 -1
  415. package/dist/cjs/utils/crypto.js +4 -1
  416. package/dist/cjs/utils/font-metrics.js +303 -0
  417. package/dist/cjs/utils/string-buf.js +92 -0
  418. package/dist/cjs/utils/theme-colors.js +126 -0
  419. package/dist/esm/modules/archive/compression/streaming-compress.browser.js +29 -0
  420. package/dist/esm/modules/archive/compression/streaming-compress.js +9 -0
  421. package/dist/esm/modules/archive/compression/worker-pool/pool.browser.js +26 -1
  422. package/dist/esm/modules/archive/fs/archive-file.js +78 -16
  423. package/dist/esm/modules/archive/unzip/stream.browser.js +43 -2
  424. package/dist/esm/modules/excel/chart/chart-ex-builder.js +7 -2
  425. package/dist/esm/modules/excel/chart/chart-ex-renderer.js +4 -9
  426. package/dist/esm/modules/excel/chart/chart.js +1 -7
  427. package/dist/esm/modules/excel/stream/workbook-reader.browser.js +25 -1
  428. package/dist/esm/modules/excel/stream/workbook-reader.js +9 -0
  429. package/dist/esm/modules/excel/stream/workbook-writer.browser.js +228 -13
  430. package/dist/esm/modules/excel/utils/string-buf.js +4 -81
  431. package/dist/esm/modules/excel/workbook.browser.js +135 -25
  432. package/dist/esm/modules/excel/xlsx/xform/chart/chart-space-xform.js +6 -20
  433. package/dist/esm/modules/excel/xlsx/xlsx.browser.js +32 -8
  434. package/dist/esm/modules/excel/xlsx/xlsx.js +9 -1
  435. package/dist/esm/modules/pdf/excel-bridge.js +32 -0
  436. package/dist/esm/modules/pdf/font/metrics.js +3 -237
  437. package/dist/esm/modules/pdf/index.js +1 -1
  438. package/dist/esm/modules/pdf/render-layout-to-pdf.js +647 -0
  439. package/dist/esm/modules/pdf/word-bridge.js +122 -274
  440. package/dist/esm/modules/stream/index.base.js +2 -1
  441. package/dist/esm/modules/stream/internal/sink-adapter.js +198 -0
  442. package/dist/esm/modules/stream/pull-stream.js +51 -5
  443. package/dist/esm/modules/word/advanced/diff.js +167 -0
  444. package/dist/esm/modules/word/advanced/drawing-shapes.js +268 -0
  445. package/dist/esm/modules/word/advanced/field-engine.js +1225 -0
  446. package/dist/esm/modules/word/advanced/glossary.js +79 -0
  447. package/dist/esm/modules/word/advanced/math-convert.js +595 -0
  448. package/dist/esm/modules/word/advanced/ole-objects.js +271 -0
  449. package/dist/esm/modules/word/advanced/style-map.js +322 -0
  450. package/dist/esm/modules/word/advanced/validation.js +1065 -0
  451. package/dist/esm/modules/word/advanced/vba-project.js +265 -0
  452. package/dist/esm/modules/word/bridge/excel-bridge.js +980 -0
  453. package/dist/esm/modules/word/builder/document-handle.js +664 -0
  454. package/dist/esm/modules/word/builder/paragraph-builders.js +90 -0
  455. package/dist/esm/modules/word/builder/run-builders.js +600 -0
  456. package/dist/esm/modules/word/builder/table-builders.js +45 -0
  457. package/dist/esm/modules/word/constants.js +109 -1
  458. package/dist/esm/modules/word/convert/conversion-ir.js +31 -0
  459. package/dist/esm/modules/word/convert/docx-to-semantic.js +499 -0
  460. package/dist/esm/modules/word/convert/flat-opc.js +385 -0
  461. package/dist/esm/modules/word/convert/html/html-import.js +1907 -0
  462. package/dist/{browser/modules/word → esm/modules/word/convert/html}/html-renderer.js +420 -69
  463. package/dist/esm/modules/word/convert/html/html.js +15 -0
  464. package/dist/esm/modules/word/convert/markdown/markdown-import.js +1325 -0
  465. package/dist/esm/modules/word/convert/markdown/markdown-renderer.js +634 -0
  466. package/dist/esm/modules/word/convert/markdown/markdown.js +15 -0
  467. package/dist/esm/modules/word/convert/odt/odt.js +1932 -0
  468. package/dist/esm/modules/word/core/color-utils.js +43 -0
  469. package/dist/esm/modules/word/core/internal-utils.js +209 -0
  470. package/dist/esm/modules/word/core/mapper.js +427 -0
  471. package/dist/esm/modules/word/core/opc-paths.js +48 -0
  472. package/dist/esm/modules/word/core/text-utils.js +202 -0
  473. package/dist/esm/modules/word/core/walker.js +570 -0
  474. package/dist/esm/modules/word/crypto.js +13 -7
  475. package/dist/esm/modules/word/document-io.js +80 -197
  476. package/dist/esm/modules/word/errors.js +54 -2
  477. package/dist/esm/modules/word/excel.js +13 -0
  478. package/dist/esm/modules/word/font/font-embed.js +646 -0
  479. package/dist/{browser/modules/word → esm/modules/word/font}/font-obfuscation.js +4 -9
  480. package/dist/esm/modules/word/font/hyphenation.js +4210 -0
  481. package/dist/esm/modules/word/font/text-shaping.js +635 -0
  482. package/dist/esm/modules/word/html.js +6 -5
  483. package/dist/esm/modules/word/incremental-edit.js +361 -0
  484. package/dist/esm/modules/word/index.base.js +138 -29
  485. package/dist/esm/modules/word/layout/layout-constants.js +17 -0
  486. package/dist/esm/modules/word/layout/layout-full.js +1696 -0
  487. package/dist/esm/modules/word/layout/layout-model.js +16 -0
  488. package/dist/esm/modules/word/layout/layout.js +1167 -0
  489. package/dist/esm/modules/word/layout/render-page.js +1238 -0
  490. package/dist/esm/modules/word/markdown.js +13 -0
  491. package/dist/esm/modules/word/patcher.js +537 -0
  492. package/dist/esm/modules/word/query/compat.js +58 -0
  493. package/dist/esm/modules/word/query/data-binding.js +392 -0
  494. package/dist/esm/modules/word/query/form-fields.js +268 -0
  495. package/dist/esm/modules/word/query/format-search.js +329 -0
  496. package/dist/esm/modules/word/query/mail-merge.js +111 -0
  497. package/dist/esm/modules/word/query/merge.js +617 -0
  498. package/dist/esm/modules/word/query/replace.js +301 -0
  499. package/dist/esm/modules/word/query/revisions.js +879 -0
  500. package/dist/esm/modules/word/query/search.js +346 -0
  501. package/dist/esm/modules/word/query/split.js +135 -0
  502. package/dist/esm/modules/word/query/style-resolve.js +368 -0
  503. package/dist/esm/modules/word/reader/chart-parser.js +810 -0
  504. package/dist/esm/modules/word/reader/comments-parser.js +92 -0
  505. package/dist/esm/modules/word/reader/doc-props-parsers.js +190 -0
  506. package/dist/esm/modules/word/reader/docx-reader.js +2557 -0
  507. package/dist/esm/modules/word/reader/drawing-helpers.js +84 -0
  508. package/dist/esm/modules/word/reader/form-field-parser.js +82 -0
  509. package/dist/esm/modules/word/reader/image-parsers.js +291 -0
  510. package/dist/esm/modules/word/reader/math-parser.js +422 -0
  511. package/dist/esm/modules/word/reader/metadata-parsers.js +87 -0
  512. package/dist/esm/modules/word/reader/numbering-parser.js +166 -0
  513. package/dist/esm/modules/word/reader/paragraph-section-parsers.js +503 -0
  514. package/dist/esm/modules/word/reader/parse-utils.js +249 -0
  515. package/dist/esm/modules/word/reader/properties-parsers.js +332 -0
  516. package/dist/esm/modules/word/reader/reader-context.js +61 -0
  517. package/dist/esm/modules/word/reader/sdt-helpers.js +111 -0
  518. package/dist/esm/modules/word/reader/settings-parser.js +263 -0
  519. package/dist/esm/modules/word/reader/styles-parser.js +147 -0
  520. package/dist/esm/modules/word/reader/table-properties-parsers.js +234 -0
  521. package/dist/esm/modules/word/reader/theme-parser.js +167 -0
  522. package/dist/esm/modules/word/reader/watermark-parser.js +110 -0
  523. package/dist/esm/modules/word/security/cfb-reader.js +410 -0
  524. package/dist/esm/modules/word/{digital-signatures.js → security/digital-signatures.js} +34 -34
  525. package/dist/esm/modules/word/security/document-protection.js +201 -0
  526. package/dist/esm/modules/word/security/encryption.js +602 -0
  527. package/dist/esm/modules/word/security/policy.js +102 -0
  528. package/dist/esm/modules/word/template/template-chart.js +167 -0
  529. package/dist/esm/modules/word/template/template-datasource.js +541 -0
  530. package/dist/esm/modules/word/template/template-engine.js +1435 -0
  531. package/dist/esm/modules/word/units.js +43 -14
  532. package/dist/esm/modules/word/{writers → writer}/chart-writer.js +164 -23
  533. package/dist/esm/modules/word/writer/checkbox-writer.js +79 -0
  534. package/dist/esm/modules/word/{writers → writer}/comment-writer.js +8 -6
  535. package/dist/esm/modules/word/writer/common-parts.js +101 -0
  536. package/dist/{browser/modules/word → esm/modules/word/writer}/content-types.js +14 -6
  537. package/dist/esm/modules/word/writer/document-writer.js +473 -0
  538. package/dist/esm/modules/word/writer/docx-packager.js +1515 -0
  539. package/dist/{browser/modules/word/writers → esm/modules/word/writer}/footnote-writer.js +13 -10
  540. package/dist/{browser/modules/word/writers → esm/modules/word/writer}/header-footer-writer.js +39 -21
  541. package/dist/esm/modules/word/{writers → writer}/image-writer.js +11 -7
  542. package/dist/{browser/modules/word/writers → esm/modules/word/writer}/math-writer.js +21 -1
  543. package/dist/{browser/modules/word/writers → esm/modules/word/writer}/numbering-writer.js +11 -4
  544. package/dist/esm/modules/word/{writers → writer}/paragraph-writer.js +73 -38
  545. package/dist/{browser/modules/word/writers → esm/modules/word/writer}/parts-writer.js +91 -12
  546. package/dist/esm/modules/word/writer/reference-scanners.js +111 -0
  547. package/dist/esm/modules/word/writer/relationships.js +117 -0
  548. package/dist/esm/modules/word/writer/render-context.js +46 -0
  549. package/dist/{browser/modules/word/writers → esm/modules/word/writer}/run-writer.js +126 -24
  550. package/dist/esm/modules/word/writer/sdt-writer.js +189 -0
  551. package/dist/esm/modules/word/writer/stream-buf.js +73 -0
  552. package/dist/esm/modules/word/writer/streaming-writer.js +1382 -0
  553. package/dist/esm/modules/word/writer/string-buf.js +7 -0
  554. package/dist/esm/modules/word/{writers → writer}/styles-writer.js +32 -1
  555. package/dist/esm/modules/word/{writers → writer}/table-writer.js +94 -11
  556. package/dist/esm/utils/crypto.browser.js +3 -1
  557. package/dist/esm/utils/crypto.js +4 -1
  558. package/dist/esm/utils/font-metrics.js +293 -0
  559. package/dist/esm/utils/string-buf.js +89 -0
  560. package/dist/esm/utils/theme-colors.js +120 -0
  561. package/dist/iife/excelts.iife.js +70692 -70337
  562. package/dist/iife/excelts.iife.js.map +1 -1
  563. package/dist/iife/excelts.iife.min.js +57 -57
  564. package/dist/types/modules/archive/fs/archive-file.d.ts +8 -5
  565. package/dist/types/modules/excel/chart/chart-ex-types.d.ts +0 -12
  566. package/dist/types/modules/excel/chart/chart.d.ts +1 -5
  567. package/dist/types/modules/excel/chart/types.d.ts +0 -6
  568. package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +40 -0
  569. package/dist/types/modules/excel/utils/string-buf.d.ts +5 -26
  570. package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +19 -9
  571. package/dist/types/modules/excel/xlsx/xlsx.d.ts +10 -2
  572. package/dist/types/modules/pdf/excel-bridge.d.ts +30 -1
  573. package/dist/types/modules/pdf/font/metrics.d.ts +3 -52
  574. package/dist/types/modules/pdf/index.d.ts +1 -1
  575. package/dist/types/modules/pdf/render-layout-to-pdf.d.ts +66 -0
  576. package/dist/types/modules/pdf/word-bridge.d.ts +80 -12
  577. package/dist/types/modules/stream/index.base.d.ts +2 -0
  578. package/dist/types/modules/stream/internal/sink-adapter.d.ts +65 -0
  579. package/dist/types/modules/stream/pull-stream.d.ts +19 -2
  580. package/dist/types/modules/stream/types.d.ts +13 -1
  581. package/dist/types/modules/word/advanced/diff.d.ts +61 -0
  582. package/dist/types/modules/word/advanced/drawing-shapes.d.ts +269 -0
  583. package/dist/types/modules/word/advanced/field-engine.d.ts +43 -0
  584. package/dist/types/modules/word/advanced/glossary.d.ts +86 -0
  585. package/dist/types/modules/word/advanced/math-convert.d.ts +30 -0
  586. package/dist/types/modules/word/advanced/ole-objects.d.ts +115 -0
  587. package/dist/types/modules/word/advanced/style-map.d.ts +105 -0
  588. package/dist/types/modules/word/advanced/validation.d.ts +56 -0
  589. package/dist/types/modules/word/advanced/vba-project.d.ts +91 -0
  590. package/dist/types/modules/word/bridge/excel-bridge.d.ts +127 -0
  591. package/dist/types/modules/word/builder/document-handle.d.ts +151 -0
  592. package/dist/types/modules/word/builder/paragraph-builders.d.ts +61 -0
  593. package/dist/types/modules/word/builder/run-builders.d.ts +374 -0
  594. package/dist/types/modules/word/builder/table-builders.d.ts +23 -0
  595. package/dist/types/modules/word/constants.d.ts +39 -1
  596. package/dist/types/modules/word/convert/conversion-ir.d.ts +210 -0
  597. package/dist/types/modules/word/convert/docx-to-semantic.d.ts +39 -0
  598. package/dist/types/modules/word/convert/flat-opc.d.ts +44 -0
  599. package/dist/types/modules/word/convert/html/html-import.d.ts +50 -0
  600. package/dist/{browser/modules/word → types/modules/word/convert/html}/html-renderer.d.ts +14 -1
  601. package/dist/types/modules/word/convert/html/html.d.ts +15 -0
  602. package/dist/types/modules/word/convert/markdown/markdown-import.d.ts +68 -0
  603. package/dist/types/modules/word/convert/markdown/markdown-renderer.d.ts +25 -0
  604. package/dist/types/modules/word/convert/markdown/markdown.d.ts +15 -0
  605. package/dist/types/modules/word/convert/odt/odt.d.ts +41 -0
  606. package/dist/types/modules/word/{color-utils.d.ts → core/color-utils.d.ts} +8 -1
  607. package/dist/types/modules/word/core/internal-utils.d.ts +90 -0
  608. package/dist/types/modules/word/core/mapper.d.ts +44 -0
  609. package/dist/types/modules/word/core/opc-paths.d.ts +33 -0
  610. package/dist/types/modules/word/core/text-utils.d.ts +38 -0
  611. package/dist/types/modules/word/core/walker.d.ts +119 -0
  612. package/dist/types/modules/word/crypto.d.ts +14 -9
  613. package/dist/types/modules/word/document-io.d.ts +59 -27
  614. package/dist/types/modules/word/errors.d.ts +44 -1
  615. package/dist/types/modules/word/excel.d.ts +14 -0
  616. package/dist/types/modules/word/font/font-embed.d.ts +112 -0
  617. package/dist/types/modules/word/font/hyphenation.d.ts +65 -0
  618. package/dist/types/modules/word/font/text-shaping.d.ts +58 -0
  619. package/dist/types/modules/word/html.d.ts +7 -6
  620. package/dist/types/modules/word/incremental-edit.d.ts +123 -0
  621. package/dist/types/modules/word/index.base.d.ts +194 -10
  622. package/dist/types/modules/word/layout/layout-constants.d.ts +17 -0
  623. package/dist/types/modules/word/layout/layout-full.d.ts +53 -0
  624. package/dist/types/modules/word/layout/layout-model.d.ts +344 -0
  625. package/dist/types/modules/word/layout/layout.d.ts +63 -0
  626. package/dist/types/modules/word/layout/render-page.d.ts +57 -0
  627. package/dist/types/modules/word/markdown.d.ts +14 -0
  628. package/dist/types/modules/word/patcher.d.ts +62 -0
  629. package/dist/types/modules/word/query/compat.d.ts +25 -0
  630. package/dist/types/modules/word/query/data-binding.d.ts +22 -0
  631. package/dist/types/modules/word/query/form-fields.d.ts +41 -0
  632. package/dist/types/modules/word/query/format-search.d.ts +99 -0
  633. package/dist/types/modules/word/query/mail-merge.d.ts +25 -0
  634. package/dist/types/modules/word/query/merge.d.ts +50 -0
  635. package/dist/types/modules/word/query/replace.d.ts +47 -0
  636. package/dist/types/modules/word/query/revisions.d.ts +67 -0
  637. package/dist/types/modules/word/query/search.d.ts +129 -0
  638. package/dist/types/modules/word/query/split.d.ts +44 -0
  639. package/dist/types/modules/word/query/style-resolve.d.ts +104 -0
  640. package/dist/types/modules/word/reader/chart-parser.d.ts +20 -0
  641. package/dist/types/modules/word/reader/comments-parser.d.ts +26 -0
  642. package/dist/types/modules/word/reader/doc-props-parsers.d.ts +15 -0
  643. package/dist/types/modules/word/reader/docx-reader.d.ts +27 -0
  644. package/dist/types/modules/word/reader/drawing-helpers.d.ts +27 -0
  645. package/dist/types/modules/word/reader/form-field-parser.d.ts +21 -0
  646. package/dist/types/modules/word/reader/image-parsers.d.ts +11 -0
  647. package/dist/types/modules/word/reader/math-parser.d.ts +12 -0
  648. package/dist/types/modules/word/reader/metadata-parsers.d.ts +17 -0
  649. package/dist/types/modules/word/reader/numbering-parser.d.ts +13 -0
  650. package/dist/types/modules/word/reader/paragraph-section-parsers.d.ts +12 -0
  651. package/dist/types/modules/word/reader/parse-utils.d.ts +91 -0
  652. package/dist/types/modules/word/reader/properties-parsers.d.ts +21 -0
  653. package/dist/types/modules/word/reader/reader-context.d.ts +69 -0
  654. package/dist/types/modules/word/reader/sdt-helpers.d.ts +29 -0
  655. package/dist/types/modules/word/reader/settings-parser.d.ts +8 -0
  656. package/dist/types/modules/word/reader/styles-parser.d.ts +12 -0
  657. package/dist/types/modules/word/reader/table-properties-parsers.d.ts +12 -0
  658. package/dist/types/modules/word/reader/theme-parser.d.ts +8 -0
  659. package/dist/types/modules/word/reader/watermark-parser.d.ts +15 -0
  660. package/dist/types/modules/word/security/cfb-reader.d.ts +37 -0
  661. package/dist/types/modules/word/{digital-signatures.d.ts → security/digital-signatures.d.ts} +19 -11
  662. package/dist/types/modules/word/security/document-protection.d.ts +93 -0
  663. package/dist/{browser/modules/word → types/modules/word/security}/encryption.d.ts +51 -4
  664. package/dist/types/modules/word/security/policy.d.ts +80 -0
  665. package/dist/types/modules/word/template/template-chart.d.ts +56 -0
  666. package/dist/types/modules/word/template/template-datasource.d.ts +154 -0
  667. package/dist/types/modules/word/template/template-engine.d.ts +121 -0
  668. package/dist/types/modules/word/types.d.ts +224 -25
  669. package/dist/types/modules/word/units.d.ts +26 -0
  670. package/dist/types/modules/word/writer/checkbox-writer.d.ts +17 -0
  671. package/dist/{browser/modules/word/writers → types/modules/word/writer}/comment-writer.d.ts +2 -1
  672. package/dist/types/modules/word/writer/common-parts.d.ts +57 -0
  673. package/dist/{browser/modules/word → types/modules/word/writer}/content-types.d.ts +2 -2
  674. package/dist/types/modules/word/writer/document-writer.d.ts +24 -0
  675. package/dist/types/modules/word/writer/docx-packager.d.ts +35 -0
  676. package/dist/{browser/modules/word/writers → types/modules/word/writer}/footnote-writer.d.ts +3 -2
  677. package/dist/{browser/modules/word/writers → types/modules/word/writer}/header-footer-writer.d.ts +3 -2
  678. package/dist/{browser/modules/word/writers → types/modules/word/writer}/image-writer.d.ts +1 -1
  679. package/dist/types/modules/word/writer/math-writer.d.ts +20 -0
  680. package/dist/types/modules/word/{writers → writer}/numbering-writer.d.ts +1 -1
  681. package/dist/types/modules/word/{writers → writer}/paragraph-writer.d.ts +2 -1
  682. package/dist/types/modules/word/{writers → writer}/parts-writer.d.ts +3 -3
  683. package/dist/types/modules/word/writer/reference-scanners.d.ts +42 -0
  684. package/dist/types/modules/word/writer/relationships.d.ts +52 -0
  685. package/dist/types/modules/word/writer/render-context.d.ts +124 -0
  686. package/dist/types/modules/word/{writers → writer}/run-writer.d.ts +10 -1
  687. package/dist/types/modules/word/writer/sdt-writer.d.ts +25 -0
  688. package/dist/types/modules/word/writer/stream-buf.d.ts +37 -0
  689. package/dist/types/modules/word/writer/streaming-writer.d.ts +344 -0
  690. package/dist/types/modules/word/writer/string-buf.d.ts +8 -0
  691. package/dist/types/modules/word/{writers → writer}/table-writer.d.ts +2 -1
  692. package/dist/types/modules/xml/types.d.ts +22 -0
  693. package/dist/types/utils/crypto.browser.d.ts +3 -1
  694. package/dist/types/utils/crypto.d.ts +4 -1
  695. package/dist/types/utils/font-metrics.d.ts +63 -0
  696. package/dist/types/utils/string-buf.d.ts +42 -0
  697. package/dist/types/utils/theme-colors.d.ts +55 -0
  698. package/package.json +121 -39
  699. package/dist/browser/modules/word/color-utils.js +0 -94
  700. package/dist/browser/modules/word/document.d.ts +0 -657
  701. package/dist/browser/modules/word/document.js +0 -1533
  702. package/dist/browser/modules/word/docx-packager.d.ts +0 -14
  703. package/dist/browser/modules/word/docx-packager.js +0 -822
  704. package/dist/browser/modules/word/docx-reader.d.ts +0 -11
  705. package/dist/browser/modules/word/docx-reader.js +0 -4929
  706. package/dist/browser/modules/word/encryption.js +0 -274
  707. package/dist/browser/modules/word/internal-utils.d.ts +0 -23
  708. package/dist/browser/modules/word/internal-utils.js +0 -54
  709. package/dist/browser/modules/word/namespaces.d.ts +0 -159
  710. package/dist/browser/modules/word/namespaces.js +0 -189
  711. package/dist/browser/modules/word/relationships.d.ts +0 -30
  712. package/dist/browser/modules/word/relationships.js +0 -48
  713. package/dist/browser/modules/word/writers/checkbox-writer.d.ts +0 -9
  714. package/dist/browser/modules/word/writers/checkbox-writer.js +0 -42
  715. package/dist/browser/modules/word/writers/document-writer.d.ts +0 -16
  716. package/dist/browser/modules/word/writers/document-writer.js +0 -461
  717. package/dist/browser/modules/word/writers/math-writer.d.ts +0 -9
  718. package/dist/cjs/modules/word/color-utils.js +0 -97
  719. package/dist/cjs/modules/word/document.js +0 -1645
  720. package/dist/cjs/modules/word/docx-packager.js +0 -825
  721. package/dist/cjs/modules/word/docx-reader.js +0 -4932
  722. package/dist/cjs/modules/word/encryption.js +0 -282
  723. package/dist/cjs/modules/word/internal-utils.js +0 -59
  724. package/dist/cjs/modules/word/namespaces.js +0 -192
  725. package/dist/cjs/modules/word/relationships.js +0 -55
  726. package/dist/cjs/modules/word/writers/checkbox-writer.js +0 -45
  727. package/dist/cjs/modules/word/writers/document-writer.js +0 -465
  728. package/dist/esm/modules/word/color-utils.js +0 -94
  729. package/dist/esm/modules/word/document.js +0 -1533
  730. package/dist/esm/modules/word/docx-packager.js +0 -822
  731. package/dist/esm/modules/word/docx-reader.js +0 -4929
  732. package/dist/esm/modules/word/encryption.js +0 -274
  733. package/dist/esm/modules/word/internal-utils.js +0 -54
  734. package/dist/esm/modules/word/namespaces.js +0 -189
  735. package/dist/esm/modules/word/relationships.js +0 -48
  736. package/dist/esm/modules/word/writers/checkbox-writer.js +0 -42
  737. package/dist/esm/modules/word/writers/document-writer.js +0 -461
  738. package/dist/types/modules/word/document.d.ts +0 -657
  739. package/dist/types/modules/word/docx-packager.d.ts +0 -14
  740. package/dist/types/modules/word/docx-reader.d.ts +0 -11
  741. package/dist/types/modules/word/internal-utils.d.ts +0 -23
  742. package/dist/types/modules/word/namespaces.d.ts +0 -159
  743. package/dist/types/modules/word/relationships.d.ts +0 -30
  744. package/dist/types/modules/word/writers/checkbox-writer.d.ts +0 -9
  745. package/dist/types/modules/word/writers/document-writer.d.ts +0 -16
  746. package/dist/types/modules/word/writers/math-writer.d.ts +0 -9
  747. /package/dist/browser/modules/word/{font-obfuscation.d.ts → font/font-obfuscation.d.ts} +0 -0
  748. /package/dist/browser/modules/word/{writers → writer}/chart-writer.d.ts +0 -0
  749. /package/dist/browser/modules/word/{writers → writer}/section-writer.d.ts +0 -0
  750. /package/dist/browser/modules/word/{writers → writer}/section-writer.js +0 -0
  751. /package/dist/browser/modules/word/{writers → writer}/styles-writer.d.ts +0 -0
  752. /package/dist/browser/modules/word/{writers → writer}/textbox-writer.d.ts +0 -0
  753. /package/dist/browser/modules/word/{writers → writer}/textbox-writer.js +0 -0
  754. /package/dist/browser/modules/word/{writers → writer}/toc-writer.d.ts +0 -0
  755. /package/dist/browser/modules/word/{writers → writer}/toc-writer.js +0 -0
  756. /package/dist/cjs/modules/word/{writers → writer}/section-writer.js +0 -0
  757. /package/dist/cjs/modules/word/{writers → writer}/textbox-writer.js +0 -0
  758. /package/dist/cjs/modules/word/{writers → writer}/toc-writer.js +0 -0
  759. /package/dist/esm/modules/word/{writers → writer}/section-writer.js +0 -0
  760. /package/dist/esm/modules/word/{writers → writer}/textbox-writer.js +0 -0
  761. /package/dist/esm/modules/word/{writers → writer}/toc-writer.js +0 -0
  762. /package/dist/types/modules/word/{font-obfuscation.d.ts → font/font-obfuscation.d.ts} +0 -0
  763. /package/dist/types/modules/word/{writers → writer}/chart-writer.d.ts +0 -0
  764. /package/dist/types/modules/word/{writers → writer}/section-writer.d.ts +0 -0
  765. /package/dist/types/modules/word/{writers → writer}/styles-writer.d.ts +0 -0
  766. /package/dist/types/modules/word/{writers → writer}/textbox-writer.d.ts +0 -0
  767. /package/dist/types/modules/word/{writers → writer}/toc-writer.d.ts +0 -0
@@ -0,0 +1,1932 @@
1
+ /**
2
+ * DOCX Module - OpenDocument Text (ODT) Format Support
3
+ *
4
+ * Implements reading and writing of ODT (OpenDocument Text) files.
5
+ * ODT files are ZIP archives containing XML content in ODF namespaces.
6
+ *
7
+ * Main archive structure:
8
+ * - content.xml — document body and automatic styles
9
+ * - styles.xml — named styles, page layout, master pages
10
+ * - meta.xml — document metadata
11
+ * - META-INF/manifest.xml — manifest of all archive entries
12
+ * - Pictures/ — embedded images
13
+ *
14
+ * @stability experimental
15
+ */
16
+ import { zip } from "../../../archive/create-archive.js";
17
+ import { unzip } from "../../../archive/read-archive.js";
18
+ import { parseXml, findChild, findChildren, textContent } from "../../../xml/dom.js";
19
+ import { XmlWriter } from "../../../xml/writer.js";
20
+ import { sanitizeUrl, utf8Decoder, utf8Encoder } from "../../core/internal-utils.js";
21
+ import { isRun } from "../../core/text-utils.js";
22
+ import { DocxParseError } from "../../errors.js";
23
+ import { EMU_PER_CM, EMU_PER_INCH, EMU_PER_POINT, EMU_PER_PX } from "../../units.js";
24
+ // =============================================================================
25
+ // ODF Namespace Constants
26
+ // =============================================================================
27
+ /** ODF namespace URIs. */
28
+ const NS = {
29
+ office: "urn:oasis:names:tc:opendocument:xmlns:office:1.0",
30
+ style: "urn:oasis:names:tc:opendocument:xmlns:style:1.0",
31
+ text: "urn:oasis:names:tc:opendocument:xmlns:text:1.0",
32
+ table: "urn:oasis:names:tc:opendocument:xmlns:table:1.0",
33
+ draw: "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0",
34
+ fo: "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
35
+ xlink: "http://www.w3.org/1999/xlink",
36
+ dc: "http://purl.org/dc/elements/1.1/",
37
+ meta: "urn:oasis:names:tc:opendocument:xmlns:meta:1.0",
38
+ svg: "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0",
39
+ manifest: "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
40
+ };
41
+ // =============================================================================
42
+ // Unit Conversion Helpers
43
+ // =============================================================================
44
+ /** Parse an ODF length value (e.g. "1.27cm", "0.5in", "12pt") to twips. */
45
+ function odfLengthToTwips(value) {
46
+ if (!value) {
47
+ return undefined;
48
+ }
49
+ const match = value.match(/^(-?\d+(?:\.\d+)?)\s*(cm|mm|in|pt|pc|px|em)$/);
50
+ if (!match) {
51
+ return undefined;
52
+ }
53
+ const num = parseFloat(match[1]);
54
+ const unit = match[2];
55
+ switch (unit) {
56
+ case "cm":
57
+ return Math.round(num * 567);
58
+ case "mm":
59
+ return Math.round(num * 56.7);
60
+ case "in":
61
+ return Math.round(num * 1440);
62
+ case "pt":
63
+ return Math.round(num * 20);
64
+ case "pc":
65
+ return Math.round(num * 240);
66
+ case "px":
67
+ // Approximate: 1px ≈ 0.75pt at 96dpi
68
+ return Math.round(num * 15);
69
+ case "em":
70
+ // Approximate: 1em ≈ 12pt
71
+ return Math.round(num * 240);
72
+ default:
73
+ return undefined;
74
+ }
75
+ }
76
+ /** Parse an ODF length value to EMU (English Metric Units). */
77
+ function odfLengthToEmu(value) {
78
+ if (!value) {
79
+ return undefined;
80
+ }
81
+ const match = value.match(/^(-?\d+(?:\.\d+)?)\s*(cm|mm|in|pt|pc|px)$/);
82
+ if (!match) {
83
+ return undefined;
84
+ }
85
+ const num = parseFloat(match[1]);
86
+ const unit = match[2];
87
+ switch (unit) {
88
+ case "cm":
89
+ return Math.round(num * EMU_PER_CM);
90
+ case "mm":
91
+ return Math.round((num * EMU_PER_CM) / 10);
92
+ case "in":
93
+ return Math.round(num * EMU_PER_INCH);
94
+ case "pt":
95
+ return Math.round(num * EMU_PER_POINT);
96
+ case "pc":
97
+ return Math.round(num * EMU_PER_POINT * 12); // 1 pica = 12 points
98
+ case "px":
99
+ return Math.round(num * EMU_PER_PX);
100
+ default:
101
+ return undefined;
102
+ }
103
+ }
104
+ /** Convert twips to ODF length string (cm). */
105
+ function twipsToCm(twips) {
106
+ return (twips / 567).toFixed(3) + "cm";
107
+ }
108
+ /** Convert EMU to ODF length string (cm). */
109
+ function emuToCm(emu) {
110
+ return (emu / EMU_PER_CM).toFixed(3) + "cm";
111
+ }
112
+ /** Convert half-points to pt string. */
113
+ function halfPointsToPt(hp) {
114
+ return (hp / 2).toString() + "pt";
115
+ }
116
+ /** Parse a font size string (e.g. "12pt") to half-points. */
117
+ function parseFontSizeToHalfPoints(value) {
118
+ if (!value) {
119
+ return undefined;
120
+ }
121
+ const match = value.match(/^(\d+(?:\.\d+)?)\s*pt$/);
122
+ if (!match) {
123
+ return undefined;
124
+ }
125
+ return Math.round(parseFloat(match[1]) * 2);
126
+ }
127
+ /** Parse a 6-digit hex color from ODF format (#RRGGBB). */
128
+ function parseOdfColor(value) {
129
+ if (!value) {
130
+ return undefined;
131
+ }
132
+ const match = value.match(/^#([0-9a-fA-F]{6})$/);
133
+ if (!match) {
134
+ return undefined;
135
+ }
136
+ return match[1].toUpperCase();
137
+ }
138
+ /** Convert a hex color to ODF format (#RRGGBB). */
139
+ function colorToOdf(hex) {
140
+ return `#${hex}`;
141
+ }
142
+ // =============================================================================
143
+ // XML Element Query Helpers (Namespace-Aware)
144
+ // =============================================================================
145
+ /**
146
+ * Find a child element by namespace-prefixed name.
147
+ * Tries both "prefix:local" and just "local" for flexibility.
148
+ */
149
+ function findNsChild(el, prefix, local) {
150
+ return findChild(el, `${prefix}:${local}`) ?? findChild(el, local);
151
+ }
152
+ /** Find all children by namespace-prefixed name. */
153
+ function findNsChildren(el, prefix, local) {
154
+ const result = findChildren(el, `${prefix}:${local}`);
155
+ if (result.length > 0) {
156
+ return result;
157
+ }
158
+ return findChildren(el, local);
159
+ }
160
+ /** Get an attribute value with a namespace prefix. */
161
+ function nsAttr(el, prefix, local) {
162
+ return el.attributes[`${prefix}:${local}`] ?? el.attributes[local];
163
+ }
164
+ // =============================================================================
165
+ // ODF Style Parsing
166
+ // =============================================================================
167
+ /** Parse style properties from a style:style element. */
168
+ function parseOdfStyle(el) {
169
+ const name = nsAttr(el, "style", "name") ?? "";
170
+ const family = nsAttr(el, "style", "family") ?? "";
171
+ const parentStyle = nsAttr(el, "style", "parent-style-name");
172
+ let paragraphProperties;
173
+ let textProperties;
174
+ let tableProperties;
175
+ let tableColumnProperties;
176
+ let tableCellProperties;
177
+ const pPropsEl = findNsChild(el, "style", "paragraph-properties");
178
+ if (pPropsEl) {
179
+ paragraphProperties = {
180
+ textAlign: nsAttr(pPropsEl, "fo", "text-align"),
181
+ marginTop: nsAttr(pPropsEl, "fo", "margin-top"),
182
+ marginBottom: nsAttr(pPropsEl, "fo", "margin-bottom"),
183
+ marginLeft: nsAttr(pPropsEl, "fo", "margin-left"),
184
+ marginRight: nsAttr(pPropsEl, "fo", "margin-right"),
185
+ textIndent: nsAttr(pPropsEl, "fo", "text-indent"),
186
+ lineHeight: nsAttr(pPropsEl, "fo", "line-height"),
187
+ breakBefore: nsAttr(pPropsEl, "fo", "break-before"),
188
+ keepWithNext: nsAttr(pPropsEl, "fo", "keep-with-next")
189
+ };
190
+ }
191
+ const tPropsEl = findNsChild(el, "style", "text-properties");
192
+ if (tPropsEl) {
193
+ textProperties = {
194
+ fontName: nsAttr(tPropsEl, "style", "font-name") ?? nsAttr(tPropsEl, "fo", "font-family"),
195
+ fontSize: nsAttr(tPropsEl, "fo", "font-size"),
196
+ fontWeight: nsAttr(tPropsEl, "fo", "font-weight"),
197
+ fontStyle: nsAttr(tPropsEl, "fo", "font-style"),
198
+ textDecoration: nsAttr(tPropsEl, "style", "text-underline-style") ??
199
+ nsAttr(tPropsEl, "style", "text-line-through-style"),
200
+ color: nsAttr(tPropsEl, "fo", "color"),
201
+ backgroundColor: nsAttr(tPropsEl, "fo", "background-color"),
202
+ textPosition: nsAttr(tPropsEl, "style", "text-position"),
203
+ fontVariant: nsAttr(tPropsEl, "fo", "font-variant"),
204
+ letterSpacing: nsAttr(tPropsEl, "fo", "letter-spacing")
205
+ };
206
+ }
207
+ const tablePropsEl = findNsChild(el, "style", "table-properties");
208
+ if (tablePropsEl) {
209
+ tableProperties = {
210
+ width: nsAttr(tablePropsEl, "style", "width"),
211
+ align: nsAttr(tablePropsEl, "table", "align") ?? nsAttr(tablePropsEl, "fo", "margin-left")
212
+ };
213
+ }
214
+ const tableColPropsEl = findNsChild(el, "style", "table-column-properties");
215
+ if (tableColPropsEl) {
216
+ tableColumnProperties = {
217
+ columnWidth: nsAttr(tableColPropsEl, "style", "column-width")
218
+ };
219
+ }
220
+ const tableCellPropsEl = findNsChild(el, "style", "table-cell-properties");
221
+ if (tableCellPropsEl) {
222
+ tableCellProperties = {
223
+ padding: nsAttr(tableCellPropsEl, "fo", "padding"),
224
+ borderTop: nsAttr(tableCellPropsEl, "fo", "border-top"),
225
+ borderBottom: nsAttr(tableCellPropsEl, "fo", "border-bottom"),
226
+ borderLeft: nsAttr(tableCellPropsEl, "fo", "border-left"),
227
+ borderRight: nsAttr(tableCellPropsEl, "fo", "border-right"),
228
+ backgroundColor: nsAttr(tableCellPropsEl, "fo", "background-color"),
229
+ verticalAlign: nsAttr(tableCellPropsEl, "style", "vertical-align")
230
+ };
231
+ }
232
+ return {
233
+ name,
234
+ family,
235
+ parentStyle,
236
+ paragraphProperties,
237
+ textProperties,
238
+ tableProperties,
239
+ tableColumnProperties,
240
+ tableCellProperties
241
+ };
242
+ }
243
+ /** Parse page layout properties from a style:page-layout element. */
244
+ function parsePageLayout(el) {
245
+ const propsEl = findNsChild(el, "style", "page-layout-properties");
246
+ if (!propsEl) {
247
+ return undefined;
248
+ }
249
+ return {
250
+ pageWidth: nsAttr(propsEl, "fo", "page-width"),
251
+ pageHeight: nsAttr(propsEl, "fo", "page-height"),
252
+ marginTop: nsAttr(propsEl, "fo", "margin-top"),
253
+ marginBottom: nsAttr(propsEl, "fo", "margin-bottom"),
254
+ marginLeft: nsAttr(propsEl, "fo", "margin-left"),
255
+ marginRight: nsAttr(propsEl, "fo", "margin-right")
256
+ };
257
+ }
258
+ // =============================================================================
259
+ // ODF Content Parsing → DocxDocument Model
260
+ // =============================================================================
261
+ /** Convert ODF text alignment to OOXML alignment. */
262
+ function odfAlignToAlignment(align) {
263
+ if (!align) {
264
+ return undefined;
265
+ }
266
+ switch (align) {
267
+ case "start":
268
+ case "left":
269
+ return "left";
270
+ case "center":
271
+ return "center";
272
+ case "end":
273
+ case "right":
274
+ return "right";
275
+ case "justify":
276
+ return "both";
277
+ default:
278
+ return undefined;
279
+ }
280
+ }
281
+ /** Convert ODF paragraph properties to OOXML ParagraphProperties. */
282
+ function odfParagraphPropsToDocx(props, styleName) {
283
+ if (!props && !styleName) {
284
+ return undefined;
285
+ }
286
+ const alignment = odfAlignToAlignment(props?.textAlign);
287
+ let indent;
288
+ const leftTwips = odfLengthToTwips(props?.marginLeft);
289
+ const rightTwips = odfLengthToTwips(props?.marginRight);
290
+ const firstLineTwips = odfLengthToTwips(props?.textIndent);
291
+ if (leftTwips !== undefined || rightTwips !== undefined || firstLineTwips !== undefined) {
292
+ indent = {
293
+ ...(leftTwips !== undefined ? { left: leftTwips } : {}),
294
+ ...(rightTwips !== undefined ? { right: rightTwips } : {}),
295
+ ...(firstLineTwips !== undefined
296
+ ? firstLineTwips >= 0
297
+ ? { firstLine: firstLineTwips }
298
+ : { hanging: -firstLineTwips }
299
+ : {})
300
+ };
301
+ }
302
+ let spacing;
303
+ const beforeTwips = odfLengthToTwips(props?.marginTop);
304
+ const afterTwips = odfLengthToTwips(props?.marginBottom);
305
+ if (beforeTwips !== undefined || afterTwips !== undefined) {
306
+ spacing = {
307
+ ...(beforeTwips !== undefined ? { before: beforeTwips } : {}),
308
+ ...(afterTwips !== undefined ? { after: afterTwips } : {})
309
+ };
310
+ }
311
+ const keepNext = props?.keepWithNext === "always" || props?.keepWithNext === "true" ? true : undefined;
312
+ const pageBreakBefore = props?.breakBefore === "page" ? true : undefined;
313
+ const result = {
314
+ ...(styleName ? { style: styleName } : {}),
315
+ ...(alignment ? { alignment } : {}),
316
+ ...(indent ? { indent } : {}),
317
+ ...(spacing ? { spacing } : {}),
318
+ ...(keepNext !== undefined ? { keepNext } : {}),
319
+ ...(pageBreakBefore !== undefined ? { pageBreakBefore } : {})
320
+ };
321
+ return Object.keys(result).length > 0 ? result : undefined;
322
+ }
323
+ /** Convert ODF text properties to OOXML RunProperties. */
324
+ function odfTextPropsToDocx(props) {
325
+ if (!props) {
326
+ return undefined;
327
+ }
328
+ const font = props.fontName || undefined;
329
+ const size = parseFontSizeToHalfPoints(props.fontSize);
330
+ const bold = props.fontWeight === "bold" ? true : undefined;
331
+ const italic = props.fontStyle === "italic" ? true : undefined;
332
+ let underline;
333
+ if (props.textDecoration && props.textDecoration !== "none") {
334
+ underline = "single";
335
+ }
336
+ let strike;
337
+ if (props.textDecoration === "line-through") {
338
+ strike = true;
339
+ underline = undefined;
340
+ }
341
+ const color = parseOdfColor(props.color);
342
+ const smallCaps = props.fontVariant === "small-caps" ? true : undefined;
343
+ const spacingTwips = odfLengthToTwips(props.letterSpacing);
344
+ let vertAlign;
345
+ if (props.textPosition) {
346
+ if (props.textPosition.startsWith("super") || props.textPosition.startsWith("33%")) {
347
+ vertAlign = "superscript";
348
+ }
349
+ else if (props.textPosition.startsWith("sub") || props.textPosition.startsWith("-33%")) {
350
+ vertAlign = "subscript";
351
+ }
352
+ }
353
+ const result = {
354
+ ...(font ? { font } : {}),
355
+ ...(size !== undefined ? { size } : {}),
356
+ ...(bold !== undefined ? { bold } : {}),
357
+ ...(italic !== undefined ? { italic } : {}),
358
+ ...(underline !== undefined ? { underline } : {}),
359
+ ...(strike !== undefined ? { strike } : {}),
360
+ ...(color !== undefined ? { color } : {}),
361
+ ...(smallCaps !== undefined ? { smallCaps } : {}),
362
+ ...(spacingTwips !== undefined ? { spacing: spacingTwips } : {}),
363
+ ...(vertAlign !== undefined ? { vertAlign } : {})
364
+ };
365
+ return Object.keys(result).length > 0 ? result : undefined;
366
+ }
367
+ /** Parse inline text spans (text:span) within a paragraph. */
368
+ function parseTextSpan(el, styles) {
369
+ const styleName = nsAttr(el, "text", "style-name");
370
+ const style = styleName ? styles.get(styleName) : undefined;
371
+ const runProps = odfTextPropsToDocx(style?.textProperties);
372
+ const content = [];
373
+ for (const child of el.children) {
374
+ if (child.type === "text") {
375
+ if (child.value) {
376
+ content.push({ type: "text", text: child.value });
377
+ }
378
+ }
379
+ else if (child.type === "element") {
380
+ const local = getLocalName(child.name);
381
+ if (local === "s") {
382
+ // text:s — multiple spaces
383
+ const count = parseInt(nsAttr(child, "text", "c") ?? "1", 10);
384
+ content.push({ type: "text", text: " ".repeat(count) });
385
+ }
386
+ else if (local === "tab") {
387
+ content.push({ type: "tab" });
388
+ }
389
+ else if (local === "line-break") {
390
+ content.push({ type: "break" });
391
+ }
392
+ else if (local === "span") {
393
+ // Nested span — flatten into current run content
394
+ const nestedRun = parseTextSpan(child, styles);
395
+ content.push(...nestedRun.content);
396
+ }
397
+ }
398
+ }
399
+ return {
400
+ ...(runProps ? { properties: runProps } : {}),
401
+ content
402
+ };
403
+ }
404
+ /** Parse a text:p or text:h element into a Paragraph. */
405
+ function parseParagraph(el, styles, listContext) {
406
+ const styleName = nsAttr(el, "text", "style-name");
407
+ const style = styleName ? styles.get(styleName) : undefined;
408
+ const isHeading = getLocalName(el.name) === "h";
409
+ const outlineLevel = isHeading
410
+ ? parseInt(nsAttr(el, "text", "outline-level") ?? "1", 10)
411
+ : undefined;
412
+ let paraProps = odfParagraphPropsToDocx(style?.paragraphProperties, styleName);
413
+ // If this is a heading, add outline level
414
+ if (outlineLevel !== undefined && outlineLevel >= 1 && outlineLevel <= 9) {
415
+ paraProps = {
416
+ ...paraProps,
417
+ outlineLevel: outlineLevel - 1,
418
+ style: styleName ?? `Heading${outlineLevel}`
419
+ };
420
+ }
421
+ // If inside a list, add numbering reference
422
+ if (listContext) {
423
+ paraProps = {
424
+ ...paraProps,
425
+ numbering: { numId: 1, level: listContext.level }
426
+ };
427
+ }
428
+ const children = [];
429
+ for (const child of el.children) {
430
+ if (child.type === "text") {
431
+ if (child.value) {
432
+ children.push({
433
+ content: [{ type: "text", text: child.value }]
434
+ });
435
+ }
436
+ }
437
+ else if (child.type === "element") {
438
+ const local = getLocalName(child.name);
439
+ if (local === "span") {
440
+ children.push(parseTextSpan(child, styles));
441
+ }
442
+ else if (local === "a") {
443
+ // Hyperlink. Pass the href through sanitizeUrl so dangerous
444
+ // schemes (javascript:, vbscript:, data: with executable payloads,
445
+ // etc.) coming from an untrusted ODT do not survive into the DOCX
446
+ // model. Unsafe links degrade to plain text — the caller still sees
447
+ // the link's children but no clickable hyperlink is created.
448
+ const rawHref = nsAttr(child, "xlink", "href");
449
+ const safeHref = sanitizeUrl(rawHref);
450
+ const runs = [];
451
+ for (const linkChild of child.children) {
452
+ if (linkChild.type === "text") {
453
+ if (linkChild.value) {
454
+ runs.push({ content: [{ type: "text", text: linkChild.value }] });
455
+ }
456
+ }
457
+ else if (linkChild.type === "element" && getLocalName(linkChild.name) === "span") {
458
+ runs.push(parseTextSpan(linkChild, styles));
459
+ }
460
+ }
461
+ if (safeHref) {
462
+ children.push({
463
+ type: "hyperlink",
464
+ url: safeHref,
465
+ children: runs
466
+ });
467
+ }
468
+ else if (rawHref && rawHref.startsWith("#")) {
469
+ // Internal anchor — sanitizeUrl rejects fragment-only URLs but
470
+ // they're safe and meaningful. Preserve as a Hyperlink with
471
+ // `anchor` set, mirroring how DOCX represents in-document
472
+ // links.
473
+ children.push({
474
+ type: "hyperlink",
475
+ anchor: rawHref.slice(1),
476
+ children: runs
477
+ });
478
+ }
479
+ else {
480
+ // Drop the wrapper — preserve text content as plain runs so no
481
+ // user-visible content disappears.
482
+ for (const r of runs) {
483
+ children.push(r);
484
+ }
485
+ }
486
+ }
487
+ else if (local === "s") {
488
+ const count = parseInt(nsAttr(child, "text", "c") ?? "1", 10);
489
+ children.push({ content: [{ type: "text", text: " ".repeat(count) }] });
490
+ }
491
+ else if (local === "tab") {
492
+ children.push({ content: [{ type: "tab" }] });
493
+ }
494
+ else if (local === "line-break") {
495
+ children.push({ content: [{ type: "break" }] });
496
+ }
497
+ else if (local === "frame") {
498
+ // Inline image frame
499
+ const imageRun = parseDrawFrame(child);
500
+ if (imageRun) {
501
+ children.push(imageRun);
502
+ }
503
+ }
504
+ else if (local === "bookmark-start") {
505
+ const bkName = nsAttr(child, "text", "name") ?? "";
506
+ children.push({ type: "bookmarkStart", id: 0, name: bkName });
507
+ }
508
+ else if (local === "bookmark-end") {
509
+ children.push({ type: "bookmarkEnd", id: 0 });
510
+ }
511
+ else if (local === "note") {
512
+ // Footnote/endnote — extract text content
513
+ const noteBodyEl = findNsChild(child, "text", "note-body");
514
+ if (noteBodyEl) {
515
+ const noteText = textContent(noteBodyEl);
516
+ if (noteText) {
517
+ children.push({ content: [{ type: "text", text: `[${noteText}]` }] });
518
+ }
519
+ }
520
+ }
521
+ }
522
+ }
523
+ return {
524
+ type: "paragraph",
525
+ ...(paraProps ? { properties: paraProps } : {}),
526
+ children
527
+ };
528
+ }
529
+ /** Parse a draw:frame element for inline images. */
530
+ function parseDrawFrame(el) {
531
+ const width = odfLengthToEmu(nsAttr(el, "svg", "width"));
532
+ const height = odfLengthToEmu(nsAttr(el, "svg", "height"));
533
+ const name = nsAttr(el, "draw", "name");
534
+ const imageEl = findNsChild(el, "draw", "image") ?? findChild(el, "draw:image") ?? findChild(el, "image");
535
+ if (!imageEl) {
536
+ return undefined;
537
+ }
538
+ const href = nsAttr(imageEl, "xlink", "href");
539
+ if (!href) {
540
+ return undefined;
541
+ }
542
+ const imageContent = {
543
+ type: "image",
544
+ rId: href, // Use the path as rId placeholder; resolved during image collection
545
+ width: width ?? EMU_PER_INCH, // Default 1 inch
546
+ height: height ?? EMU_PER_INCH,
547
+ ...(name ? { name } : {})
548
+ };
549
+ return {
550
+ content: [imageContent]
551
+ };
552
+ }
553
+ /** Parse a text:list element. */
554
+ function parseList(el, styles, parentLevel) {
555
+ const listStyleName = nsAttr(el, "text", "style-name");
556
+ const paragraphs = [];
557
+ const level = parentLevel;
558
+ const items = findNsChildren(el, "text", "list-item");
559
+ for (const item of items) {
560
+ for (const child of item.children) {
561
+ if (child.type !== "element") {
562
+ continue;
563
+ }
564
+ const local = getLocalName(child.name);
565
+ if (local === "p" || local === "h") {
566
+ paragraphs.push(parseParagraph(child, styles, { listStyleName, level }));
567
+ }
568
+ else if (local === "list") {
569
+ // Nested list
570
+ paragraphs.push(...parseList(child, styles, level + 1));
571
+ }
572
+ }
573
+ }
574
+ return paragraphs;
575
+ }
576
+ /** Parse a table:table element into a Table. */
577
+ function parseTable(el, styles) {
578
+ const tableStyleName = nsAttr(el, "table", "style-name");
579
+ const tableStyle = tableStyleName ? styles.get(tableStyleName) : undefined;
580
+ // Parse column definitions for widths
581
+ const columnWidths = [];
582
+ const colElements = findNsChildren(el, "table", "table-column");
583
+ for (const colEl of colElements) {
584
+ const colStyleName = nsAttr(colEl, "table", "style-name");
585
+ const colStyle = colStyleName ? styles.get(colStyleName) : undefined;
586
+ const width = odfLengthToTwips(colStyle?.tableColumnProperties?.columnWidth);
587
+ const repeatCount = parseInt(nsAttr(colEl, "table", "number-columns-repeated") ?? "1", 10);
588
+ for (let i = 0; i < repeatCount; i++) {
589
+ columnWidths.push(width ?? 2000);
590
+ }
591
+ }
592
+ // Parse rows
593
+ const rows = [];
594
+ const rowElements = findNsChildren(el, "table", "table-row");
595
+ for (const rowEl of rowElements) {
596
+ const cells = [];
597
+ // Iterate in document order (preserve natural order)
598
+ const orderedCells = [];
599
+ for (const child of rowEl.children) {
600
+ if (child.type === "element") {
601
+ const childLocal = getLocalName(child.name);
602
+ if (childLocal === "table-cell" || childLocal === "covered-table-cell") {
603
+ orderedCells.push(child);
604
+ }
605
+ }
606
+ }
607
+ for (const cellEl of orderedCells) {
608
+ const cellLocal = getLocalName(cellEl.name);
609
+ const isCovered = cellLocal === "covered-table-cell";
610
+ const gridSpan = parseInt(nsAttr(cellEl, "table", "number-columns-spanned") ?? "1", 10);
611
+ const rowSpan = parseInt(nsAttr(cellEl, "table", "number-rows-spanned") ?? "1", 10);
612
+ const cellProps = {
613
+ ...(gridSpan > 1 ? { gridSpan } : {}),
614
+ ...(rowSpan > 1 ? { rowSpan } : {}),
615
+ ...(isCovered ? { verticalMerge: "continue" } : {})
616
+ };
617
+ // Parse cell content
618
+ const cellContent = [];
619
+ for (const cellChild of cellEl.children) {
620
+ if (cellChild.type !== "element") {
621
+ continue;
622
+ }
623
+ const cellChildLocal = getLocalName(cellChild.name);
624
+ if (cellChildLocal === "p" || cellChildLocal === "h") {
625
+ cellContent.push(parseParagraph(cellChild, styles));
626
+ }
627
+ else if (cellChildLocal === "table") {
628
+ cellContent.push(parseTable(cellChild, styles));
629
+ }
630
+ else if (cellChildLocal === "list") {
631
+ cellContent.push(...parseList(cellChild, styles, 0));
632
+ }
633
+ }
634
+ // Ensure at least one paragraph per cell
635
+ if (cellContent.length === 0) {
636
+ cellContent.push({ type: "paragraph", children: [] });
637
+ }
638
+ cells.push({
639
+ ...(Object.keys(cellProps).length > 0 ? { properties: cellProps } : {}),
640
+ content: cellContent
641
+ });
642
+ }
643
+ rows.push({ cells });
644
+ }
645
+ // Build table properties
646
+ let tableProps;
647
+ const widthTwips = odfLengthToTwips(tableStyle?.tableProperties?.width);
648
+ if (widthTwips !== undefined || tableStyleName) {
649
+ tableProps = {
650
+ ...(tableStyleName ? { style: tableStyleName } : {}),
651
+ ...(widthTwips !== undefined ? { width: { value: widthTwips, type: "dxa" } } : {})
652
+ };
653
+ }
654
+ return {
655
+ type: "table",
656
+ ...(tableProps ? { properties: tableProps } : {}),
657
+ ...(columnWidths.length > 0 ? { columnWidths } : {}),
658
+ rows
659
+ };
660
+ }
661
+ /** Parse document body content from the office:body/office:text element. */
662
+ function parseDocumentBody(bodyEl, styles) {
663
+ const content = [];
664
+ for (const child of bodyEl.children) {
665
+ if (child.type !== "element") {
666
+ continue;
667
+ }
668
+ const local = getLocalName(child.name);
669
+ if (local === "p" || local === "h") {
670
+ content.push(parseParagraph(child, styles));
671
+ }
672
+ else if (local === "table") {
673
+ content.push(parseTable(child, styles));
674
+ }
675
+ else if (local === "list") {
676
+ content.push(...parseList(child, styles, 0));
677
+ }
678
+ else if (local === "section") {
679
+ // Sections in ODF are logical containers; flatten their content
680
+ content.push(...parseDocumentBody(child, styles));
681
+ }
682
+ }
683
+ return content;
684
+ }
685
+ /** Get the local name from a possibly prefixed XML name. */
686
+ function getLocalName(name) {
687
+ const colonIdx = name.indexOf(":");
688
+ if (colonIdx >= 0) {
689
+ return name.substring(colonIdx + 1);
690
+ }
691
+ return name;
692
+ }
693
+ // =============================================================================
694
+ // Meta Parsing
695
+ // =============================================================================
696
+ /** Parse meta.xml into CoreProperties. */
697
+ function parseMetaXml(xml) {
698
+ let doc;
699
+ try {
700
+ doc = parseXml(xml);
701
+ }
702
+ catch {
703
+ return undefined;
704
+ }
705
+ const root = doc.root;
706
+ const metaEl = findNsChild(root, "office", "meta") ?? findChild(root, "office:meta") ?? root;
707
+ const title = getMetaText(metaEl, "dc", "title");
708
+ const subject = getMetaText(metaEl, "dc", "subject");
709
+ const creator = getMetaText(metaEl, "dc", "creator") ?? getMetaText(metaEl, "meta", "initial-creator");
710
+ const description = getMetaText(metaEl, "dc", "description");
711
+ const keywords = getMetaText(metaEl, "meta", "keyword");
712
+ const createdStr = getMetaText(metaEl, "meta", "creation-date");
713
+ const modifiedStr = getMetaText(metaEl, "dc", "date");
714
+ const created = createdStr ? new Date(createdStr) : undefined;
715
+ const modified = modifiedStr ? new Date(modifiedStr) : undefined;
716
+ const result = {
717
+ ...(title ? { title } : {}),
718
+ ...(subject ? { subject } : {}),
719
+ ...(creator ? { creator } : {}),
720
+ ...(description ? { description } : {}),
721
+ ...(keywords ? { keywords } : {}),
722
+ ...(created && !isNaN(created.getTime()) ? { created } : {}),
723
+ ...(modified && !isNaN(modified.getTime()) ? { modified } : {})
724
+ };
725
+ return Object.keys(result).length > 0 ? result : undefined;
726
+ }
727
+ /** Get text content of a metadata element. */
728
+ function getMetaText(parent, prefix, local) {
729
+ const el = findNsChild(parent, prefix, local);
730
+ if (!el) {
731
+ return undefined;
732
+ }
733
+ const text = textContent(el).trim();
734
+ return text || undefined;
735
+ }
736
+ // =============================================================================
737
+ // Page Layout Parsing
738
+ // =============================================================================
739
+ /** Parse page layout from styles.xml into SectionProperties. */
740
+ function parsePageLayoutToSection(pageLayout) {
741
+ if (!pageLayout) {
742
+ return undefined;
743
+ }
744
+ let pageSize;
745
+ const widthTwips = odfLengthToTwips(pageLayout.pageWidth);
746
+ const heightTwips = odfLengthToTwips(pageLayout.pageHeight);
747
+ if (widthTwips !== undefined && heightTwips !== undefined) {
748
+ pageSize = {
749
+ width: widthTwips,
750
+ height: heightTwips,
751
+ ...(widthTwips > heightTwips ? { orientation: "landscape" } : {})
752
+ };
753
+ }
754
+ let margins;
755
+ const marginTop = odfLengthToTwips(pageLayout.marginTop);
756
+ const marginBottom = odfLengthToTwips(pageLayout.marginBottom);
757
+ const marginLeft = odfLengthToTwips(pageLayout.marginLeft);
758
+ const marginRight = odfLengthToTwips(pageLayout.marginRight);
759
+ if (marginTop !== undefined ||
760
+ marginBottom !== undefined ||
761
+ marginLeft !== undefined ||
762
+ marginRight !== undefined) {
763
+ margins = {
764
+ top: marginTop ?? 1440,
765
+ right: marginRight ?? 1440,
766
+ bottom: marginBottom ?? 1440,
767
+ left: marginLeft ?? 1440
768
+ };
769
+ }
770
+ if (!pageSize && !margins) {
771
+ return undefined;
772
+ }
773
+ return {
774
+ ...(pageSize ? { pageSize } : {}),
775
+ ...(margins ? { margins } : {})
776
+ };
777
+ }
778
+ // =============================================================================
779
+ // Styles Conversion → StyleDef[]
780
+ // =============================================================================
781
+ /** Convert parsed ODF styles to DocxDocument style definitions. */
782
+ function convertStylesToStyleDefs(styles) {
783
+ const defs = [];
784
+ for (const [, style] of styles) {
785
+ // Only convert named/non-automatic styles
786
+ if (!style.name) {
787
+ continue;
788
+ }
789
+ let type;
790
+ switch (style.family) {
791
+ case "paragraph":
792
+ type = "paragraph";
793
+ break;
794
+ case "text":
795
+ type = "character";
796
+ break;
797
+ case "table":
798
+ type = "table";
799
+ break;
800
+ default:
801
+ continue; // Skip unsupported style families
802
+ }
803
+ const def = {
804
+ type,
805
+ styleId: style.name,
806
+ name: style.name,
807
+ ...(style.parentStyle ? { basedOn: style.parentStyle } : {}),
808
+ ...(style.paragraphProperties
809
+ ? { paragraphProperties: odfParagraphPropsToDocx(style.paragraphProperties) }
810
+ : {}),
811
+ ...(style.textProperties ? { runProperties: odfTextPropsToDocx(style.textProperties) } : {})
812
+ };
813
+ defs.push(def);
814
+ }
815
+ return defs;
816
+ }
817
+ // =============================================================================
818
+ // ODT list numbering definition (numId=1)
819
+ // =============================================================================
820
+ /**
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.
826
+ *
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.
831
+ */
832
+ function buildOdtNumberingDefs(body) {
833
+ if (!hasListParagraph(body)) {
834
+ return {};
835
+ }
836
+ const tabSuffix = "tab";
837
+ const bulletChars = ["•", "◦", "▪", "•", "◦", "▪", "•", "◦", "▪"];
838
+ const levels = bulletChars.map((ch, level) => ({
839
+ level,
840
+ format: "bullet",
841
+ text: ch,
842
+ start: 1,
843
+ paragraphProperties: {
844
+ indent: { left: 720 * (level + 1), hanging: 360 }
845
+ },
846
+ suffix: tabSuffix
847
+ }));
848
+ return {
849
+ abstractNumberings: [{ abstractNumId: 1, levels }],
850
+ numberingInstances: [{ numId: 1, abstractNumId: 1 }]
851
+ };
852
+ }
853
+ function hasListParagraph(blocks) {
854
+ for (const block of blocks) {
855
+ if (block.type === "paragraph") {
856
+ if (block.properties?.numbering) {
857
+ return true;
858
+ }
859
+ }
860
+ else if (block.type === "table") {
861
+ for (const row of block.rows) {
862
+ for (const cell of row.cells) {
863
+ if (hasListParagraph(cell.content)) {
864
+ return true;
865
+ }
866
+ }
867
+ }
868
+ }
869
+ }
870
+ return false;
871
+ }
872
+ // =============================================================================
873
+ // readOdt — Main Entry Point
874
+ // =============================================================================
875
+ /**
876
+ * Read an ODT file and convert to DocxDocument model.
877
+ *
878
+ * Extracts the ZIP archive, parses content.xml, styles.xml, and meta.xml,
879
+ * and produces a unified DocxDocument representation.
880
+ *
881
+ * @param buffer - The ODT file as a Uint8Array.
882
+ * @returns A DocxDocument representing the ODT content.
883
+ * @throws {DocxParseError} If the ODT file is malformed or missing required parts.
884
+ *
885
+ * @stability experimental
886
+ */
887
+ export async function readOdt(buffer) {
888
+ // Extract ZIP contents
889
+ const reader = unzip(buffer);
890
+ const entries = new Map();
891
+ for await (const entry of reader.entries()) {
892
+ const data = await entry.bytes();
893
+ const path = entry.path.replace(/^\//, "").replace(/\\/g, "/");
894
+ entries.set(path, data);
895
+ }
896
+ const decoder = utf8Decoder;
897
+ // Parse content.xml (required)
898
+ const contentData = entries.get("content.xml");
899
+ if (!contentData) {
900
+ throw new DocxParseError("Required ODT part not found: content.xml");
901
+ }
902
+ const contentXml = decoder.decode(contentData);
903
+ const contentDoc = parseXml(contentXml);
904
+ // Parse styles.xml (optional)
905
+ const stylesData = entries.get("styles.xml");
906
+ let stylesXml;
907
+ if (stylesData) {
908
+ stylesXml = decoder.decode(stylesData);
909
+ }
910
+ // Parse meta.xml (optional)
911
+ const metaData = entries.get("meta.xml");
912
+ let coreProperties;
913
+ if (metaData) {
914
+ coreProperties = parseMetaXml(decoder.decode(metaData));
915
+ }
916
+ // Collect all styles (from both content.xml and styles.xml)
917
+ const allStyles = new Map();
918
+ let pageLayoutProps;
919
+ // Parse styles from styles.xml
920
+ if (stylesXml) {
921
+ const stylesDoc = parseXml(stylesXml);
922
+ const stylesRoot = stylesDoc.root;
923
+ // Named styles (office:styles)
924
+ const officeStylesEl = findNsChild(stylesRoot, "office", "styles");
925
+ if (officeStylesEl) {
926
+ for (const child of officeStylesEl.children) {
927
+ if (child.type === "element" && getLocalName(child.name) === "style") {
928
+ const s = parseOdfStyle(child);
929
+ if (s.name) {
930
+ allStyles.set(s.name, s);
931
+ }
932
+ }
933
+ }
934
+ }
935
+ // Automatic styles in styles.xml (office:automatic-styles)
936
+ const autoStylesEl = findNsChild(stylesRoot, "office", "automatic-styles");
937
+ if (autoStylesEl) {
938
+ for (const child of autoStylesEl.children) {
939
+ if (child.type === "element") {
940
+ const childLocal = getLocalName(child.name);
941
+ if (childLocal === "style") {
942
+ const s = parseOdfStyle(child);
943
+ if (s.name) {
944
+ allStyles.set(s.name, s);
945
+ }
946
+ }
947
+ else if (childLocal === "page-layout") {
948
+ pageLayoutProps = parsePageLayout(child);
949
+ }
950
+ }
951
+ }
952
+ }
953
+ // Master page styles for page layout reference
954
+ const masterStylesEl = findNsChild(stylesRoot, "office", "master-styles");
955
+ if (masterStylesEl && !pageLayoutProps) {
956
+ // Look for the page layout reference in master pages
957
+ for (const child of masterStylesEl.children) {
958
+ if (child.type === "element" && getLocalName(child.name) === "master-page") {
959
+ const pageLayoutName = nsAttr(child, "style", "page-layout-name");
960
+ if (pageLayoutName && autoStylesEl) {
961
+ for (const autoChild of autoStylesEl.children) {
962
+ if (autoChild.type === "element" &&
963
+ getLocalName(autoChild.name) === "page-layout" &&
964
+ nsAttr(autoChild, "style", "name") === pageLayoutName) {
965
+ pageLayoutProps = parsePageLayout(autoChild);
966
+ break;
967
+ }
968
+ }
969
+ }
970
+ break;
971
+ }
972
+ }
973
+ }
974
+ }
975
+ // Parse automatic styles from content.xml
976
+ const contentAutoStylesEl = findNsChild(contentDoc.root, "office", "automatic-styles");
977
+ if (contentAutoStylesEl) {
978
+ for (const child of contentAutoStylesEl.children) {
979
+ if (child.type === "element") {
980
+ const childLocal = getLocalName(child.name);
981
+ if (childLocal === "style") {
982
+ const s = parseOdfStyle(child);
983
+ if (s.name) {
984
+ allStyles.set(s.name, s);
985
+ }
986
+ }
987
+ else if (childLocal === "page-layout" && !pageLayoutProps) {
988
+ pageLayoutProps = parsePageLayout(child);
989
+ }
990
+ }
991
+ }
992
+ }
993
+ // Parse document body
994
+ const bodyEl = findNsChild(contentDoc.root, "office", "body");
995
+ if (!bodyEl) {
996
+ throw new DocxParseError("Invalid ODT: missing office:body element");
997
+ }
998
+ const textEl = findNsChild(bodyEl, "office", "text");
999
+ if (!textEl) {
1000
+ throw new DocxParseError("Invalid ODT: missing office:text element");
1001
+ }
1002
+ const body = parseDocumentBody(textEl, allStyles);
1003
+ // Convert styles to StyleDef[]
1004
+ const styleDefs = convertStylesToStyleDefs(allStyles);
1005
+ // Parse section properties from page layout
1006
+ const sectionProperties = parsePageLayoutToSection(pageLayoutProps);
1007
+ // Collect images
1008
+ const images = [];
1009
+ for (const [path, data] of entries) {
1010
+ if (path.startsWith("Pictures/") && data.length > 0) {
1011
+ const ext = path.substring(path.lastIndexOf(".") + 1).toLowerCase();
1012
+ let mediaType;
1013
+ switch (ext) {
1014
+ case "png":
1015
+ mediaType = "image/png";
1016
+ break;
1017
+ case "jpg":
1018
+ case "jpeg":
1019
+ mediaType = "image/jpeg";
1020
+ break;
1021
+ case "gif":
1022
+ mediaType = "image/gif";
1023
+ break;
1024
+ case "svg":
1025
+ mediaType = "image/svg+xml";
1026
+ break;
1027
+ case "emf":
1028
+ mediaType = "image/x-emf";
1029
+ break;
1030
+ case "wmf":
1031
+ mediaType = "image/x-wmf";
1032
+ break;
1033
+ case "tiff":
1034
+ case "tif":
1035
+ mediaType = "image/tiff";
1036
+ break;
1037
+ case "bmp":
1038
+ mediaType = "image/bmp";
1039
+ break;
1040
+ default:
1041
+ mediaType = "application/octet-stream";
1042
+ break;
1043
+ }
1044
+ images.push({
1045
+ rId: path,
1046
+ data,
1047
+ mediaType: mediaType,
1048
+ fileName: path.substring(path.lastIndexOf("/") + 1)
1049
+ });
1050
+ }
1051
+ }
1052
+ const doc = {
1053
+ body,
1054
+ ...(sectionProperties ? { sectionProperties } : {}),
1055
+ ...(styleDefs.length > 0 ? { styles: styleDefs } : {}),
1056
+ ...(coreProperties ? { coreProperties } : {}),
1057
+ ...(images.length > 0 ? { images } : {}),
1058
+ ...buildOdtNumberingDefs(body)
1059
+ };
1060
+ return doc;
1061
+ }
1062
+ /**
1063
+ * Build a deterministic, ZIP-safe `Pictures/` path for each image in the
1064
+ * document. The same rId → path map is consumed by both the content
1065
+ * writer (`xlink:href`) and the archive writer so that what we point at
1066
+ * in content.xml actually exists in the package.
1067
+ *
1068
+ * Image identifiers (`rId`, `fileName`) come from arbitrary upstream
1069
+ * input — including untrusted DOCX files round-tripped through readDocx.
1070
+ * Without sanitisation a hostile rId like `../../etc/passwd` would
1071
+ * produce both an out-of-tree ZIP entry name and an out-of-package
1072
+ * `xlink:href`, which is a real vector for confusing downstream ODT
1073
+ * readers and tooling that resolves relative paths.
1074
+ */
1075
+ function buildOdtImagePathMap(images) {
1076
+ const byRId = new Map();
1077
+ const byFileName = new Map();
1078
+ if (!images) {
1079
+ return { byRId, byFileName };
1080
+ }
1081
+ const used = new Set();
1082
+ // Allocate a unique `Pictures/<safe>` path for each image. We generate
1083
+ // a single counter-based fallback when the preferred name is empty or
1084
+ // collides with an entry that's already taken; the previous version
1085
+ // double-incremented `counter` (once inside the collision loop, once
1086
+ // after) producing sparse names like `image1.bin`, `image3.bin`, ….
1087
+ let counter = 1;
1088
+ const allocate = (preferred) => {
1089
+ const safeBase = sanitizeOdtPictureName(preferred);
1090
+ let candidate = safeBase || `image${counter++}.bin`;
1091
+ while (used.has(candidate)) {
1092
+ // Suffix a fresh counter to disambiguate; preserve the extension
1093
+ // when we have one so downstream readers still classify the file
1094
+ // correctly. `_bin` was the previous fallback when no extension
1095
+ // was available — keep that behaviour for the no-base case.
1096
+ const c = counter++;
1097
+ if (safeBase) {
1098
+ const dot = safeBase.lastIndexOf(".");
1099
+ const stem = dot >= 0 ? safeBase.slice(0, dot) : safeBase;
1100
+ const ext = dot >= 0 ? safeBase.slice(dot) : "";
1101
+ candidate = `${stem}_${c}${ext}`;
1102
+ }
1103
+ else {
1104
+ candidate = `image${c}.bin`;
1105
+ }
1106
+ }
1107
+ used.add(candidate);
1108
+ return `Pictures/${candidate}`;
1109
+ };
1110
+ for (const image of images) {
1111
+ const path = allocate(image.fileName ?? image.rId);
1112
+ if (image.rId) {
1113
+ byRId.set(image.rId, path);
1114
+ }
1115
+ if (image.fileName) {
1116
+ // Two distinct images might share a fileName (the inputs are
1117
+ // attacker-controlled in the round-trip path). The first one to
1118
+ // claim a fileName wins the byFileName lookup; later collisions
1119
+ // are still reachable via their own rId in byRId. Without this
1120
+ // guard the second insertion would silently overwrite the first
1121
+ // and inline-image lookups via fileName would resolve to the
1122
+ // wrong binary.
1123
+ if (!byFileName.has(image.fileName)) {
1124
+ byFileName.set(image.fileName, path);
1125
+ }
1126
+ }
1127
+ }
1128
+ return { byRId, byFileName };
1129
+ }
1130
+ /**
1131
+ * Sanitise a single ODT picture file-name component. Strips path
1132
+ * separators, parent-directory traversal, leading dots, and anything
1133
+ * outside a conservative whitelist. Returns an empty string if no usable
1134
+ * characters remain (caller falls back to a generated name).
1135
+ */
1136
+ function sanitizeOdtPictureName(raw) {
1137
+ if (!raw) {
1138
+ return "";
1139
+ }
1140
+ // Drop directory components — we only ever want a leaf file name.
1141
+ const lastSep = Math.max(raw.lastIndexOf("/"), raw.lastIndexOf("\\"));
1142
+ let leaf = lastSep >= 0 ? raw.substring(lastSep + 1) : raw;
1143
+ // Strip leading dots so names like "..png" or ".htaccess" don't sneak
1144
+ // through with attribute meaning to filesystems.
1145
+ while (leaf.startsWith(".")) {
1146
+ leaf = leaf.substring(1);
1147
+ }
1148
+ // Whitelist alnum, dash, underscore, dot. Replace everything else with
1149
+ // underscore. Empty result triggers fallback in the caller.
1150
+ leaf = leaf.replace(/[^A-Za-z0-9._-]/g, "_");
1151
+ // Avoid pathological double-dot anywhere mid-name (e.g. "foo..bin").
1152
+ leaf = leaf.replace(/\.{2,}/g, ".");
1153
+ return leaf;
1154
+ }
1155
+ // =============================================================================
1156
+ // writeOdt — Main Entry Point
1157
+ // =============================================================================
1158
+ /**
1159
+ * Convert a DocxDocument to ODT (OpenDocument Text) format.
1160
+ *
1161
+ * Generates the ZIP archive structure with content.xml, styles.xml,
1162
+ * meta.xml, and META-INF/manifest.xml.
1163
+ *
1164
+ * @param doc - The DocxDocument to convert.
1165
+ * @returns A Uint8Array containing the ODT ZIP archive.
1166
+ *
1167
+ * @stability experimental
1168
+ */
1169
+ export async function writeOdt(doc) {
1170
+ const encoder = utf8Encoder;
1171
+ const archive = zip();
1172
+ // Mimetype MUST be the first entry in the ZIP, uncompressed (ODF spec requirement)
1173
+ archive.add("mimetype", encoder.encode("application/vnd.oasis.opendocument.text"), { level: 0 });
1174
+ // Compute a sanitised rId → Pictures/<safe>.<ext> map up front so the
1175
+ // content writer and archive writer agree on every image path.
1176
+ const imageMap = buildOdtImagePathMap(doc.images);
1177
+ // Collect image paths for manifest
1178
+ const imagePaths = [];
1179
+ // Generate content.xml — the writer reads from imageMap so href values
1180
+ // are guaranteed in-package and free of traversal sequences.
1181
+ const contentXml = generateContentXml(doc, imagePaths, imageMap);
1182
+ archive.add("content.xml", encoder.encode(contentXml));
1183
+ // Generate styles.xml
1184
+ const stylesXml = generateStylesXml(doc);
1185
+ archive.add("styles.xml", encoder.encode(stylesXml));
1186
+ // Generate meta.xml
1187
+ const metaXml = generateMetaXml(doc);
1188
+ archive.add("meta.xml", encoder.encode(metaXml));
1189
+ // Add images at the sanitised paths.
1190
+ if (doc.images) {
1191
+ for (const image of doc.images) {
1192
+ if (!image.data) {
1193
+ continue;
1194
+ }
1195
+ const imgPath = (image.rId ? imageMap.byRId.get(image.rId) : undefined) ??
1196
+ (image.fileName ? imageMap.byFileName.get(image.fileName) : undefined);
1197
+ if (!imgPath) {
1198
+ continue;
1199
+ }
1200
+ archive.add(imgPath, image.data);
1201
+ imagePaths.push(imgPath);
1202
+ }
1203
+ }
1204
+ // Generate META-INF/manifest.xml
1205
+ const manifestXml = generateManifestXml(imagePaths);
1206
+ archive.add("META-INF/manifest.xml", encoder.encode(manifestXml));
1207
+ return archive.bytes();
1208
+ }
1209
+ /** Create a new write context. */
1210
+ function createWriteContext(imageMap) {
1211
+ return {
1212
+ runStyleMap: new Map(),
1213
+ runStyleProps: new Map(),
1214
+ nextRunStyleId: 1,
1215
+ bookmarkNames: new Map(),
1216
+ imagePathByRId: imageMap?.byRId ?? new Map(),
1217
+ imagePathByFileName: imageMap?.byFileName ?? new Map()
1218
+ };
1219
+ }
1220
+ /**
1221
+ * Generate a deterministic cache key for RunProperties.
1222
+ * Only includes formatting-relevant properties.
1223
+ */
1224
+ function runPropsKey(props) {
1225
+ const parts = [];
1226
+ if (props.font) {
1227
+ const fontName = typeof props.font === "string" ? props.font : props.font.ascii;
1228
+ if (fontName) {
1229
+ parts.push(`f:${fontName}`);
1230
+ }
1231
+ }
1232
+ if (props.size !== undefined) {
1233
+ parts.push(`s:${props.size}`);
1234
+ }
1235
+ if (props.bold) {
1236
+ parts.push("b");
1237
+ }
1238
+ if (props.italic) {
1239
+ parts.push("i");
1240
+ }
1241
+ if (props.underline) {
1242
+ parts.push("u");
1243
+ }
1244
+ if (props.strike) {
1245
+ parts.push("st");
1246
+ }
1247
+ if (props.color) {
1248
+ const colorVal = typeof props.color === "string" ? props.color : props.color.val;
1249
+ if (colorVal) {
1250
+ parts.push(`c:${colorVal}`);
1251
+ }
1252
+ }
1253
+ if (props.smallCaps) {
1254
+ parts.push("sc");
1255
+ }
1256
+ if (props.vertAlign) {
1257
+ parts.push(`va:${props.vertAlign}`);
1258
+ }
1259
+ if (props.spacing !== undefined) {
1260
+ parts.push(`sp:${props.spacing}`);
1261
+ }
1262
+ return parts.join("|");
1263
+ }
1264
+ /** Get or create an automatic style name for the given RunProperties. */
1265
+ function getRunAutoStyleName(ctx, props) {
1266
+ const key = runPropsKey(props);
1267
+ let name = ctx.runStyleMap.get(key);
1268
+ if (!name) {
1269
+ name = `T${ctx.nextRunStyleId++}`;
1270
+ ctx.runStyleMap.set(key, name);
1271
+ ctx.runStyleProps.set(name, props);
1272
+ }
1273
+ return name;
1274
+ }
1275
+ /** Generate the content.xml for the ODT package. */
1276
+ function generateContentXml(doc, imagePaths, imageMap) {
1277
+ // First pass: write body to collect automatic run styles
1278
+ const ctx = createWriteContext(imageMap);
1279
+ const bodyWriter = new XmlWriter();
1280
+ bodyWriter.openNode("office:body");
1281
+ bodyWriter.openNode("office:text");
1282
+ for (const block of doc.body) {
1283
+ writeBodyContent(bodyWriter, block, doc, imagePaths, ctx);
1284
+ }
1285
+ bodyWriter.closeNode(); // office:text
1286
+ bodyWriter.closeNode(); // office:body
1287
+ const bodyXml = bodyWriter.xml;
1288
+ // Second pass: assemble the full content.xml with collected automatic styles
1289
+ const w = new XmlWriter();
1290
+ w.openXml();
1291
+ w.openNode("office:document-content", {
1292
+ "xmlns:office": NS.office,
1293
+ "xmlns:style": NS.style,
1294
+ "xmlns:text": NS.text,
1295
+ "xmlns:table": NS.table,
1296
+ "xmlns:draw": NS.draw,
1297
+ "xmlns:fo": NS.fo,
1298
+ "xmlns:xlink": NS.xlink,
1299
+ "xmlns:svg": NS.svg,
1300
+ "office:version": "1.3"
1301
+ });
1302
+ // Automatic styles section
1303
+ w.openNode("office:automatic-styles");
1304
+ writeAutoStyles(w, doc);
1305
+ writeCollectedRunStyles(w, ctx);
1306
+ w.closeNode();
1307
+ // Body (inject pre-built body XML)
1308
+ w.writeRaw(bodyXml);
1309
+ w.closeNode(); // office:document-content
1310
+ return w.xml;
1311
+ }
1312
+ /** Write collected automatic run styles into the automatic-styles section. */
1313
+ function writeCollectedRunStyles(w, ctx) {
1314
+ for (const [styleName, props] of ctx.runStyleProps) {
1315
+ w.openNode("style:style", {
1316
+ "style:name": styleName,
1317
+ "style:family": "text"
1318
+ });
1319
+ writeTextPropertiesOdf(w, props);
1320
+ w.closeNode();
1321
+ }
1322
+ }
1323
+ /** Write automatic styles based on document content. */
1324
+ function writeAutoStyles(w, doc) {
1325
+ // Output styles from the StyleDef array that exist in the document.
1326
+ if (doc.styles) {
1327
+ for (const styleDef of doc.styles) {
1328
+ writeStyleDef(w, styleDef);
1329
+ }
1330
+ }
1331
+ }
1332
+ /** Write a single StyleDef as an ODF style. */
1333
+ function writeStyleDef(w, def) {
1334
+ const family = styleTypeToOdfFamily(def.type);
1335
+ if (!family) {
1336
+ return;
1337
+ }
1338
+ w.openNode("style:style", {
1339
+ "style:name": def.styleId,
1340
+ "style:family": family,
1341
+ ...(def.basedOn ? { "style:parent-style-name": def.basedOn } : {}),
1342
+ ...(def.name !== def.styleId ? { "style:display-name": def.name } : {})
1343
+ });
1344
+ // Paragraph properties
1345
+ if (def.paragraphProperties) {
1346
+ writeParagraphPropertiesOdf(w, def.paragraphProperties);
1347
+ }
1348
+ // Run/text properties
1349
+ if (def.runProperties) {
1350
+ writeTextPropertiesOdf(w, def.runProperties);
1351
+ }
1352
+ w.closeNode();
1353
+ }
1354
+ /** Convert DocxDocument style type to ODF style family. */
1355
+ function styleTypeToOdfFamily(type) {
1356
+ switch (type) {
1357
+ case "paragraph":
1358
+ return "paragraph";
1359
+ case "character":
1360
+ return "text";
1361
+ case "table":
1362
+ return "table";
1363
+ default:
1364
+ return undefined;
1365
+ }
1366
+ }
1367
+ /** Write paragraph properties as ODF elements. */
1368
+ function writeParagraphPropertiesOdf(w, props) {
1369
+ const attrs = {};
1370
+ if (props.alignment) {
1371
+ attrs["fo:text-align"] = alignmentToOdf(props.alignment);
1372
+ }
1373
+ if (props.indent) {
1374
+ if (props.indent.left !== undefined) {
1375
+ attrs["fo:margin-left"] = twipsToCm(props.indent.left);
1376
+ }
1377
+ if (props.indent.right !== undefined) {
1378
+ attrs["fo:margin-right"] = twipsToCm(props.indent.right);
1379
+ }
1380
+ if (props.indent.firstLine !== undefined) {
1381
+ attrs["fo:text-indent"] = twipsToCm(props.indent.firstLine);
1382
+ }
1383
+ else if (props.indent.hanging !== undefined) {
1384
+ attrs["fo:text-indent"] = twipsToCm(-props.indent.hanging);
1385
+ }
1386
+ }
1387
+ if (props.spacing) {
1388
+ if (props.spacing.before !== undefined) {
1389
+ attrs["fo:margin-top"] = twipsToCm(props.spacing.before);
1390
+ }
1391
+ if (props.spacing.after !== undefined) {
1392
+ attrs["fo:margin-bottom"] = twipsToCm(props.spacing.after);
1393
+ }
1394
+ }
1395
+ if (props.keepNext) {
1396
+ attrs["fo:keep-with-next"] = "always";
1397
+ }
1398
+ if (props.pageBreakBefore) {
1399
+ attrs["fo:break-before"] = "page";
1400
+ }
1401
+ if (Object.keys(attrs).length > 0) {
1402
+ w.leafNode("style:paragraph-properties", attrs);
1403
+ }
1404
+ }
1405
+ /** Write run/text properties as ODF elements. */
1406
+ function writeTextPropertiesOdf(w, props) {
1407
+ const attrs = {};
1408
+ if (props.font) {
1409
+ const fontName = typeof props.font === "string" ? props.font : props.font.ascii;
1410
+ if (fontName) {
1411
+ attrs["style:font-name"] = fontName;
1412
+ }
1413
+ }
1414
+ if (props.size !== undefined) {
1415
+ attrs["fo:font-size"] = halfPointsToPt(props.size);
1416
+ }
1417
+ if (props.bold) {
1418
+ attrs["fo:font-weight"] = "bold";
1419
+ }
1420
+ if (props.italic) {
1421
+ attrs["fo:font-style"] = "italic";
1422
+ }
1423
+ if (props.underline) {
1424
+ attrs["style:text-underline-style"] = "solid";
1425
+ attrs["style:text-underline-width"] = "auto";
1426
+ attrs["style:text-underline-color"] = "font-color";
1427
+ }
1428
+ if (props.strike) {
1429
+ attrs["style:text-line-through-style"] = "solid";
1430
+ }
1431
+ if (props.color) {
1432
+ const colorVal = typeof props.color === "string" ? props.color : props.color.val;
1433
+ if (colorVal && colorVal !== "auto") {
1434
+ attrs["fo:color"] = colorToOdf(colorVal);
1435
+ }
1436
+ }
1437
+ if (props.smallCaps) {
1438
+ attrs["fo:font-variant"] = "small-caps";
1439
+ }
1440
+ if (props.vertAlign === "superscript") {
1441
+ attrs["style:text-position"] = "super 58%";
1442
+ }
1443
+ else if (props.vertAlign === "subscript") {
1444
+ attrs["style:text-position"] = "sub 58%";
1445
+ }
1446
+ if (props.spacing !== undefined) {
1447
+ attrs["fo:letter-spacing"] = twipsToCm(props.spacing);
1448
+ }
1449
+ if (Object.keys(attrs).length > 0) {
1450
+ w.leafNode("style:text-properties", attrs);
1451
+ }
1452
+ }
1453
+ /** Convert OOXML alignment to ODF text-align value. */
1454
+ function alignmentToOdf(alignment) {
1455
+ switch (alignment) {
1456
+ case "left":
1457
+ case "start":
1458
+ return "start";
1459
+ case "center":
1460
+ return "center";
1461
+ case "right":
1462
+ case "end":
1463
+ return "end";
1464
+ case "both":
1465
+ return "justify";
1466
+ default:
1467
+ return "start";
1468
+ }
1469
+ }
1470
+ /** Write a block-level content element to the XML writer. */
1471
+ function writeBodyContent(w, block, doc, imagePaths, ctx) {
1472
+ switch (block.type) {
1473
+ case "paragraph":
1474
+ writeParagraph(w, block, doc, imagePaths, ctx);
1475
+ break;
1476
+ case "table":
1477
+ writeTable(w, block, doc, imagePaths, ctx);
1478
+ break;
1479
+ case "tableOfContents":
1480
+ // Write TOC cached paragraphs if available
1481
+ if (block.cachedParagraphs) {
1482
+ for (const p of block.cachedParagraphs) {
1483
+ writeParagraph(w, p, doc, imagePaths, ctx);
1484
+ }
1485
+ }
1486
+ break;
1487
+ case "math":
1488
+ // Math blocks are rendered as paragraphs with math text
1489
+ w.openNode("text:p");
1490
+ for (const mc of block.content) {
1491
+ if (mc.type === "mathRun") {
1492
+ w.writeText(mc.text);
1493
+ }
1494
+ }
1495
+ w.closeNode();
1496
+ break;
1497
+ default:
1498
+ // Other block types (floatingImage, textBox, etc.) — emit as empty paragraph placeholder
1499
+ w.leafNode("text:p");
1500
+ break;
1501
+ }
1502
+ }
1503
+ /** Write a paragraph element. */
1504
+ function writeParagraph(w, para, doc, imagePaths, ctx) {
1505
+ const isHeading = para.properties?.outlineLevel !== undefined && para.properties.outlineLevel >= 0;
1506
+ const styleName = para.properties?.style;
1507
+ if (isHeading) {
1508
+ const level = (para.properties.outlineLevel ?? 0) + 1;
1509
+ w.openNode("text:h", {
1510
+ ...(styleName ? { "text:style-name": styleName } : {}),
1511
+ "text:outline-level": String(level)
1512
+ });
1513
+ }
1514
+ else {
1515
+ w.openNode("text:p", {
1516
+ ...(styleName ? { "text:style-name": styleName } : {})
1517
+ });
1518
+ }
1519
+ for (const child of para.children) {
1520
+ writeParagraphChild(w, child, doc, imagePaths, ctx);
1521
+ }
1522
+ w.closeNode();
1523
+ }
1524
+ /** Write a paragraph child (Run, Hyperlink, etc.). */
1525
+ function writeParagraphChild(w, child, doc, imagePaths, ctx) {
1526
+ if (isRun(child)) {
1527
+ writeRun(w, child, doc, imagePaths, ctx);
1528
+ return;
1529
+ }
1530
+ switch (child.type) {
1531
+ case "hyperlink": {
1532
+ // Defense in depth: even though hyperlinks coming through readDocx /
1533
+ // htmlImport / odtRead are already sanitized, models can be built
1534
+ // by hand. Run the URL through sanitizeUrl one more time before
1535
+ // writing it into the ODT, so a malicious model can't smuggle
1536
+ // javascript:/vbscript: URLs through this writer.
1537
+ const rawHref = child.url ?? (child.anchor ? `#${child.anchor}` : "");
1538
+ const href = rawHref.startsWith("#") ? rawHref : (sanitizeUrl(rawHref) ?? "");
1539
+ if (!href) {
1540
+ // Drop the link wrapper — write children as plain runs.
1541
+ for (const run of child.children) {
1542
+ writeRun(w, run, doc, imagePaths, ctx);
1543
+ }
1544
+ break;
1545
+ }
1546
+ w.openNode("text:a", {
1547
+ "xlink:type": "simple",
1548
+ "xlink:href": href
1549
+ });
1550
+ for (const run of child.children) {
1551
+ writeRun(w, run, doc, imagePaths, ctx);
1552
+ }
1553
+ w.closeNode();
1554
+ break;
1555
+ }
1556
+ case "bookmarkStart":
1557
+ ctx.bookmarkNames.set(child.id, child.name);
1558
+ w.leafNode("text:bookmark-start", { "text:name": child.name });
1559
+ break;
1560
+ case "bookmarkEnd": {
1561
+ const name = ctx.bookmarkNames.get(child.id);
1562
+ if (name !== undefined) {
1563
+ // Emit the matching name so range-aware ODT readers can pair the
1564
+ // start/end markers correctly.
1565
+ w.leafNode("text:bookmark-end", { "text:name": name });
1566
+ }
1567
+ else {
1568
+ // Stray end without a known start (shouldn't happen for documents
1569
+ // produced by this library) — emit with an empty name to keep the
1570
+ // XML well-formed.
1571
+ w.leafNode("text:bookmark-end", { "text:name": "" });
1572
+ }
1573
+ break;
1574
+ }
1575
+ case "insertedRun":
1576
+ writeRun(w, child.run, doc, imagePaths, ctx);
1577
+ break;
1578
+ case "deletedRun":
1579
+ // Deleted runs are typically not shown in the output
1580
+ break;
1581
+ default:
1582
+ // Other paragraph children (comments, etc.) — skip
1583
+ break;
1584
+ }
1585
+ }
1586
+ /** Write a Run (text:span or direct text). */
1587
+ function writeRun(w, run, doc, imagePaths, ctx) {
1588
+ const hasProps = run.properties && Object.keys(run.properties).length > 0;
1589
+ if (hasProps) {
1590
+ const styleName = getRunAutoStyleName(ctx, run.properties);
1591
+ w.openNode("text:span", { "text:style-name": styleName });
1592
+ }
1593
+ for (const content of run.content) {
1594
+ writeRunContent(w, content, doc, imagePaths, ctx);
1595
+ }
1596
+ if (hasProps) {
1597
+ w.closeNode();
1598
+ }
1599
+ }
1600
+ /** Write run content elements. */
1601
+ function writeRunContent(w, content, doc, imagePaths, ctx) {
1602
+ switch (content.type) {
1603
+ case "text":
1604
+ writeOdfText(w, content.text);
1605
+ break;
1606
+ case "break":
1607
+ if (content.breakType === "page") {
1608
+ // ODF 1.2 §5.1.4 — `text:soft-page-break` is a hint that a layout
1609
+ // page break occurs at this position. Strict semantic page breaks
1610
+ // require `fo:break-before="page"` on the surrounding paragraph
1611
+ // automatic style; we emit the soft hint here so the break is at
1612
+ // least preserved (rather than silently dropped, which corrupted
1613
+ // documents on round-trip).
1614
+ w.leafNode("text:soft-page-break");
1615
+ }
1616
+ else if (content.breakType === "column") {
1617
+ // Column breaks degrade to a soft page break in ODF — better than
1618
+ // dropping silently.
1619
+ w.leafNode("text:soft-page-break");
1620
+ }
1621
+ else {
1622
+ w.leafNode("text:line-break");
1623
+ }
1624
+ break;
1625
+ case "tab":
1626
+ w.leafNode("text:tab");
1627
+ break;
1628
+ case "image": {
1629
+ // Write inline image as draw:frame
1630
+ const widthCm = emuToCm(content.width);
1631
+ const heightCm = emuToCm(content.height);
1632
+ // Resolve the safe Pictures/ path that writeOdt registered when it
1633
+ // built the image map. This keeps content.xml's xlink:href in
1634
+ // lockstep with the actual ZIP entry name, and prevents a hostile
1635
+ // rId from emitting a traversing `xlink:href`.
1636
+ const mapped = ctx.imagePathByRId.get(content.rId) ??
1637
+ (content.name ? ctx.imagePathByFileName.get(content.name) : undefined);
1638
+ const imgPath = mapped ?? `Pictures/${sanitizeOdtPictureName(content.rId) || "image.bin"}`;
1639
+ w.openNode("draw:frame", {
1640
+ "draw:name": content.name ?? "",
1641
+ "svg:width": widthCm,
1642
+ "svg:height": heightCm,
1643
+ "text:anchor-type": "as-char"
1644
+ });
1645
+ w.leafNode("draw:image", {
1646
+ "xlink:href": imgPath,
1647
+ "xlink:type": "simple",
1648
+ "xlink:show": "embed",
1649
+ "xlink:actuate": "onLoad"
1650
+ });
1651
+ w.closeNode();
1652
+ break;
1653
+ }
1654
+ case "carriageReturn":
1655
+ w.leafNode("text:line-break");
1656
+ break;
1657
+ case "noBreakHyphen":
1658
+ w.writeText("\u2011");
1659
+ break;
1660
+ case "softHyphen":
1661
+ w.writeText("\u00AD");
1662
+ break;
1663
+ default:
1664
+ // Other content types (field, footnoteRef, etc.) — skip for now
1665
+ break;
1666
+ }
1667
+ }
1668
+ /** Write text content, handling multiple spaces via text:s. */
1669
+ function writeOdfText(w, text) {
1670
+ // ODF collapses whitespace like HTML. Use text:s for multiple spaces.
1671
+ let i = 0;
1672
+ while (i < text.length) {
1673
+ const spaceStart = text.indexOf(" ", i);
1674
+ if (spaceStart < 0) {
1675
+ w.writeText(text.substring(i));
1676
+ break;
1677
+ }
1678
+ // Write text before the spaces
1679
+ if (spaceStart > i) {
1680
+ w.writeText(text.substring(i, spaceStart));
1681
+ }
1682
+ // Count consecutive spaces
1683
+ let spaceCount = 0;
1684
+ let j = spaceStart;
1685
+ while (j < text.length && text[j] === " ") {
1686
+ spaceCount++;
1687
+ j++;
1688
+ }
1689
+ // First space is implicit, rest use text:s
1690
+ w.writeText(" ");
1691
+ if (spaceCount > 1) {
1692
+ w.leafNode("text:s", { "text:c": String(spaceCount - 1) });
1693
+ }
1694
+ i = j;
1695
+ }
1696
+ }
1697
+ /** Write a table element. */
1698
+ function writeTable(w, table, doc, imagePaths, ctx) {
1699
+ w.openNode("table:table", {
1700
+ ...(table.properties?.style ? { "table:style-name": table.properties.style } : {})
1701
+ });
1702
+ // Write column definitions
1703
+ if (table.columnWidths && table.columnWidths.length > 0) {
1704
+ for (const _width of table.columnWidths) {
1705
+ w.leafNode("table:table-column");
1706
+ }
1707
+ }
1708
+ // Write rows
1709
+ for (const row of table.rows) {
1710
+ writeTableRow(w, row, doc, imagePaths, ctx);
1711
+ }
1712
+ w.closeNode();
1713
+ }
1714
+ /** Write a table row. */
1715
+ function writeTableRow(w, row, doc, imagePaths, ctx) {
1716
+ w.openNode("table:table-row");
1717
+ for (const cell of row.cells) {
1718
+ writeTableCell(w, cell, doc, imagePaths, ctx);
1719
+ }
1720
+ w.closeNode();
1721
+ }
1722
+ /** Write a table cell. */
1723
+ function writeTableCell(w, cell, doc, imagePaths, ctx) {
1724
+ const attrs = {};
1725
+ if (cell.properties?.gridSpan && cell.properties.gridSpan > 1) {
1726
+ attrs["table:number-columns-spanned"] = String(cell.properties.gridSpan);
1727
+ }
1728
+ if (cell.properties?.rowSpan && cell.properties.rowSpan > 1) {
1729
+ attrs["table:number-rows-spanned"] = String(cell.properties.rowSpan);
1730
+ }
1731
+ if (cell.properties?.verticalMerge === "continue") {
1732
+ w.leafNode("table:covered-table-cell");
1733
+ return;
1734
+ }
1735
+ w.openNode("table:table-cell", attrs);
1736
+ for (const content of cell.content) {
1737
+ if (content.type === "paragraph") {
1738
+ writeParagraph(w, content, doc, imagePaths, ctx);
1739
+ }
1740
+ else if (content.type === "table") {
1741
+ writeTable(w, content, doc, imagePaths, ctx);
1742
+ }
1743
+ }
1744
+ w.closeNode();
1745
+ }
1746
+ // =============================================================================
1747
+ // Styles XML Generation
1748
+ // =============================================================================
1749
+ /** Generate styles.xml for the ODT package. */
1750
+ function generateStylesXml(doc) {
1751
+ const w = new XmlWriter();
1752
+ w.openXml();
1753
+ w.openNode("office:document-styles", {
1754
+ "xmlns:office": NS.office,
1755
+ "xmlns:style": NS.style,
1756
+ "xmlns:text": NS.text,
1757
+ "xmlns:table": NS.table,
1758
+ "xmlns:fo": NS.fo,
1759
+ "xmlns:draw": NS.draw,
1760
+ "xmlns:svg": NS.svg,
1761
+ "office:version": "1.3"
1762
+ });
1763
+ // Office styles (named styles)
1764
+ w.openNode("office:styles");
1765
+ // Default paragraph style
1766
+ w.openNode("style:default-style", { "style:family": "paragraph" });
1767
+ if (doc.docDefaults?.paragraphProperties) {
1768
+ writeParagraphPropertiesOdf(w, doc.docDefaults.paragraphProperties);
1769
+ }
1770
+ if (doc.docDefaults?.runProperties) {
1771
+ writeTextPropertiesOdf(w, doc.docDefaults.runProperties);
1772
+ }
1773
+ else {
1774
+ w.leafNode("style:text-properties", {
1775
+ "fo:font-size": "12pt",
1776
+ "style:font-name": "Times New Roman"
1777
+ });
1778
+ }
1779
+ w.closeNode();
1780
+ // Default table style
1781
+ w.openNode("style:default-style", { "style:family": "table" });
1782
+ w.closeNode();
1783
+ w.closeNode(); // office:styles
1784
+ // Automatic styles (page layout)
1785
+ w.openNode("office:automatic-styles");
1786
+ // Page layout
1787
+ w.openNode("style:page-layout", { "style:name": "pm1" });
1788
+ const pageProps = {};
1789
+ if (doc.sectionProperties?.pageSize) {
1790
+ pageProps["fo:page-width"] = twipsToCm(doc.sectionProperties.pageSize.width);
1791
+ pageProps["fo:page-height"] = twipsToCm(doc.sectionProperties.pageSize.height);
1792
+ if (doc.sectionProperties.pageSize.orientation === "landscape") {
1793
+ pageProps["style:print-orientation"] = "landscape";
1794
+ }
1795
+ }
1796
+ else {
1797
+ // Default A4
1798
+ pageProps["fo:page-width"] = "21.001cm";
1799
+ pageProps["fo:page-height"] = "29.700cm";
1800
+ }
1801
+ if (doc.sectionProperties?.margins) {
1802
+ const m = doc.sectionProperties.margins;
1803
+ pageProps["fo:margin-top"] = twipsToCm(m.top);
1804
+ pageProps["fo:margin-bottom"] = twipsToCm(m.bottom);
1805
+ pageProps["fo:margin-left"] = twipsToCm(m.left);
1806
+ pageProps["fo:margin-right"] = twipsToCm(m.right);
1807
+ }
1808
+ else {
1809
+ // Default 1 inch margins
1810
+ pageProps["fo:margin-top"] = "2.540cm";
1811
+ pageProps["fo:margin-bottom"] = "2.540cm";
1812
+ pageProps["fo:margin-left"] = "2.540cm";
1813
+ pageProps["fo:margin-right"] = "2.540cm";
1814
+ }
1815
+ w.leafNode("style:page-layout-properties", pageProps);
1816
+ w.closeNode(); // style:page-layout
1817
+ w.closeNode(); // office:automatic-styles
1818
+ // Master styles
1819
+ w.openNode("office:master-styles");
1820
+ w.leafNode("style:master-page", {
1821
+ "style:name": "Default",
1822
+ "style:page-layout-name": "pm1"
1823
+ });
1824
+ w.closeNode();
1825
+ w.closeNode(); // office:document-styles
1826
+ return w.xml;
1827
+ }
1828
+ // =============================================================================
1829
+ // Meta XML Generation
1830
+ // =============================================================================
1831
+ /** Generate meta.xml for the ODT package. */
1832
+ function generateMetaXml(doc) {
1833
+ const w = new XmlWriter();
1834
+ w.openXml();
1835
+ w.openNode("office:document-meta", {
1836
+ "xmlns:office": NS.office,
1837
+ "xmlns:meta": NS.meta,
1838
+ "xmlns:dc": NS.dc,
1839
+ "office:version": "1.3"
1840
+ });
1841
+ w.openNode("office:meta");
1842
+ if (doc.coreProperties) {
1843
+ const cp = doc.coreProperties;
1844
+ if (cp.title) {
1845
+ w.leafNode("dc:title", undefined, cp.title);
1846
+ }
1847
+ if (cp.subject) {
1848
+ w.leafNode("dc:subject", undefined, cp.subject);
1849
+ }
1850
+ if (cp.creator) {
1851
+ w.leafNode("meta:initial-creator", undefined, cp.creator);
1852
+ w.leafNode("dc:creator", undefined, cp.creator);
1853
+ }
1854
+ if (cp.description) {
1855
+ w.leafNode("dc:description", undefined, cp.description);
1856
+ }
1857
+ if (cp.keywords) {
1858
+ w.leafNode("meta:keyword", undefined, cp.keywords);
1859
+ }
1860
+ if (cp.created) {
1861
+ w.leafNode("meta:creation-date", undefined, cp.created.toISOString());
1862
+ }
1863
+ if (cp.modified) {
1864
+ w.leafNode("dc:date", undefined, cp.modified.toISOString());
1865
+ }
1866
+ }
1867
+ // Generator
1868
+ w.leafNode("meta:generator", undefined, "excelts/odt");
1869
+ w.closeNode(); // office:meta
1870
+ w.closeNode(); // office:document-meta
1871
+ return w.xml;
1872
+ }
1873
+ // =============================================================================
1874
+ // Manifest XML Generation
1875
+ // =============================================================================
1876
+ /** Generate META-INF/manifest.xml for the ODT package. */
1877
+ function generateManifestXml(imagePaths) {
1878
+ const w = new XmlWriter();
1879
+ w.openXml();
1880
+ w.openNode("manifest:manifest", {
1881
+ "xmlns:manifest": NS.manifest,
1882
+ "manifest:version": "1.3"
1883
+ });
1884
+ // Root entry
1885
+ w.leafNode("manifest:file-entry", {
1886
+ "manifest:full-path": "/",
1887
+ "manifest:version": "1.3",
1888
+ "manifest:media-type": "application/vnd.oasis.opendocument.text"
1889
+ });
1890
+ // Content parts
1891
+ w.leafNode("manifest:file-entry", {
1892
+ "manifest:full-path": "content.xml",
1893
+ "manifest:media-type": "text/xml"
1894
+ });
1895
+ w.leafNode("manifest:file-entry", {
1896
+ "manifest:full-path": "styles.xml",
1897
+ "manifest:media-type": "text/xml"
1898
+ });
1899
+ w.leafNode("manifest:file-entry", {
1900
+ "manifest:full-path": "meta.xml",
1901
+ "manifest:media-type": "text/xml"
1902
+ });
1903
+ // Images
1904
+ for (const imgPath of imagePaths) {
1905
+ const ext = imgPath.substring(imgPath.lastIndexOf(".") + 1).toLowerCase();
1906
+ let mediaType;
1907
+ switch (ext) {
1908
+ case "png":
1909
+ mediaType = "image/png";
1910
+ break;
1911
+ case "jpg":
1912
+ case "jpeg":
1913
+ mediaType = "image/jpeg";
1914
+ break;
1915
+ case "gif":
1916
+ mediaType = "image/gif";
1917
+ break;
1918
+ case "svg":
1919
+ mediaType = "image/svg+xml";
1920
+ break;
1921
+ default:
1922
+ mediaType = "application/octet-stream";
1923
+ break;
1924
+ }
1925
+ w.leafNode("manifest:file-entry", {
1926
+ "manifest:full-path": imgPath,
1927
+ "manifest:media-type": mediaType
1928
+ });
1929
+ }
1930
+ w.closeNode(); // manifest:manifest
1931
+ return w.xml;
1932
+ }