@cj-tech-master/excelts 9.4.2 → 9.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (618) hide show
  1. package/dist/browser/index.browser.d.ts +8 -5
  2. package/dist/browser/index.browser.js +19 -1
  3. package/dist/browser/index.d.ts +4 -2
  4. package/dist/browser/index.js +9 -1
  5. package/dist/browser/modules/excel/chart/cache-populator.d.ts +49 -0
  6. package/dist/browser/modules/excel/chart/cache-populator.js +1171 -0
  7. package/dist/browser/modules/excel/chart/chart-api.d.ts +92 -0
  8. package/dist/browser/modules/excel/chart/chart-api.js +364 -0
  9. package/dist/browser/modules/excel/chart/chart-builder.d.ts +48 -0
  10. package/dist/browser/modules/excel/chart/chart-builder.js +2432 -0
  11. package/dist/browser/modules/excel/chart/chart-ex-builder.d.ts +36 -0
  12. package/dist/browser/modules/excel/chart/chart-ex-builder.js +903 -0
  13. package/dist/browser/modules/excel/chart/chart-ex-parser.d.ts +8 -0
  14. package/dist/browser/modules/excel/chart/chart-ex-parser.js +1205 -0
  15. package/dist/browser/modules/excel/chart/chart-ex-renderer.d.ts +187 -0
  16. package/dist/browser/modules/excel/chart/chart-ex-renderer.js +5352 -0
  17. package/dist/browser/modules/excel/chart/chart-ex-types.d.ts +531 -0
  18. package/dist/browser/modules/excel/chart/chart-ex-types.js +11 -0
  19. package/dist/browser/modules/excel/chart/chart-images.d.ts +78 -0
  20. package/dist/browser/modules/excel/chart/chart-images.js +363 -0
  21. package/dist/browser/modules/excel/chart/chart-presets.d.ts +392 -0
  22. package/dist/browser/modules/excel/chart/chart-presets.js +179 -0
  23. package/dist/browser/modules/excel/chart/chart-renderer.d.ts +550 -0
  24. package/dist/browser/modules/excel/chart/chart-renderer.js +6440 -0
  25. package/dist/browser/modules/excel/chart/chart-sidecar.d.ts +21 -0
  26. package/dist/browser/modules/excel/chart/chart-sidecar.js +427 -0
  27. package/dist/browser/modules/excel/chart/chart-utils.d.ts +306 -0
  28. package/dist/browser/modules/excel/chart/chart-utils.js +821 -0
  29. package/dist/browser/modules/excel/chart/chart.d.ts +504 -0
  30. package/dist/browser/modules/excel/chart/chart.js +1320 -0
  31. package/dist/browser/modules/excel/chart/glyph-rasterizer.d.ts +62 -0
  32. package/dist/browser/modules/excel/chart/glyph-rasterizer.js +658 -0
  33. package/dist/browser/modules/excel/chart/index.d.ts +54 -0
  34. package/dist/browser/modules/excel/chart/index.js +46 -0
  35. package/dist/browser/modules/excel/chart/install.d.ts +44 -0
  36. package/dist/browser/modules/excel/chart/install.js +91 -0
  37. package/dist/browser/modules/excel/chart/shape-properties.d.ts +156 -0
  38. package/dist/browser/modules/excel/chart/shape-properties.js +1557 -0
  39. package/dist/browser/modules/excel/chart/stroke-font.d.ts +36 -0
  40. package/dist/browser/modules/excel/chart/stroke-font.js +1556 -0
  41. package/dist/browser/modules/excel/chart/topojson.d.ts +98 -0
  42. package/dist/browser/modules/excel/chart/topojson.js +236 -0
  43. package/dist/browser/modules/excel/chart/types.d.ts +2559 -0
  44. package/dist/browser/modules/excel/chart/types.js +8 -0
  45. package/dist/browser/modules/excel/chart-host-registry.d.ts +157 -0
  46. package/dist/browser/modules/excel/chart-host-registry.js +90 -0
  47. package/dist/browser/modules/excel/chartsheet.d.ts +102 -0
  48. package/dist/browser/modules/excel/chartsheet.js +196 -0
  49. package/dist/browser/modules/excel/defined-names.d.ts +35 -0
  50. package/dist/browser/modules/excel/defined-names.js +44 -4
  51. package/dist/browser/modules/excel/errors.d.ts +6 -0
  52. package/dist/browser/modules/excel/errors.js +9 -0
  53. package/dist/browser/modules/excel/form-control.d.ts +6 -0
  54. package/dist/browser/modules/excel/form-control.js +17 -0
  55. package/dist/browser/modules/excel/image.js +12 -2
  56. package/dist/browser/modules/excel/pivot-chart.d.ts +7 -0
  57. package/dist/browser/modules/excel/pivot-chart.js +53 -0
  58. package/dist/browser/modules/excel/pivot-table.d.ts +55 -0
  59. package/dist/browser/modules/excel/pivot-table.js +35 -0
  60. package/dist/browser/modules/excel/range.js +5 -1
  61. package/dist/browser/modules/excel/sparkline/index.d.ts +7 -0
  62. package/dist/browser/modules/excel/sparkline/index.js +7 -0
  63. package/dist/browser/modules/excel/sparkline/sparkline.d.ts +206 -0
  64. package/dist/browser/modules/excel/sparkline/sparkline.js +750 -0
  65. package/dist/browser/modules/excel/stream/worksheet-writer.js +3 -2
  66. package/dist/browser/modules/excel/table.js +42 -6
  67. package/dist/browser/modules/excel/types.d.ts +72 -0
  68. package/dist/browser/modules/excel/utils/address.d.ts +18 -0
  69. package/dist/browser/modules/excel/utils/address.js +28 -0
  70. package/dist/browser/modules/excel/utils/drawing-utils.js +11 -6
  71. package/dist/browser/modules/excel/utils/guid.d.ts +15 -0
  72. package/dist/browser/modules/excel/utils/guid.js +35 -0
  73. package/dist/browser/modules/excel/utils/ooxml-paths.d.ts +74 -0
  74. package/dist/browser/modules/excel/utils/ooxml-paths.js +206 -9
  75. package/dist/browser/modules/excel/utils/ooxml-validator/check-chart-sidecar.d.ts +35 -0
  76. package/dist/browser/modules/excel/utils/ooxml-validator/check-chart-sidecar.js +101 -0
  77. package/dist/browser/modules/excel/utils/ooxml-validator/check-chart.d.ts +32 -0
  78. package/dist/browser/modules/excel/utils/ooxml-validator/check-chart.js +2125 -0
  79. package/dist/browser/modules/excel/utils/ooxml-validator/check-chartsheet.d.ts +9 -0
  80. package/dist/browser/modules/excel/utils/ooxml-validator/check-chartsheet.js +26 -0
  81. package/dist/browser/modules/excel/utils/ooxml-validator/check-content-types.d.ts +16 -0
  82. package/dist/browser/modules/excel/utils/ooxml-validator/check-content-types.js +181 -0
  83. package/dist/browser/modules/excel/utils/ooxml-validator/check-drawing.d.ts +34 -0
  84. package/dist/browser/modules/excel/utils/ooxml-validator/check-drawing.js +267 -0
  85. package/dist/browser/modules/excel/utils/ooxml-validator/check-pivot.d.ts +14 -0
  86. package/dist/browser/modules/excel/utils/ooxml-validator/check-pivot.js +104 -0
  87. package/dist/browser/modules/excel/utils/ooxml-validator/check-relationships.d.ts +18 -0
  88. package/dist/browser/modules/excel/utils/ooxml-validator/check-relationships.js +184 -0
  89. package/dist/browser/modules/excel/utils/ooxml-validator/check-structure.d.ts +21 -0
  90. package/dist/browser/modules/excel/utils/ooxml-validator/check-structure.js +56 -0
  91. package/dist/browser/modules/excel/utils/ooxml-validator/check-styles.d.ts +15 -0
  92. package/dist/browser/modules/excel/utils/ooxml-validator/check-styles.js +89 -0
  93. package/dist/browser/modules/excel/utils/ooxml-validator/check-table.d.ts +31 -0
  94. package/dist/browser/modules/excel/utils/ooxml-validator/check-table.js +177 -0
  95. package/dist/browser/modules/excel/utils/ooxml-validator/check-workbook.d.ts +19 -0
  96. package/dist/browser/modules/excel/utils/ooxml-validator/check-workbook.js +163 -0
  97. package/dist/browser/modules/excel/utils/ooxml-validator/check-worksheet.d.ts +25 -0
  98. package/dist/browser/modules/excel/utils/ooxml-validator/check-worksheet.js +569 -0
  99. package/dist/browser/modules/excel/utils/ooxml-validator/context.d.ts +85 -0
  100. package/dist/browser/modules/excel/utils/ooxml-validator/context.js +191 -0
  101. package/dist/browser/modules/excel/utils/ooxml-validator/index.d.ts +31 -0
  102. package/dist/browser/modules/excel/utils/ooxml-validator/index.js +102 -0
  103. package/dist/browser/modules/excel/utils/ooxml-validator/path-utils.d.ts +67 -0
  104. package/dist/browser/modules/excel/utils/ooxml-validator/path-utils.js +156 -0
  105. package/dist/browser/modules/excel/utils/ooxml-validator/reporter.d.ts +41 -0
  106. package/dist/browser/modules/excel/utils/ooxml-validator/reporter.js +61 -0
  107. package/dist/browser/modules/excel/utils/ooxml-validator/types.d.ts +109 -0
  108. package/dist/browser/modules/excel/utils/ooxml-validator/types.js +12 -0
  109. package/dist/browser/modules/excel/utils/ooxml-validator/xml-utils.d.ts +38 -0
  110. package/dist/browser/modules/excel/utils/ooxml-validator/xml-utils.js +100 -0
  111. package/dist/browser/modules/excel/workbook.browser.d.ts +248 -30
  112. package/dist/browser/modules/excel/workbook.browser.js +966 -31
  113. package/dist/browser/modules/excel/workbook.d.ts +43 -0
  114. package/dist/browser/modules/excel/workbook.js +48 -0
  115. package/dist/browser/modules/excel/worksheet.d.ts +157 -3
  116. package/dist/browser/modules/excel/worksheet.js +394 -35
  117. package/dist/browser/modules/excel/xlsx/rel-type.d.ts +40 -0
  118. package/dist/browser/modules/excel/xlsx/rel-type.js +41 -1
  119. package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +1 -0
  120. package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.js +11 -2
  121. package/dist/browser/modules/excel/xlsx/xform/book/external-link-xform.js +12 -10
  122. package/dist/browser/modules/excel/xlsx/xform/book/workbook-xform.js +96 -22
  123. package/dist/browser/modules/excel/xlsx/xform/chart/chart-space-xform.d.ts +353 -0
  124. package/dist/browser/modules/excel/xlsx/xform/chart/chart-space-xform.js +6000 -0
  125. package/dist/browser/modules/excel/xlsx/xform/comment/threaded-comments-xform.d.ts +60 -0
  126. package/dist/browser/modules/excel/xlsx/xform/comment/threaded-comments-xform.js +213 -0
  127. package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +150 -11
  128. package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +20 -1
  129. package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +1 -1
  130. package/dist/browser/modules/excel/xlsx/xform/drawing/drawing-xform.d.ts +30 -0
  131. package/dist/browser/modules/excel/xlsx/xform/drawing/drawing-xform.js +109 -5
  132. package/dist/browser/modules/excel/xlsx/xform/drawing/graphic-frame-xform.d.ts +54 -0
  133. package/dist/browser/modules/excel/xlsx/xform/drawing/graphic-frame-xform.js +225 -0
  134. package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +3 -1
  135. package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +18 -3
  136. package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.d.ts +46 -0
  137. package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +294 -12
  138. package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +13 -2
  139. package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +32 -6
  140. package/dist/browser/modules/excel/xlsx/xform/sheet/chartsheet-xform.d.ts +185 -0
  141. package/dist/browser/modules/excel/xlsx/xform/sheet/chartsheet-xform.js +441 -0
  142. package/dist/browser/modules/excel/xlsx/xform/sheet/ext-lst-xform.d.ts +1 -0
  143. package/dist/browser/modules/excel/xlsx/xform/sheet/ext-lst-xform.js +51 -2
  144. package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +196 -20
  145. package/dist/browser/modules/excel/xlsx/xform/table/auto-filter-xform.js +16 -1
  146. package/dist/browser/modules/excel/xlsx/xform/table/table-column-xform.js +17 -2
  147. package/dist/browser/modules/excel/xlsx/xform/xsd-values.d.ts +63 -0
  148. package/dist/browser/modules/excel/xlsx/xform/xsd-values.js +101 -0
  149. package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +115 -21
  150. package/dist/browser/modules/excel/xlsx/xlsx.browser.js +4422 -78
  151. package/dist/browser/modules/pdf/builder/document-builder.d.ts +74 -0
  152. package/dist/browser/modules/pdf/builder/document-builder.js +507 -2
  153. package/dist/browser/modules/pdf/builder/pdf-editor.js +48 -3
  154. package/dist/browser/modules/pdf/excel-bridge.d.ts +69 -0
  155. package/dist/browser/modules/pdf/excel-bridge.js +683 -12
  156. package/dist/browser/modules/pdf/font/font-manager.d.ts +25 -0
  157. package/dist/browser/modules/pdf/font/font-manager.js +39 -0
  158. package/dist/browser/modules/pdf/index.d.ts +5 -2
  159. package/dist/browser/modules/pdf/index.js +3 -1
  160. package/dist/browser/modules/pdf/render/chart-surface.d.ts +33 -0
  161. package/dist/browser/modules/pdf/render/chart-surface.js +200 -0
  162. package/dist/browser/modules/pdf/render/layout-engine.d.ts +22 -1
  163. package/dist/browser/modules/pdf/render/layout-engine.js +436 -56
  164. package/dist/browser/modules/pdf/render/page-renderer.js +169 -28
  165. package/dist/browser/modules/pdf/render/pdf-exporter.js +117 -7
  166. package/dist/browser/modules/pdf/types.d.ts +227 -23
  167. package/dist/browser/modules/pdf/types.js +4 -0
  168. package/dist/browser/modules/pdf/word-bridge.d.ts +47 -0
  169. package/dist/browser/modules/pdf/word-bridge.js +304 -0
  170. package/dist/browser/modules/word/constants.d.ts +179 -0
  171. package/dist/browser/modules/word/constants.js +231 -0
  172. package/dist/browser/modules/word/content-types.d.ts +27 -0
  173. package/dist/browser/modules/word/content-types.js +53 -0
  174. package/dist/browser/modules/word/digital-signatures.d.ts +87 -0
  175. package/dist/browser/modules/word/digital-signatures.js +134 -0
  176. package/dist/browser/modules/word/document.d.ts +728 -0
  177. package/dist/browser/modules/word/document.js +1795 -0
  178. package/dist/browser/modules/word/docx-packager.d.ts +14 -0
  179. package/dist/browser/modules/word/docx-packager.js +822 -0
  180. package/dist/browser/modules/word/docx-reader.d.ts +11 -0
  181. package/dist/browser/modules/word/docx-reader.js +4929 -0
  182. package/dist/browser/modules/word/encryption.d.ts +102 -0
  183. package/dist/browser/modules/word/encryption.js +274 -0
  184. package/dist/browser/modules/word/errors.d.ts +49 -0
  185. package/dist/browser/modules/word/errors.js +68 -0
  186. package/dist/browser/modules/word/font-obfuscation.d.ts +31 -0
  187. package/dist/browser/modules/word/font-obfuscation.js +83 -0
  188. package/dist/browser/modules/word/html-renderer.d.ts +38 -0
  189. package/dist/browser/modules/word/html-renderer.js +782 -0
  190. package/dist/browser/modules/word/index.base.d.ts +19 -0
  191. package/dist/browser/modules/word/index.base.js +51 -0
  192. package/dist/browser/modules/word/index.browser.d.ts +4 -0
  193. package/dist/browser/modules/word/index.browser.js +4 -0
  194. package/dist/browser/modules/word/index.d.ts +4 -0
  195. package/dist/browser/modules/word/index.js +4 -0
  196. package/dist/browser/modules/word/internal-utils.d.ts +23 -0
  197. package/dist/browser/modules/word/internal-utils.js +54 -0
  198. package/dist/browser/modules/word/relationships.d.ts +31 -0
  199. package/dist/browser/modules/word/relationships.js +56 -0
  200. package/dist/browser/modules/word/types.d.ts +2325 -0
  201. package/dist/browser/modules/word/types.js +10 -0
  202. package/dist/browser/modules/word/units.d.ts +49 -0
  203. package/dist/browser/modules/word/units.js +111 -0
  204. package/dist/browser/modules/word/writers/chart-writer.d.ts +10 -0
  205. package/dist/browser/modules/word/writers/chart-writer.js +385 -0
  206. package/dist/browser/modules/word/writers/checkbox-writer.d.ts +9 -0
  207. package/dist/browser/modules/word/writers/checkbox-writer.js +42 -0
  208. package/dist/browser/modules/word/writers/comment-writer.d.ts +15 -0
  209. package/dist/browser/modules/word/writers/comment-writer.js +70 -0
  210. package/dist/browser/modules/word/writers/document-writer.d.ts +16 -0
  211. package/dist/browser/modules/word/writers/document-writer.js +461 -0
  212. package/dist/browser/modules/word/writers/footnote-writer.d.ts +11 -0
  213. package/dist/browser/modules/word/writers/footnote-writer.js +72 -0
  214. package/dist/browser/modules/word/writers/header-footer-writer.d.ts +13 -0
  215. package/dist/browser/modules/word/writers/header-footer-writer.js +129 -0
  216. package/dist/browser/modules/word/writers/image-writer.d.ts +10 -0
  217. package/dist/browser/modules/word/writers/image-writer.js +185 -0
  218. package/dist/browser/modules/word/writers/math-writer.d.ts +9 -0
  219. package/dist/browser/modules/word/writers/math-writer.js +428 -0
  220. package/dist/browser/modules/word/writers/numbering-writer.d.ts +10 -0
  221. package/dist/browser/modules/word/writers/numbering-writer.js +125 -0
  222. package/dist/browser/modules/word/writers/paragraph-writer.d.ts +13 -0
  223. package/dist/browser/modules/word/writers/paragraph-writer.js +516 -0
  224. package/dist/browser/modules/word/writers/parts-writer.d.ts +26 -0
  225. package/dist/browser/modules/word/writers/parts-writer.js +660 -0
  226. package/dist/browser/modules/word/writers/run-writer.d.ts +15 -0
  227. package/dist/browser/modules/word/writers/run-writer.js +649 -0
  228. package/dist/browser/modules/word/writers/section-writer.d.ts +10 -0
  229. package/dist/browser/modules/word/writers/section-writer.js +238 -0
  230. package/dist/browser/modules/word/writers/styles-writer.d.ts +10 -0
  231. package/dist/browser/modules/word/writers/styles-writer.js +242 -0
  232. package/dist/browser/modules/word/writers/table-writer.d.ts +10 -0
  233. package/dist/browser/modules/word/writers/table-writer.js +503 -0
  234. package/dist/browser/modules/word/writers/textbox-writer.d.ts +9 -0
  235. package/dist/browser/modules/word/writers/textbox-writer.js +53 -0
  236. package/dist/browser/modules/word/writers/toc-writer.d.ts +9 -0
  237. package/dist/browser/modules/word/writers/toc-writer.js +79 -0
  238. package/dist/browser/modules/xml/encode.d.ts +56 -7
  239. package/dist/browser/modules/xml/encode.js +157 -11
  240. package/dist/cjs/index.js +13 -2
  241. package/dist/cjs/modules/excel/chart/cache-populator.js +1178 -0
  242. package/dist/cjs/modules/excel/chart/chart-api.js +371 -0
  243. package/dist/cjs/modules/excel/chart/chart-builder.js +2440 -0
  244. package/dist/cjs/modules/excel/chart/chart-ex-builder.js +907 -0
  245. package/dist/cjs/modules/excel/chart/chart-ex-parser.js +1208 -0
  246. package/dist/cjs/modules/excel/chart/chart-ex-renderer.js +5364 -0
  247. package/dist/cjs/modules/excel/chart/chart-ex-types.js +12 -0
  248. package/dist/cjs/modules/excel/chart/chart-images.js +366 -0
  249. package/dist/cjs/modules/excel/chart/chart-presets.js +184 -0
  250. package/dist/cjs/modules/excel/chart/chart-renderer.js +6450 -0
  251. package/dist/cjs/modules/excel/chart/chart-sidecar.js +433 -0
  252. package/dist/cjs/modules/excel/chart/chart-utils.js +845 -0
  253. package/dist/cjs/modules/excel/chart/chart.js +1324 -0
  254. package/dist/cjs/modules/excel/chart/glyph-rasterizer.js +664 -0
  255. package/dist/cjs/modules/excel/chart/index.js +101 -0
  256. package/dist/cjs/modules/excel/chart/install.js +95 -0
  257. package/dist/cjs/modules/excel/chart/shape-properties.js +1577 -0
  258. package/dist/cjs/modules/excel/chart/stroke-font.js +1559 -0
  259. package/dist/cjs/modules/excel/chart/topojson.js +239 -0
  260. package/dist/cjs/modules/excel/chart/types.js +9 -0
  261. package/dist/cjs/modules/excel/chart-host-registry.js +96 -0
  262. package/dist/cjs/modules/excel/chartsheet.js +199 -0
  263. package/dist/cjs/modules/excel/defined-names.js +44 -4
  264. package/dist/cjs/modules/excel/errors.js +11 -1
  265. package/dist/cjs/modules/excel/form-control.js +17 -0
  266. package/dist/cjs/modules/excel/image.js +12 -2
  267. package/dist/cjs/modules/excel/pivot-chart.js +56 -0
  268. package/dist/cjs/modules/excel/pivot-table.js +35 -0
  269. package/dist/cjs/modules/excel/range.js +5 -1
  270. package/dist/cjs/modules/excel/sparkline/index.js +23 -0
  271. package/dist/cjs/modules/excel/sparkline/sparkline.js +756 -0
  272. package/dist/cjs/modules/excel/stream/worksheet-writer.js +3 -2
  273. package/dist/cjs/modules/excel/table.js +42 -6
  274. package/dist/cjs/modules/excel/utils/address.js +29 -0
  275. package/dist/cjs/modules/excel/utils/drawing-utils.js +11 -6
  276. package/dist/cjs/modules/excel/utils/guid.js +38 -0
  277. package/dist/cjs/modules/excel/utils/ooxml-paths.js +246 -9
  278. package/dist/cjs/modules/excel/utils/ooxml-validator/check-chart-sidecar.js +103 -0
  279. package/dist/cjs/modules/excel/utils/ooxml-validator/check-chart.js +2128 -0
  280. package/dist/cjs/modules/excel/utils/ooxml-validator/check-chartsheet.js +29 -0
  281. package/dist/cjs/modules/excel/utils/ooxml-validator/check-content-types.js +184 -0
  282. package/dist/cjs/modules/excel/utils/ooxml-validator/check-drawing.js +270 -0
  283. package/dist/cjs/modules/excel/utils/ooxml-validator/check-pivot.js +107 -0
  284. package/dist/cjs/modules/excel/utils/ooxml-validator/check-relationships.js +188 -0
  285. package/dist/cjs/modules/excel/utils/ooxml-validator/check-structure.js +60 -0
  286. package/dist/cjs/modules/excel/utils/ooxml-validator/check-styles.js +92 -0
  287. package/dist/cjs/modules/excel/utils/ooxml-validator/check-table.js +180 -0
  288. package/dist/cjs/modules/excel/utils/ooxml-validator/check-workbook.js +166 -0
  289. package/dist/cjs/modules/excel/utils/ooxml-validator/check-worksheet.js +572 -0
  290. package/dist/cjs/modules/excel/utils/ooxml-validator/context.js +196 -0
  291. package/dist/cjs/modules/excel/utils/ooxml-validator/index.js +105 -0
  292. package/dist/cjs/modules/excel/utils/ooxml-validator/path-utils.js +168 -0
  293. package/dist/cjs/modules/excel/utils/ooxml-validator/reporter.js +66 -0
  294. package/dist/cjs/modules/excel/utils/ooxml-validator/types.js +13 -0
  295. package/dist/cjs/modules/excel/utils/ooxml-validator/xml-utils.js +110 -0
  296. package/dist/cjs/modules/excel/workbook.browser.js +973 -38
  297. package/dist/cjs/modules/excel/workbook.js +48 -0
  298. package/dist/cjs/modules/excel/worksheet.js +393 -34
  299. package/dist/cjs/modules/excel/xlsx/rel-type.js +41 -1
  300. package/dist/cjs/modules/excel/xlsx/xform/book/defined-name-xform.js +11 -2
  301. package/dist/cjs/modules/excel/xlsx/xform/book/external-link-xform.js +12 -10
  302. package/dist/cjs/modules/excel/xlsx/xform/book/workbook-xform.js +96 -22
  303. package/dist/cjs/modules/excel/xlsx/xform/chart/chart-space-xform.js +6003 -0
  304. package/dist/cjs/modules/excel/xlsx/xform/comment/threaded-comments-xform.js +219 -0
  305. package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +149 -10
  306. package/dist/cjs/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +20 -1
  307. package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +1 -1
  308. package/dist/cjs/modules/excel/xlsx/xform/drawing/drawing-xform.js +109 -5
  309. package/dist/cjs/modules/excel/xlsx/xform/drawing/graphic-frame-xform.js +228 -0
  310. package/dist/cjs/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +18 -3
  311. package/dist/cjs/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +294 -12
  312. package/dist/cjs/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +32 -6
  313. package/dist/cjs/modules/excel/xlsx/xform/sheet/chartsheet-xform.js +444 -0
  314. package/dist/cjs/modules/excel/xlsx/xform/sheet/ext-lst-xform.js +51 -2
  315. package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +195 -19
  316. package/dist/cjs/modules/excel/xlsx/xform/table/auto-filter-xform.js +16 -1
  317. package/dist/cjs/modules/excel/xlsx/xform/table/table-column-xform.js +17 -2
  318. package/dist/cjs/modules/excel/xlsx/xform/xsd-values.js +106 -0
  319. package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +4420 -76
  320. package/dist/cjs/modules/pdf/builder/document-builder.js +506 -1
  321. package/dist/cjs/modules/pdf/builder/pdf-editor.js +48 -3
  322. package/dist/cjs/modules/pdf/excel-bridge.js +684 -12
  323. package/dist/cjs/modules/pdf/font/font-manager.js +39 -0
  324. package/dist/cjs/modules/pdf/index.js +5 -1
  325. package/dist/cjs/modules/pdf/render/chart-surface.js +203 -0
  326. package/dist/cjs/modules/pdf/render/layout-engine.js +437 -56
  327. package/dist/cjs/modules/pdf/render/page-renderer.js +169 -28
  328. package/dist/cjs/modules/pdf/render/pdf-exporter.js +115 -5
  329. package/dist/cjs/modules/pdf/types.js +5 -0
  330. package/dist/cjs/modules/pdf/word-bridge.js +307 -0
  331. package/dist/cjs/modules/word/constants.js +234 -0
  332. package/dist/cjs/modules/word/content-types.js +57 -0
  333. package/dist/cjs/modules/word/digital-signatures.js +140 -0
  334. package/dist/cjs/modules/word/document.js +1909 -0
  335. package/dist/cjs/modules/word/docx-packager.js +825 -0
  336. package/dist/cjs/modules/word/docx-reader.js +4932 -0
  337. package/dist/cjs/modules/word/encryption.js +282 -0
  338. package/dist/cjs/modules/word/errors.js +88 -0
  339. package/dist/cjs/modules/word/font-obfuscation.js +88 -0
  340. package/dist/cjs/modules/word/html-renderer.js +785 -0
  341. package/dist/cjs/modules/word/index.base.js +199 -0
  342. package/dist/cjs/modules/word/index.browser.js +20 -0
  343. package/dist/cjs/modules/word/index.js +20 -0
  344. package/dist/cjs/modules/word/internal-utils.js +59 -0
  345. package/dist/cjs/modules/word/relationships.js +60 -0
  346. package/dist/cjs/modules/word/types.js +11 -0
  347. package/dist/cjs/modules/word/units.js +135 -0
  348. package/dist/cjs/modules/word/writers/chart-writer.js +388 -0
  349. package/dist/cjs/modules/word/writers/checkbox-writer.js +45 -0
  350. package/dist/cjs/modules/word/writers/comment-writer.js +74 -0
  351. package/dist/cjs/modules/word/writers/document-writer.js +465 -0
  352. package/dist/cjs/modules/word/writers/footnote-writer.js +76 -0
  353. package/dist/cjs/modules/word/writers/header-footer-writer.js +134 -0
  354. package/dist/cjs/modules/word/writers/image-writer.js +188 -0
  355. package/dist/cjs/modules/word/writers/math-writer.js +431 -0
  356. package/dist/cjs/modules/word/writers/numbering-writer.js +128 -0
  357. package/dist/cjs/modules/word/writers/paragraph-writer.js +521 -0
  358. package/dist/cjs/modules/word/writers/parts-writer.js +671 -0
  359. package/dist/cjs/modules/word/writers/run-writer.js +655 -0
  360. package/dist/cjs/modules/word/writers/section-writer.js +241 -0
  361. package/dist/cjs/modules/word/writers/styles-writer.js +245 -0
  362. package/dist/cjs/modules/word/writers/table-writer.js +506 -0
  363. package/dist/cjs/modules/word/writers/textbox-writer.js +56 -0
  364. package/dist/cjs/modules/word/writers/toc-writer.js +82 -0
  365. package/dist/cjs/modules/xml/encode.js +158 -11
  366. package/dist/esm/index.browser.js +20 -2
  367. package/dist/esm/index.js +9 -1
  368. package/dist/esm/modules/excel/chart/cache-populator.js +1171 -0
  369. package/dist/esm/modules/excel/chart/chart-api.js +364 -0
  370. package/dist/esm/modules/excel/chart/chart-builder.js +2432 -0
  371. package/dist/esm/modules/excel/chart/chart-ex-builder.js +903 -0
  372. package/dist/esm/modules/excel/chart/chart-ex-parser.js +1205 -0
  373. package/dist/esm/modules/excel/chart/chart-ex-renderer.js +5352 -0
  374. package/dist/esm/modules/excel/chart/chart-ex-types.js +11 -0
  375. package/dist/esm/modules/excel/chart/chart-images.js +363 -0
  376. package/dist/esm/modules/excel/chart/chart-presets.js +179 -0
  377. package/dist/esm/modules/excel/chart/chart-renderer.js +6440 -0
  378. package/dist/esm/modules/excel/chart/chart-sidecar.js +427 -0
  379. package/dist/esm/modules/excel/chart/chart-utils.js +821 -0
  380. package/dist/esm/modules/excel/chart/chart.js +1320 -0
  381. package/dist/esm/modules/excel/chart/glyph-rasterizer.js +658 -0
  382. package/dist/esm/modules/excel/chart/index.js +46 -0
  383. package/dist/esm/modules/excel/chart/install.js +91 -0
  384. package/dist/esm/modules/excel/chart/shape-properties.js +1557 -0
  385. package/dist/esm/modules/excel/chart/stroke-font.js +1556 -0
  386. package/dist/esm/modules/excel/chart/topojson.js +236 -0
  387. package/dist/esm/modules/excel/chart/types.js +8 -0
  388. package/dist/esm/modules/excel/chart-host-registry.js +90 -0
  389. package/dist/esm/modules/excel/chartsheet.js +196 -0
  390. package/dist/esm/modules/excel/defined-names.js +44 -4
  391. package/dist/esm/modules/excel/errors.js +9 -0
  392. package/dist/esm/modules/excel/form-control.js +17 -0
  393. package/dist/esm/modules/excel/image.js +12 -2
  394. package/dist/esm/modules/excel/pivot-chart.js +53 -0
  395. package/dist/esm/modules/excel/pivot-table.js +35 -0
  396. package/dist/esm/modules/excel/range.js +5 -1
  397. package/dist/esm/modules/excel/sparkline/index.js +7 -0
  398. package/dist/esm/modules/excel/sparkline/sparkline.js +750 -0
  399. package/dist/esm/modules/excel/stream/worksheet-writer.js +3 -2
  400. package/dist/esm/modules/excel/table.js +42 -6
  401. package/dist/esm/modules/excel/utils/address.js +28 -0
  402. package/dist/esm/modules/excel/utils/drawing-utils.js +11 -6
  403. package/dist/esm/modules/excel/utils/guid.js +35 -0
  404. package/dist/esm/modules/excel/utils/ooxml-paths.js +206 -9
  405. package/dist/esm/modules/excel/utils/ooxml-validator/check-chart-sidecar.js +101 -0
  406. package/dist/esm/modules/excel/utils/ooxml-validator/check-chart.js +2125 -0
  407. package/dist/esm/modules/excel/utils/ooxml-validator/check-chartsheet.js +26 -0
  408. package/dist/esm/modules/excel/utils/ooxml-validator/check-content-types.js +181 -0
  409. package/dist/esm/modules/excel/utils/ooxml-validator/check-drawing.js +267 -0
  410. package/dist/esm/modules/excel/utils/ooxml-validator/check-pivot.js +104 -0
  411. package/dist/esm/modules/excel/utils/ooxml-validator/check-relationships.js +184 -0
  412. package/dist/esm/modules/excel/utils/ooxml-validator/check-structure.js +56 -0
  413. package/dist/esm/modules/excel/utils/ooxml-validator/check-styles.js +89 -0
  414. package/dist/esm/modules/excel/utils/ooxml-validator/check-table.js +177 -0
  415. package/dist/esm/modules/excel/utils/ooxml-validator/check-workbook.js +163 -0
  416. package/dist/esm/modules/excel/utils/ooxml-validator/check-worksheet.js +569 -0
  417. package/dist/esm/modules/excel/utils/ooxml-validator/context.js +191 -0
  418. package/dist/esm/modules/excel/utils/ooxml-validator/index.js +102 -0
  419. package/dist/esm/modules/excel/utils/ooxml-validator/path-utils.js +156 -0
  420. package/dist/esm/modules/excel/utils/ooxml-validator/reporter.js +61 -0
  421. package/dist/esm/modules/excel/utils/ooxml-validator/types.js +12 -0
  422. package/dist/esm/modules/excel/utils/ooxml-validator/xml-utils.js +100 -0
  423. package/dist/esm/modules/excel/workbook.browser.js +969 -34
  424. package/dist/esm/modules/excel/workbook.js +48 -0
  425. package/dist/esm/modules/excel/worksheet.js +394 -35
  426. package/dist/esm/modules/excel/xlsx/rel-type.js +41 -1
  427. package/dist/esm/modules/excel/xlsx/xform/book/defined-name-xform.js +11 -2
  428. package/dist/esm/modules/excel/xlsx/xform/book/external-link-xform.js +12 -10
  429. package/dist/esm/modules/excel/xlsx/xform/book/workbook-xform.js +96 -22
  430. package/dist/esm/modules/excel/xlsx/xform/chart/chart-space-xform.js +6000 -0
  431. package/dist/esm/modules/excel/xlsx/xform/comment/threaded-comments-xform.js +213 -0
  432. package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +150 -11
  433. package/dist/esm/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +20 -1
  434. package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +1 -1
  435. package/dist/esm/modules/excel/xlsx/xform/drawing/drawing-xform.js +109 -5
  436. package/dist/esm/modules/excel/xlsx/xform/drawing/graphic-frame-xform.js +225 -0
  437. package/dist/esm/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +18 -3
  438. package/dist/esm/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +294 -12
  439. package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +32 -6
  440. package/dist/esm/modules/excel/xlsx/xform/sheet/chartsheet-xform.js +441 -0
  441. package/dist/esm/modules/excel/xlsx/xform/sheet/ext-lst-xform.js +51 -2
  442. package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +196 -20
  443. package/dist/esm/modules/excel/xlsx/xform/table/auto-filter-xform.js +16 -1
  444. package/dist/esm/modules/excel/xlsx/xform/table/table-column-xform.js +17 -2
  445. package/dist/esm/modules/excel/xlsx/xform/xsd-values.js +101 -0
  446. package/dist/esm/modules/excel/xlsx/xlsx.browser.js +4422 -78
  447. package/dist/esm/modules/pdf/builder/document-builder.js +507 -2
  448. package/dist/esm/modules/pdf/builder/pdf-editor.js +48 -3
  449. package/dist/esm/modules/pdf/excel-bridge.js +683 -12
  450. package/dist/esm/modules/pdf/font/font-manager.js +39 -0
  451. package/dist/esm/modules/pdf/index.js +3 -1
  452. package/dist/esm/modules/pdf/render/chart-surface.js +200 -0
  453. package/dist/esm/modules/pdf/render/layout-engine.js +436 -56
  454. package/dist/esm/modules/pdf/render/page-renderer.js +169 -28
  455. package/dist/esm/modules/pdf/render/pdf-exporter.js +117 -7
  456. package/dist/esm/modules/pdf/types.js +4 -0
  457. package/dist/esm/modules/pdf/word-bridge.js +304 -0
  458. package/dist/esm/modules/word/constants.js +231 -0
  459. package/dist/esm/modules/word/content-types.js +53 -0
  460. package/dist/esm/modules/word/digital-signatures.js +134 -0
  461. package/dist/esm/modules/word/document.js +1795 -0
  462. package/dist/esm/modules/word/docx-packager.js +822 -0
  463. package/dist/esm/modules/word/docx-reader.js +4929 -0
  464. package/dist/esm/modules/word/encryption.js +274 -0
  465. package/dist/esm/modules/word/errors.js +68 -0
  466. package/dist/esm/modules/word/font-obfuscation.js +83 -0
  467. package/dist/esm/modules/word/html-renderer.js +782 -0
  468. package/dist/esm/modules/word/index.base.js +51 -0
  469. package/dist/esm/modules/word/index.browser.js +4 -0
  470. package/dist/esm/modules/word/index.js +4 -0
  471. package/dist/esm/modules/word/internal-utils.js +54 -0
  472. package/dist/esm/modules/word/relationships.js +56 -0
  473. package/dist/esm/modules/word/types.js +10 -0
  474. package/dist/esm/modules/word/units.js +111 -0
  475. package/dist/esm/modules/word/writers/chart-writer.js +385 -0
  476. package/dist/esm/modules/word/writers/checkbox-writer.js +42 -0
  477. package/dist/esm/modules/word/writers/comment-writer.js +70 -0
  478. package/dist/esm/modules/word/writers/document-writer.js +461 -0
  479. package/dist/esm/modules/word/writers/footnote-writer.js +72 -0
  480. package/dist/esm/modules/word/writers/header-footer-writer.js +129 -0
  481. package/dist/esm/modules/word/writers/image-writer.js +185 -0
  482. package/dist/esm/modules/word/writers/math-writer.js +428 -0
  483. package/dist/esm/modules/word/writers/numbering-writer.js +125 -0
  484. package/dist/esm/modules/word/writers/paragraph-writer.js +516 -0
  485. package/dist/esm/modules/word/writers/parts-writer.js +660 -0
  486. package/dist/esm/modules/word/writers/run-writer.js +649 -0
  487. package/dist/esm/modules/word/writers/section-writer.js +238 -0
  488. package/dist/esm/modules/word/writers/styles-writer.js +242 -0
  489. package/dist/esm/modules/word/writers/table-writer.js +503 -0
  490. package/dist/esm/modules/word/writers/textbox-writer.js +53 -0
  491. package/dist/esm/modules/word/writers/toc-writer.js +79 -0
  492. package/dist/esm/modules/xml/encode.js +157 -11
  493. package/dist/iife/excelts.iife.js +11789 -687
  494. package/dist/iife/excelts.iife.js.map +1 -1
  495. package/dist/iife/excelts.iife.min.js +52 -44
  496. package/dist/types/index.browser.d.ts +8 -5
  497. package/dist/types/index.d.ts +4 -2
  498. package/dist/types/modules/excel/chart/cache-populator.d.ts +49 -0
  499. package/dist/types/modules/excel/chart/chart-api.d.ts +92 -0
  500. package/dist/types/modules/excel/chart/chart-builder.d.ts +48 -0
  501. package/dist/types/modules/excel/chart/chart-ex-builder.d.ts +36 -0
  502. package/dist/types/modules/excel/chart/chart-ex-parser.d.ts +8 -0
  503. package/dist/types/modules/excel/chart/chart-ex-renderer.d.ts +187 -0
  504. package/dist/types/modules/excel/chart/chart-ex-types.d.ts +531 -0
  505. package/dist/types/modules/excel/chart/chart-images.d.ts +78 -0
  506. package/dist/types/modules/excel/chart/chart-presets.d.ts +392 -0
  507. package/dist/types/modules/excel/chart/chart-renderer.d.ts +550 -0
  508. package/dist/types/modules/excel/chart/chart-sidecar.d.ts +21 -0
  509. package/dist/types/modules/excel/chart/chart-utils.d.ts +306 -0
  510. package/dist/types/modules/excel/chart/chart.d.ts +504 -0
  511. package/dist/types/modules/excel/chart/glyph-rasterizer.d.ts +62 -0
  512. package/dist/types/modules/excel/chart/index.d.ts +54 -0
  513. package/dist/types/modules/excel/chart/install.d.ts +44 -0
  514. package/dist/types/modules/excel/chart/shape-properties.d.ts +156 -0
  515. package/dist/types/modules/excel/chart/stroke-font.d.ts +36 -0
  516. package/dist/types/modules/excel/chart/topojson.d.ts +98 -0
  517. package/dist/types/modules/excel/chart/types.d.ts +2559 -0
  518. package/dist/types/modules/excel/chart-host-registry.d.ts +157 -0
  519. package/dist/types/modules/excel/chartsheet.d.ts +102 -0
  520. package/dist/types/modules/excel/defined-names.d.ts +35 -0
  521. package/dist/types/modules/excel/errors.d.ts +6 -0
  522. package/dist/types/modules/excel/form-control.d.ts +6 -0
  523. package/dist/types/modules/excel/pivot-chart.d.ts +7 -0
  524. package/dist/types/modules/excel/pivot-table.d.ts +55 -0
  525. package/dist/types/modules/excel/sparkline/index.d.ts +7 -0
  526. package/dist/types/modules/excel/sparkline/sparkline.d.ts +206 -0
  527. package/dist/types/modules/excel/types.d.ts +72 -0
  528. package/dist/types/modules/excel/utils/address.d.ts +18 -0
  529. package/dist/types/modules/excel/utils/guid.d.ts +15 -0
  530. package/dist/types/modules/excel/utils/ooxml-paths.d.ts +74 -0
  531. package/dist/types/modules/excel/utils/ooxml-validator/check-chart-sidecar.d.ts +35 -0
  532. package/dist/types/modules/excel/utils/ooxml-validator/check-chart.d.ts +32 -0
  533. package/dist/types/modules/excel/utils/ooxml-validator/check-chartsheet.d.ts +9 -0
  534. package/dist/types/modules/excel/utils/ooxml-validator/check-content-types.d.ts +16 -0
  535. package/dist/types/modules/excel/utils/ooxml-validator/check-drawing.d.ts +34 -0
  536. package/dist/types/modules/excel/utils/ooxml-validator/check-pivot.d.ts +14 -0
  537. package/dist/types/modules/excel/utils/ooxml-validator/check-relationships.d.ts +18 -0
  538. package/dist/types/modules/excel/utils/ooxml-validator/check-structure.d.ts +21 -0
  539. package/dist/types/modules/excel/utils/ooxml-validator/check-styles.d.ts +15 -0
  540. package/dist/types/modules/excel/utils/ooxml-validator/check-table.d.ts +31 -0
  541. package/dist/types/modules/excel/utils/ooxml-validator/check-workbook.d.ts +19 -0
  542. package/dist/types/modules/excel/utils/ooxml-validator/check-worksheet.d.ts +25 -0
  543. package/dist/types/modules/excel/utils/ooxml-validator/context.d.ts +85 -0
  544. package/dist/types/modules/excel/utils/ooxml-validator/index.d.ts +31 -0
  545. package/dist/types/modules/excel/utils/ooxml-validator/path-utils.d.ts +67 -0
  546. package/dist/types/modules/excel/utils/ooxml-validator/reporter.d.ts +41 -0
  547. package/dist/types/modules/excel/utils/ooxml-validator/types.d.ts +109 -0
  548. package/dist/types/modules/excel/utils/ooxml-validator/xml-utils.d.ts +38 -0
  549. package/dist/types/modules/excel/workbook.browser.d.ts +248 -30
  550. package/dist/types/modules/excel/workbook.d.ts +43 -0
  551. package/dist/types/modules/excel/worksheet.d.ts +157 -3
  552. package/dist/types/modules/excel/xlsx/rel-type.d.ts +40 -0
  553. package/dist/types/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +1 -0
  554. package/dist/types/modules/excel/xlsx/xform/chart/chart-space-xform.d.ts +353 -0
  555. package/dist/types/modules/excel/xlsx/xform/comment/threaded-comments-xform.d.ts +60 -0
  556. package/dist/types/modules/excel/xlsx/xform/drawing/drawing-xform.d.ts +30 -0
  557. package/dist/types/modules/excel/xlsx/xform/drawing/graphic-frame-xform.d.ts +54 -0
  558. package/dist/types/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +3 -1
  559. package/dist/types/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.d.ts +46 -0
  560. package/dist/types/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +13 -2
  561. package/dist/types/modules/excel/xlsx/xform/sheet/chartsheet-xform.d.ts +185 -0
  562. package/dist/types/modules/excel/xlsx/xform/sheet/ext-lst-xform.d.ts +1 -0
  563. package/dist/types/modules/excel/xlsx/xform/xsd-values.d.ts +63 -0
  564. package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +115 -21
  565. package/dist/types/modules/pdf/builder/document-builder.d.ts +74 -0
  566. package/dist/types/modules/pdf/excel-bridge.d.ts +69 -0
  567. package/dist/types/modules/pdf/font/font-manager.d.ts +25 -0
  568. package/dist/types/modules/pdf/index.d.ts +5 -2
  569. package/dist/types/modules/pdf/render/chart-surface.d.ts +33 -0
  570. package/dist/types/modules/pdf/render/layout-engine.d.ts +22 -1
  571. package/dist/types/modules/pdf/types.d.ts +227 -23
  572. package/dist/types/modules/pdf/word-bridge.d.ts +47 -0
  573. package/dist/types/modules/word/constants.d.ts +179 -0
  574. package/dist/types/modules/word/content-types.d.ts +27 -0
  575. package/dist/types/modules/word/digital-signatures.d.ts +87 -0
  576. package/dist/types/modules/word/document.d.ts +728 -0
  577. package/dist/types/modules/word/docx-packager.d.ts +14 -0
  578. package/dist/types/modules/word/docx-reader.d.ts +11 -0
  579. package/dist/types/modules/word/encryption.d.ts +102 -0
  580. package/dist/types/modules/word/errors.d.ts +49 -0
  581. package/dist/types/modules/word/font-obfuscation.d.ts +31 -0
  582. package/dist/types/modules/word/html-renderer.d.ts +38 -0
  583. package/dist/types/modules/word/index.base.d.ts +19 -0
  584. package/dist/types/modules/word/index.browser.d.ts +4 -0
  585. package/dist/types/modules/word/index.d.ts +4 -0
  586. package/dist/types/modules/word/internal-utils.d.ts +23 -0
  587. package/dist/types/modules/word/relationships.d.ts +31 -0
  588. package/dist/types/modules/word/types.d.ts +2325 -0
  589. package/dist/types/modules/word/units.d.ts +49 -0
  590. package/dist/types/modules/word/writers/chart-writer.d.ts +10 -0
  591. package/dist/types/modules/word/writers/checkbox-writer.d.ts +9 -0
  592. package/dist/types/modules/word/writers/comment-writer.d.ts +15 -0
  593. package/dist/types/modules/word/writers/document-writer.d.ts +16 -0
  594. package/dist/types/modules/word/writers/footnote-writer.d.ts +11 -0
  595. package/dist/types/modules/word/writers/header-footer-writer.d.ts +13 -0
  596. package/dist/types/modules/word/writers/image-writer.d.ts +10 -0
  597. package/dist/types/modules/word/writers/math-writer.d.ts +9 -0
  598. package/dist/types/modules/word/writers/numbering-writer.d.ts +10 -0
  599. package/dist/types/modules/word/writers/paragraph-writer.d.ts +13 -0
  600. package/dist/types/modules/word/writers/parts-writer.d.ts +26 -0
  601. package/dist/types/modules/word/writers/run-writer.d.ts +15 -0
  602. package/dist/types/modules/word/writers/section-writer.d.ts +10 -0
  603. package/dist/types/modules/word/writers/styles-writer.d.ts +10 -0
  604. package/dist/types/modules/word/writers/table-writer.d.ts +10 -0
  605. package/dist/types/modules/word/writers/textbox-writer.d.ts +9 -0
  606. package/dist/types/modules/word/writers/toc-writer.d.ts +9 -0
  607. package/dist/types/modules/xml/encode.d.ts +56 -7
  608. package/package.json +29 -11
  609. package/dist/browser/modules/excel/utils/ooxml-validator.d.ts +0 -48
  610. package/dist/browser/modules/excel/utils/ooxml-validator.js +0 -493
  611. package/dist/browser/modules/excel/utils/passthrough-manager.d.ts +0 -77
  612. package/dist/browser/modules/excel/utils/passthrough-manager.js +0 -129
  613. package/dist/cjs/modules/excel/utils/ooxml-validator.js +0 -499
  614. package/dist/cjs/modules/excel/utils/passthrough-manager.js +0 -133
  615. package/dist/esm/modules/excel/utils/ooxml-validator.js +0 -493
  616. package/dist/esm/modules/excel/utils/passthrough-manager.js +0 -129
  617. package/dist/types/modules/excel/utils/ooxml-validator.d.ts +0 -48
  618. package/dist/types/modules/excel/utils/passthrough-manager.d.ts +0 -77
@@ -0,0 +1,4929 @@
1
+ /**
2
+ * DOCX Module - Reader / Parser
3
+ *
4
+ * Reads a DOCX ZIP file and parses it into a DocxDocument model.
5
+ * Uses the archive module for ZIP reading and XML module for parsing.
6
+ */
7
+ import { unzip } from "../archive/read-archive.js";
8
+ import { parseXml, findChild, findChildren, textContent } from "../xml/dom.js";
9
+ import { RelType } from "./constants.js";
10
+ import { DocxError, DocxParseError, DocxMissingPartError } from "./errors.js";
11
+ // Module-level parsing context: set at the start of readDocx, used by parseParagraph etc.
12
+ let _parseRelMap = new Map();
13
+ // =============================================================================
14
+ // Helper Functions
15
+ // =============================================================================
16
+ function attrVal(el, name) {
17
+ // Try with w: prefix and without
18
+ return el.attributes[`w:${name}`] ?? el.attributes[name];
19
+ }
20
+ function attrInt(el, name) {
21
+ const v = attrVal(el, name);
22
+ if (v === undefined) {
23
+ return undefined;
24
+ }
25
+ const n = parseInt(v, 10);
26
+ return Number.isFinite(n) ? n : undefined;
27
+ }
28
+ /**
29
+ * Read an attribute as a strict boolean:
30
+ * "1"|"true" → true
31
+ * "0"|"false" → false
32
+ * otherwise → undefined
33
+ * Useful for attributes where default-false vs explicit-false matters.
34
+ */
35
+ function _attrBool(el, name) {
36
+ const v = attrVal(el, name);
37
+ if (v === undefined) {
38
+ return undefined;
39
+ }
40
+ if (v === "1" || v === "true") {
41
+ return true;
42
+ }
43
+ if (v === "0" || v === "false") {
44
+ return false;
45
+ }
46
+ return undefined;
47
+ }
48
+ function findChildNs(el, localName) {
49
+ // Match either w:localName or just localName
50
+ return findChild(el, `w:${localName}`) ?? findChild(el, localName);
51
+ }
52
+ function findChildrenNs(el, localName) {
53
+ const a = findChildren(el, `w:${localName}`);
54
+ return a.length > 0 ? a : findChildren(el, localName);
55
+ }
56
+ /** Check for a boolean toggle element (present = true, w:val="0" or "false" = false). */
57
+ function boolToggle(parent, name) {
58
+ const el = findChildNs(parent, name);
59
+ if (!el) {
60
+ return undefined;
61
+ }
62
+ const v = attrVal(el, "val");
63
+ if (v === "0" || v === "false") {
64
+ return false;
65
+ }
66
+ return true;
67
+ }
68
+ /** Escape special XML characters in text content. */
69
+ function escapeXml(s) {
70
+ return s
71
+ .replace(/&/g, "&")
72
+ .replace(/</g, "&lt;")
73
+ .replace(/>/g, "&gt;")
74
+ .replace(/"/g, "&quot;");
75
+ }
76
+ /** Serialize an XmlElement back to an XML string (for opaque preservation). */
77
+ function serializeElement(el) {
78
+ let s = `<${el.name}`;
79
+ for (const [k, v] of Object.entries(el.attributes)) {
80
+ s += ` ${k}="${escapeXml(v)}"`;
81
+ }
82
+ if (el.children.length === 0) {
83
+ return s + "/>";
84
+ }
85
+ s += ">";
86
+ for (const child of el.children) {
87
+ if (child.type === "element") {
88
+ s += serializeElement(child);
89
+ }
90
+ else if (child.type === "text") {
91
+ s += escapeXml(child.value);
92
+ }
93
+ }
94
+ s += `</${el.name}>`;
95
+ return s;
96
+ }
97
+ /** Extract all r:xxx attribute values (relationship IDs) from an element tree. */
98
+ function collectRIds(el, out) {
99
+ for (const [k, v] of Object.entries(el.attributes)) {
100
+ if (k.startsWith("r:") || k === "r:id" || k === "r:embed" || k === "r:link") {
101
+ out.add(v);
102
+ }
103
+ }
104
+ for (const child of el.children) {
105
+ if (child.type === "element") {
106
+ collectRIds(child, out);
107
+ }
108
+ }
109
+ }
110
+ /** Get the .rels path for a given part path. */
111
+ function getPartRelsPath(partPath) {
112
+ const lastSlash = partPath.lastIndexOf("/");
113
+ const dir = lastSlash >= 0 ? partPath.substring(0, lastSlash) : "";
114
+ const name = lastSlash >= 0 ? partPath.substring(lastSlash + 1) : partPath;
115
+ return dir ? `${dir}/_rels/${name}.rels` : `_rels/${name}.rels`;
116
+ }
117
+ /**
118
+ * Resolve a relationship target path to an absolute package-root path.
119
+ *
120
+ * - Leading "/" → package root absolute
121
+ * - "../" / "./" → resolved relative to the source part's directory
122
+ * - Plain paths → resolved relative to the source part's directory
123
+ */
124
+ function resolvePartPath(sourcePart, target) {
125
+ if (!target) {
126
+ return "";
127
+ }
128
+ if (target.startsWith("/")) {
129
+ return target.slice(1);
130
+ }
131
+ const lastSlash = sourcePart.lastIndexOf("/");
132
+ const baseDir = lastSlash >= 0 ? sourcePart.substring(0, lastSlash).split("/") : [];
133
+ const segs = target.split("/");
134
+ for (const seg of segs) {
135
+ if (seg === "..") {
136
+ baseDir.pop();
137
+ }
138
+ else if (seg !== "." && seg !== "") {
139
+ baseDir.push(seg);
140
+ }
141
+ }
142
+ return baseDir.join("/");
143
+ }
144
+ /** Parse footnote/endnote properties element. */
145
+ function parseNoteProperties(el) {
146
+ const props = {};
147
+ const numFmtEl = findChildNs(el, "numFmt");
148
+ if (numFmtEl) {
149
+ props.numFmt = attrVal(numFmtEl, "val");
150
+ }
151
+ const numStartEl = findChildNs(el, "numStart");
152
+ if (numStartEl) {
153
+ props.numStart = attrInt(numStartEl, "val");
154
+ }
155
+ const numRestartEl = findChildNs(el, "numRestart");
156
+ if (numRestartEl) {
157
+ props.numRestart = attrVal(numRestartEl, "val");
158
+ }
159
+ const posEl = findChildNs(el, "pos");
160
+ if (posEl) {
161
+ props.position = attrVal(posEl, "val");
162
+ }
163
+ return Object.keys(props).length > 0 ? props : undefined;
164
+ }
165
+ /** Parse w:ffData element into a FormField. */
166
+ function parseFfData(el) {
167
+ const nameEl = findChildNs(el, "name");
168
+ const name = nameEl ? attrVal(nameEl, "val") : undefined;
169
+ const enabledEl = findChildNs(el, "enabled");
170
+ const enabled = enabledEl ? attrVal(enabledEl, "val") !== "0" : undefined;
171
+ const helpTextEl = findChildNs(el, "helpText");
172
+ const helpText = helpTextEl ? attrVal(helpTextEl, "val") : undefined;
173
+ const statusTextEl = findChildNs(el, "statusText");
174
+ const statusText = statusTextEl ? attrVal(statusTextEl, "val") : undefined;
175
+ // Text input
176
+ const textInputEl = findChildNs(el, "textInput");
177
+ if (textInputEl) {
178
+ const defEl = findChildNs(textInputEl, "default");
179
+ const maxLenEl = findChildNs(textInputEl, "maxLength");
180
+ const fmtEl = findChildNs(textInputEl, "format");
181
+ return {
182
+ type: "text",
183
+ name,
184
+ default: defEl ? attrVal(defEl, "val") : undefined,
185
+ maxLength: maxLenEl ? attrInt(maxLenEl, "val") : undefined,
186
+ format: fmtEl ? attrVal(fmtEl, "val") : undefined,
187
+ helpText,
188
+ statusText,
189
+ enabled
190
+ };
191
+ }
192
+ // CheckBox
193
+ const cbEl = findChildNs(el, "checkBox");
194
+ if (cbEl) {
195
+ const checkedEl = findChildNs(cbEl, "checked");
196
+ const defEl = findChildNs(cbEl, "default");
197
+ const sizeEl = findChildNs(cbEl, "size");
198
+ return {
199
+ type: "checkBox",
200
+ name,
201
+ checked: checkedEl ? attrVal(checkedEl, "val") !== "0" : undefined,
202
+ default: defEl ? attrVal(defEl, "val") !== "0" : undefined,
203
+ size: sizeEl ? attrInt(sizeEl, "val") : undefined
204
+ };
205
+ }
206
+ // Drop-down list
207
+ const ddlEl = findChildNs(el, "ddList");
208
+ if (ddlEl) {
209
+ const defEl = findChildNs(ddlEl, "default");
210
+ const entries = [];
211
+ for (const le of findChildrenNs(ddlEl, "listEntry")) {
212
+ const v = attrVal(le, "val");
213
+ if (v !== undefined) {
214
+ entries.push(v);
215
+ }
216
+ }
217
+ return {
218
+ type: "dropDown",
219
+ name,
220
+ entries: entries.length > 0 ? entries : undefined,
221
+ default: defEl ? attrInt(defEl, "val") : undefined,
222
+ helpText,
223
+ statusText,
224
+ enabled
225
+ };
226
+ }
227
+ return undefined;
228
+ }
229
+ // =============================================================================
230
+ // Run Properties Parser
231
+ // =============================================================================
232
+ function parseRunProperties(rPrEl) {
233
+ const rPr = {};
234
+ const rStyleEl = findChildNs(rPrEl, "rStyle");
235
+ if (rStyleEl) {
236
+ rPr.style = attrVal(rStyleEl, "val");
237
+ }
238
+ const fontsEl = findChildNs(rPrEl, "rFonts");
239
+ if (fontsEl) {
240
+ const f = {};
241
+ const ascii = attrVal(fontsEl, "ascii");
242
+ const hAnsi = attrVal(fontsEl, "hAnsi");
243
+ const eastAsia = attrVal(fontsEl, "eastAsia");
244
+ const cs = attrVal(fontsEl, "cs");
245
+ const hint = attrVal(fontsEl, "hint");
246
+ if (ascii) {
247
+ f.ascii = ascii;
248
+ }
249
+ if (hAnsi) {
250
+ f.hAnsi = hAnsi;
251
+ }
252
+ if (eastAsia) {
253
+ f.eastAsia = eastAsia;
254
+ }
255
+ if (cs) {
256
+ f.cs = cs;
257
+ }
258
+ if (hint) {
259
+ f.hint = hint;
260
+ }
261
+ const asciiTheme = attrVal(fontsEl, "asciiTheme");
262
+ if (asciiTheme) {
263
+ f.asciiTheme = asciiTheme;
264
+ }
265
+ const hAnsiTheme = attrVal(fontsEl, "hAnsiTheme");
266
+ if (hAnsiTheme) {
267
+ f.hAnsiTheme = hAnsiTheme;
268
+ }
269
+ const eastAsiaTheme = attrVal(fontsEl, "eastAsiaTheme");
270
+ if (eastAsiaTheme) {
271
+ f.eastAsiaTheme = eastAsiaTheme;
272
+ }
273
+ const cstheme = attrVal(fontsEl, "cstheme");
274
+ if (cstheme) {
275
+ f.cstheme = cstheme;
276
+ }
277
+ rPr.font = f;
278
+ }
279
+ // Boolean toggles: true-only (element presence = true; absence = undefined)
280
+ // Format: [elementName, propertyKey]
281
+ const RUN_ONCE_TOGGLES = [
282
+ ["strike", "strike"],
283
+ ["dstrike", "doubleStrike"],
284
+ ["caps", "caps"],
285
+ ["smallCaps", "smallCaps"],
286
+ ["vanish", "vanish"],
287
+ ["emboss", "emboss"],
288
+ ["imprint", "imprint"],
289
+ ["noProof", "noProof"],
290
+ ["specVanish", "specVanish"],
291
+ ["outline", "outline"],
292
+ ["shadow", "shadow"],
293
+ ["cs", "complexScript"],
294
+ ["oMath", "math"],
295
+ ["webHidden", "webHidden"]
296
+ ];
297
+ for (const [tag, key] of RUN_ONCE_TOGGLES) {
298
+ if (findChildNs(rPrEl, tag) && boolToggle(rPrEl, tag) !== false) {
299
+ rPr[key] = true;
300
+ }
301
+ }
302
+ // Boolean toggles: tri-state (can be explicitly true or false)
303
+ const RUN_TRISTATE_TOGGLES = [
304
+ ["b", "bold"],
305
+ ["bCs", "boldCs"],
306
+ ["i", "italic"],
307
+ ["iCs", "italicCs"],
308
+ ["snapToGrid", "snapToGrid"],
309
+ ["rtl", "rightToLeft"]
310
+ ];
311
+ for (const [tag, key] of RUN_TRISTATE_TOGGLES) {
312
+ const v = boolToggle(rPrEl, tag);
313
+ if (v !== undefined) {
314
+ rPr[key] = v;
315
+ }
316
+ }
317
+ // fitText
318
+ const fitTextEl = findChildNs(rPrEl, "fitText");
319
+ if (fitTextEl) {
320
+ const val = attrInt(fitTextEl, "val");
321
+ if (val !== undefined) {
322
+ const fitText = { val };
323
+ const id = attrInt(fitTextEl, "id");
324
+ if (id !== undefined) {
325
+ fitText.id = id;
326
+ }
327
+ rPr.fitText = fitText;
328
+ }
329
+ }
330
+ const colorEl = findChildNs(rPrEl, "color");
331
+ if (colorEl) {
332
+ const val = attrVal(colorEl, "val");
333
+ const themeColor = attrVal(colorEl, "themeColor");
334
+ if (themeColor) {
335
+ const spec = { val, themeColor };
336
+ const themeTint = attrVal(colorEl, "themeTint");
337
+ const themeShade = attrVal(colorEl, "themeShade");
338
+ if (themeTint) {
339
+ spec.themeTint = themeTint;
340
+ }
341
+ if (themeShade) {
342
+ spec.themeShade = themeShade;
343
+ }
344
+ rPr.color = spec;
345
+ }
346
+ else {
347
+ rPr.color = val;
348
+ }
349
+ }
350
+ const szEl = findChildNs(rPrEl, "sz");
351
+ if (szEl) {
352
+ rPr.size = attrInt(szEl, "val");
353
+ }
354
+ const szCsEl = findChildNs(rPrEl, "szCs");
355
+ if (szCsEl) {
356
+ rPr.sizeCs = attrInt(szCsEl, "val");
357
+ }
358
+ const uEl = findChildNs(rPrEl, "u");
359
+ if (uEl) {
360
+ const uStyle = attrVal(uEl, "val") ?? "single";
361
+ const uColor = attrVal(uEl, "color");
362
+ if (uColor) {
363
+ rPr.underline = { style: uStyle, color: uColor };
364
+ }
365
+ else {
366
+ rPr.underline = uStyle;
367
+ }
368
+ }
369
+ const highlightEl = findChildNs(rPrEl, "highlight");
370
+ if (highlightEl) {
371
+ rPr.highlight = attrVal(highlightEl, "val");
372
+ }
373
+ const vertAlignEl = findChildNs(rPrEl, "vertAlign");
374
+ if (vertAlignEl) {
375
+ rPr.vertAlign = attrVal(vertAlignEl, "val");
376
+ }
377
+ const spacingEl = findChildNs(rPrEl, "spacing");
378
+ if (spacingEl) {
379
+ rPr.spacing = attrInt(spacingEl, "val");
380
+ }
381
+ const shdEl = findChildNs(rPrEl, "shd");
382
+ if (shdEl) {
383
+ rPr.shading = parseShading(shdEl);
384
+ }
385
+ const langEl = findChildNs(rPrEl, "lang");
386
+ if (langEl) {
387
+ rPr.language = {
388
+ val: attrVal(langEl, "val"),
389
+ eastAsia: attrVal(langEl, "eastAsia"),
390
+ bidi: attrVal(langEl, "bidi")
391
+ };
392
+ }
393
+ // New valued properties
394
+ const kernEl = findChildNs(rPrEl, "kern");
395
+ if (kernEl) {
396
+ rPr.kern = attrInt(kernEl, "val");
397
+ }
398
+ const positionEl = findChildNs(rPrEl, "position");
399
+ if (positionEl) {
400
+ rPr.position = attrInt(positionEl, "val");
401
+ }
402
+ const wEl = findChildNs(rPrEl, "w");
403
+ if (wEl) {
404
+ rPr.scale = attrInt(wEl, "val");
405
+ }
406
+ const effectEl = findChildNs(rPrEl, "effect");
407
+ if (effectEl) {
408
+ rPr.effect = attrVal(effectEl, "val");
409
+ }
410
+ const emEl = findChildNs(rPrEl, "em");
411
+ if (emEl) {
412
+ rPr.emphasisMark = attrVal(emEl, "val");
413
+ }
414
+ const bdrEl = findChildNs(rPrEl, "bdr");
415
+ if (bdrEl) {
416
+ rPr.border = parseBorder(bdrEl);
417
+ }
418
+ // rPrChange (track changes for run properties)
419
+ const rPrChangeEl = findChildNs(rPrEl, "rPrChange");
420
+ if (rPrChangeEl) {
421
+ const rev = parseRevisionInfo(rPrChangeEl);
422
+ if (rev) {
423
+ const prevRPrEl = findChildNs(rPrChangeEl, "rPr");
424
+ const change = {
425
+ revision: rev,
426
+ previousProperties: prevRPrEl ? parseRunProperties(prevRPrEl) : undefined
427
+ };
428
+ rPr.propertyChange = change;
429
+ }
430
+ }
431
+ return rPr;
432
+ }
433
+ function parseShading(el) {
434
+ return {
435
+ pattern: attrVal(el, "val"),
436
+ color: attrVal(el, "color"),
437
+ fill: attrVal(el, "fill") ?? "auto"
438
+ };
439
+ }
440
+ function parseBorder(el) {
441
+ const b = {
442
+ style: (attrVal(el, "val") ?? "single"),
443
+ size: attrInt(el, "sz"),
444
+ space: attrInt(el, "space"),
445
+ color: attrVal(el, "color")
446
+ };
447
+ const tc = attrVal(el, "themeColor");
448
+ if (tc) {
449
+ b.themeColor = tc;
450
+ }
451
+ const shadow = attrVal(el, "shadow");
452
+ if (shadow === "1" || shadow === "true") {
453
+ b.shadow = true;
454
+ }
455
+ const frame = attrVal(el, "frame");
456
+ if (frame === "1" || frame === "true") {
457
+ b.frame = true;
458
+ }
459
+ const art = attrVal(el, "art");
460
+ if (art) {
461
+ b.art = art;
462
+ }
463
+ return b;
464
+ }
465
+ function parseTableWidth(el) {
466
+ return {
467
+ value: parseInt(el.attributes["w:w"] ?? el.attributes["w"] ?? "0", 10),
468
+ type: (el.attributes["w:type"] ?? el.attributes["type"] ?? "dxa")
469
+ };
470
+ }
471
+ // =============================================================================
472
+ // Paragraph Properties Parser
473
+ // =============================================================================
474
+ function parseParagraphProperties(pPrEl) {
475
+ const pPr = {};
476
+ const pStyleEl = findChildNs(pPrEl, "pStyle");
477
+ if (pStyleEl) {
478
+ pPr.style = attrVal(pStyleEl, "val");
479
+ }
480
+ const jcEl = findChildNs(pPrEl, "jc");
481
+ if (jcEl) {
482
+ pPr.alignment = attrVal(jcEl, "val");
483
+ }
484
+ if (findChildNs(pPrEl, "keepNext")) {
485
+ pPr.keepNext = true;
486
+ }
487
+ if (findChildNs(pPrEl, "keepLines")) {
488
+ pPr.keepLines = true;
489
+ }
490
+ if (findChildNs(pPrEl, "pageBreakBefore")) {
491
+ pPr.pageBreakBefore = true;
492
+ }
493
+ if (findChildNs(pPrEl, "bidi")) {
494
+ pPr.bidi = true;
495
+ }
496
+ // New boolean toggles
497
+ const ctxSp = boolToggle(pPrEl, "contextualSpacing");
498
+ if (ctxSp !== undefined) {
499
+ pPr.contextualSpacing = ctxSp;
500
+ }
501
+ const suppLn = boolToggle(pPrEl, "suppressLineNumbers");
502
+ if (suppLn !== undefined) {
503
+ pPr.suppressLineNumbers = suppLn;
504
+ }
505
+ const suppHyph = boolToggle(pPrEl, "suppressAutoHyphens");
506
+ if (suppHyph !== undefined) {
507
+ pPr.suppressAutoHyphens = suppHyph;
508
+ }
509
+ const mirr = boolToggle(pPrEl, "mirrorIndents");
510
+ if (mirr !== undefined) {
511
+ pPr.mirrorIndents = mirr;
512
+ }
513
+ const wc = boolToggle(pPrEl, "widowControl");
514
+ if (wc !== undefined) {
515
+ pPr.widowControl = wc;
516
+ }
517
+ const ww = boolToggle(pPrEl, "wordWrap");
518
+ if (ww !== undefined) {
519
+ pPr.wordWrap = ww;
520
+ }
521
+ const stg = boolToggle(pPrEl, "snapToGrid");
522
+ if (stg !== undefined) {
523
+ pPr.snapToGrid = stg;
524
+ }
525
+ const ofp = boolToggle(pPrEl, "overflowPunct");
526
+ if (ofp !== undefined) {
527
+ pPr.overflowPunctuation = ofp;
528
+ }
529
+ const topLinePunct = boolToggle(pPrEl, "topLinePunct");
530
+ if (topLinePunct !== undefined) {
531
+ pPr.topLinePunctuation = topLinePunct;
532
+ }
533
+ const kinsoku = boolToggle(pPrEl, "kinsoku");
534
+ if (kinsoku !== undefined) {
535
+ pPr.kinsoku = kinsoku;
536
+ }
537
+ const asd = boolToggle(pPrEl, "autoSpaceDE");
538
+ if (asd !== undefined) {
539
+ pPr.autoSpaceEastAsianText = asd;
540
+ }
541
+ const asdn = boolToggle(pPrEl, "autoSpaceDN");
542
+ if (asdn !== undefined) {
543
+ pPr.autoSpaceEastAsianDigit = asdn;
544
+ }
545
+ const textAlignEl = findChildNs(pPrEl, "textAlignment");
546
+ if (textAlignEl) {
547
+ pPr.textAlignment = attrVal(textAlignEl, "val");
548
+ }
549
+ const outlineLvlEl = findChildNs(pPrEl, "outlineLvl");
550
+ if (outlineLvlEl) {
551
+ pPr.outlineLevel = attrInt(outlineLvlEl, "val");
552
+ }
553
+ const textDirEl = findChildNs(pPrEl, "textDirection");
554
+ if (textDirEl) {
555
+ pPr.textDirection = attrVal(textDirEl, "val");
556
+ }
557
+ // Paragraph frame
558
+ const framePrEl = findChildNs(pPrEl, "framePr");
559
+ if (framePrEl) {
560
+ const frame = {};
561
+ const f = frame;
562
+ const dropCap = attrVal(framePrEl, "dropCap");
563
+ if (dropCap) {
564
+ f.dropCap = dropCap;
565
+ }
566
+ const lines = attrInt(framePrEl, "lines");
567
+ if (lines !== undefined) {
568
+ f.lines = lines;
569
+ }
570
+ const fw = attrInt(framePrEl, "w");
571
+ if (fw !== undefined) {
572
+ f.width = fw;
573
+ }
574
+ const fh = attrInt(framePrEl, "h");
575
+ if (fh !== undefined) {
576
+ f.height = fh;
577
+ }
578
+ const hSpace = attrInt(framePrEl, "hSpace");
579
+ if (hSpace !== undefined) {
580
+ f.hSpace = hSpace;
581
+ }
582
+ const vSpace = attrInt(framePrEl, "vSpace");
583
+ if (vSpace !== undefined) {
584
+ f.vSpace = vSpace;
585
+ }
586
+ const wrap = attrVal(framePrEl, "wrap");
587
+ if (wrap) {
588
+ f.wrap = wrap;
589
+ }
590
+ const hAnchor = attrVal(framePrEl, "hAnchor");
591
+ if (hAnchor) {
592
+ f.hAnchor = hAnchor;
593
+ }
594
+ const vAnchor = attrVal(framePrEl, "vAnchor");
595
+ if (vAnchor) {
596
+ f.vAnchor = vAnchor;
597
+ }
598
+ const x = attrInt(framePrEl, "x");
599
+ if (x !== undefined) {
600
+ f.x = x;
601
+ }
602
+ const xAlign = attrVal(framePrEl, "xAlign");
603
+ if (xAlign) {
604
+ f.xAlign = xAlign;
605
+ }
606
+ const y = attrInt(framePrEl, "y");
607
+ if (y !== undefined) {
608
+ f.y = y;
609
+ }
610
+ const yAlign = attrVal(framePrEl, "yAlign");
611
+ if (yAlign) {
612
+ f.yAlign = yAlign;
613
+ }
614
+ pPr.frame = frame;
615
+ }
616
+ // Thematic break: check for bottom border with special pattern
617
+ const pBdrEl = findChildNs(pPrEl, "pBdr");
618
+ if (pBdrEl) {
619
+ const borders = {};
620
+ for (const side of ["top", "bottom", "left", "right", "between", "bar"]) {
621
+ const sideEl = findChildNs(pBdrEl, side);
622
+ if (sideEl) {
623
+ borders[side] = parseBorder(sideEl);
624
+ }
625
+ }
626
+ pPr.borders = borders;
627
+ }
628
+ const spacingEl = findChildNs(pPrEl, "spacing");
629
+ if (spacingEl) {
630
+ const spacing = {};
631
+ const before = attrInt(spacingEl, "before");
632
+ const after = attrInt(spacingEl, "after");
633
+ const line = attrInt(spacingEl, "line");
634
+ const lineRule = attrVal(spacingEl, "lineRule");
635
+ if (before !== undefined) {
636
+ spacing.before = before;
637
+ }
638
+ if (after !== undefined) {
639
+ spacing.after = after;
640
+ }
641
+ if (line !== undefined) {
642
+ spacing.line = line;
643
+ // Per ECMA-376, w:lineRule defaults to "auto" when line is set
644
+ spacing.lineRule = (lineRule ?? "auto");
645
+ }
646
+ else if (lineRule) {
647
+ spacing.lineRule = lineRule;
648
+ }
649
+ const beforeAuto = attrVal(spacingEl, "beforeAutospacing");
650
+ if (beforeAuto === "1" || beforeAuto === "true") {
651
+ spacing.beforeAutoSpacing = true;
652
+ }
653
+ const afterAuto = attrVal(spacingEl, "afterAutospacing");
654
+ if (afterAuto === "1" || afterAuto === "true") {
655
+ spacing.afterAutoSpacing = true;
656
+ }
657
+ pPr.spacing = spacing;
658
+ }
659
+ const indEl = findChildNs(pPrEl, "ind");
660
+ if (indEl) {
661
+ const indent = {};
662
+ const left = attrInt(indEl, "left");
663
+ const right = attrInt(indEl, "right");
664
+ const hanging = attrInt(indEl, "hanging");
665
+ const firstLine = attrInt(indEl, "firstLine");
666
+ const start = attrInt(indEl, "start");
667
+ const end = attrInt(indEl, "end");
668
+ if (left !== undefined) {
669
+ indent.left = left;
670
+ }
671
+ if (right !== undefined) {
672
+ indent.right = right;
673
+ }
674
+ if (hanging !== undefined) {
675
+ indent.hanging = hanging;
676
+ }
677
+ if (firstLine !== undefined) {
678
+ indent.firstLine = firstLine;
679
+ }
680
+ if (start !== undefined) {
681
+ indent.start = start;
682
+ }
683
+ if (end !== undefined) {
684
+ indent.end = end;
685
+ }
686
+ pPr.indent = indent;
687
+ }
688
+ const numPrEl = findChildNs(pPrEl, "numPr");
689
+ if (numPrEl) {
690
+ const ilvlEl = findChildNs(numPrEl, "ilvl");
691
+ const numIdEl = findChildNs(numPrEl, "numId");
692
+ // Per OOXML schema, numId is required but ilvl is optional (defaults to 0).
693
+ if (numIdEl) {
694
+ pPr.numbering = {
695
+ level: ilvlEl ? (attrInt(ilvlEl, "val") ?? 0) : 0,
696
+ numId: attrInt(numIdEl, "val") ?? 0
697
+ };
698
+ }
699
+ }
700
+ const tabsEl = findChildNs(pPrEl, "tabs");
701
+ if (tabsEl) {
702
+ const tabs = [];
703
+ for (const tabEl of findChildrenNs(tabsEl, "tab")) {
704
+ tabs.push({
705
+ type: (attrVal(tabEl, "val") ?? "left"),
706
+ position: attrInt(tabEl, "pos") ?? 0,
707
+ leader: attrVal(tabEl, "leader")
708
+ });
709
+ }
710
+ if (tabs.length > 0) {
711
+ pPr.tabs = tabs;
712
+ }
713
+ }
714
+ const shdEl = findChildNs(pPrEl, "shd");
715
+ if (shdEl) {
716
+ pPr.shading = parseShading(shdEl);
717
+ }
718
+ const rPrEl = findChildNs(pPrEl, "rPr");
719
+ if (rPrEl) {
720
+ pPr.markRunProperties = parseRunProperties(rPrEl);
721
+ }
722
+ const sectPrEl = findChildNs(pPrEl, "sectPr");
723
+ if (sectPrEl) {
724
+ pPr.sectionProperties = parseSectionProperties(sectPrEl);
725
+ }
726
+ // Conditional formatting style mask
727
+ const cnfStyleEl = findChildNs(pPrEl, "cnfStyle");
728
+ if (cnfStyleEl) {
729
+ pPr.cnfStyle = attrVal(cnfStyleEl, "val");
730
+ }
731
+ // Paragraph property change
732
+ const pPrChangeEl = findChildNs(pPrEl, "pPrChange");
733
+ if (pPrChangeEl) {
734
+ const rev = parseRevisionInfo(pPrChangeEl);
735
+ if (rev) {
736
+ const prevPPrEl = findChildNs(pPrChangeEl, "pPr");
737
+ pPr.propertyChange = {
738
+ revision: rev,
739
+ previousProperties: prevPPrEl ? parseParagraphProperties(prevPPrEl) : undefined
740
+ };
741
+ }
742
+ }
743
+ // Paragraph mark insertion/deletion (w:pPr > w:rPr > w:ins/w:del)
744
+ const rPrInPPr = findChildNs(pPrEl, "rPr");
745
+ if (rPrInPPr) {
746
+ const insEl = findChildNs(rPrInPPr, "ins");
747
+ if (insEl) {
748
+ const rev = parseRevisionInfo(insEl);
749
+ if (rev) {
750
+ pPr.paragraphInsertion = rev;
751
+ }
752
+ }
753
+ const delEl = findChildNs(rPrInPPr, "del");
754
+ if (delEl) {
755
+ const rev = parseRevisionInfo(delEl);
756
+ if (rev) {
757
+ pPr.paragraphDeletion = rev;
758
+ }
759
+ }
760
+ }
761
+ return pPr;
762
+ }
763
+ // =============================================================================
764
+ // Revision Info Parser
765
+ // =============================================================================
766
+ function parseRevisionInfo(el) {
767
+ const author = attrVal(el, "author");
768
+ const id = attrInt(el, "id");
769
+ if (author === undefined || id === undefined) {
770
+ return undefined;
771
+ }
772
+ return {
773
+ author,
774
+ id,
775
+ date: attrVal(el, "date")
776
+ };
777
+ }
778
+ // =============================================================================
779
+ // Run Content Parser
780
+ // =============================================================================
781
+ function parseRunContent(el) {
782
+ const content = [];
783
+ for (const child of el.children) {
784
+ if (child.type !== "element") {
785
+ continue;
786
+ }
787
+ const name = child.name.replace(/^w:/, "");
788
+ switch (name) {
789
+ case "t":
790
+ content.push({ type: "text", text: textContent(child) });
791
+ break;
792
+ case "br": {
793
+ const brType = attrVal(child, "type");
794
+ content.push({ type: "break", breakType: brType });
795
+ break;
796
+ }
797
+ case "tab":
798
+ content.push({ type: "tab" });
799
+ break;
800
+ case "ptab": {
801
+ const alignment = attrVal(child, "alignment") ?? "left";
802
+ const relativeTo = attrVal(child, "relativeTo") ?? "margin";
803
+ const leader = attrVal(child, "leader");
804
+ const ptab = {
805
+ type: "ptab",
806
+ alignment,
807
+ relativeTo
808
+ };
809
+ if (leader) {
810
+ ptab.leader = leader;
811
+ }
812
+ content.push(ptab);
813
+ break;
814
+ }
815
+ case "ruby": {
816
+ const ruby = { type: "ruby" };
817
+ const rubyPrEl = findChildNs(child, "rubyPr");
818
+ if (rubyPrEl) {
819
+ const props = {};
820
+ const alignEl = findChildNs(rubyPrEl, "rubyAlign");
821
+ if (alignEl) {
822
+ props.align = attrVal(alignEl, "val");
823
+ }
824
+ const hpsEl = findChildNs(rubyPrEl, "hps");
825
+ if (hpsEl) {
826
+ props.fontSize = attrInt(hpsEl, "val");
827
+ }
828
+ const hpsRaiseEl = findChildNs(rubyPrEl, "hpsRaise");
829
+ if (hpsRaiseEl) {
830
+ props.raise = attrInt(hpsRaiseEl, "val");
831
+ }
832
+ const hpsBaseTextEl = findChildNs(rubyPrEl, "hpsBaseText");
833
+ if (hpsBaseTextEl) {
834
+ props.baseFontSize = attrInt(hpsBaseTextEl, "val");
835
+ }
836
+ const lidEl = findChildNs(rubyPrEl, "lid");
837
+ if (lidEl) {
838
+ props.language = attrVal(lidEl, "val");
839
+ }
840
+ if (Object.keys(props).length > 0) {
841
+ ruby.properties = props;
842
+ }
843
+ }
844
+ // Parse w:rt (ruby text)
845
+ const rtEl = findChildNs(child, "rt");
846
+ const rubyText = [];
847
+ if (rtEl) {
848
+ for (const rtChild of rtEl.children) {
849
+ if (rtChild.type === "element" && rtChild.name.replace(/^w:/, "") === "r") {
850
+ rubyText.push(parseRun(rtChild));
851
+ }
852
+ }
853
+ }
854
+ ruby.rubyText = rubyText;
855
+ // Parse w:rubyBase
856
+ const baseEl = findChildNs(child, "rubyBase");
857
+ const baseText = [];
858
+ if (baseEl) {
859
+ for (const bChild of baseEl.children) {
860
+ if (bChild.type === "element" && bChild.name.replace(/^w:/, "") === "r") {
861
+ baseText.push(parseRun(bChild));
862
+ }
863
+ }
864
+ }
865
+ ruby.baseText = baseText;
866
+ content.push(ruby);
867
+ break;
868
+ }
869
+ case "sym":
870
+ content.push({
871
+ type: "symbol",
872
+ font: attrVal(child, "font") ?? "",
873
+ char: attrVal(child, "char") ?? ""
874
+ });
875
+ break;
876
+ case "footnoteReference": {
877
+ const fr = { type: "footnoteRef", id: attrInt(child, "id") ?? 0 };
878
+ const cmf = attrVal(child, "customMarkFollows");
879
+ if (cmf === "1" || cmf === "true") {
880
+ fr.customMarkFollows = true;
881
+ }
882
+ content.push(fr);
883
+ break;
884
+ }
885
+ case "endnoteReference": {
886
+ const er = { type: "endnoteRef", id: attrInt(child, "id") ?? 0 };
887
+ const cmf = attrVal(child, "customMarkFollows");
888
+ if (cmf === "1" || cmf === "true") {
889
+ er.customMarkFollows = true;
890
+ }
891
+ content.push(er);
892
+ break;
893
+ }
894
+ case "drawing":
895
+ parseDrawingContent(child, content);
896
+ break;
897
+ case "cr":
898
+ content.push({ type: "carriageReturn" });
899
+ break;
900
+ case "noBreakHyphen":
901
+ content.push({ type: "noBreakHyphen" });
902
+ break;
903
+ case "softHyphen":
904
+ content.push({ type: "softHyphen" });
905
+ break;
906
+ case "lastRenderedPageBreak":
907
+ content.push({ type: "lastRenderedPageBreak" });
908
+ break;
909
+ case "annotationRef":
910
+ content.push({ type: "annotationReference", id: attrInt(child, "id") ?? 0 });
911
+ break;
912
+ case "commentReference":
913
+ // This is annotationReference for comments inside runs
914
+ content.push({ type: "annotationReference", id: attrInt(child, "id") ?? 0 });
915
+ break;
916
+ }
917
+ }
918
+ return content;
919
+ }
920
+ function parseDrawingContent(drawingEl, content) {
921
+ // Look for wp:inline
922
+ const inlineEl = findChild(drawingEl, "wp:inline");
923
+ if (inlineEl) {
924
+ const extentEl = findChild(inlineEl, "wp:extent");
925
+ const docPrEl = findChild(inlineEl, "wp:docPr");
926
+ const graphicEl = findChild(inlineEl, "a:graphic");
927
+ const graphicDataEl = graphicEl ? findChild(graphicEl, "a:graphicData") : undefined;
928
+ const picEl = graphicDataEl ? findChild(graphicDataEl, "pic:pic") : undefined;
929
+ const blipFillEl = picEl ? findChild(picEl, "pic:blipFill") : undefined;
930
+ const blipEl = blipFillEl ? findChild(blipFillEl, "a:blip") : undefined;
931
+ const rId = blipEl?.attributes["r:embed"] ?? "";
932
+ const cx = parseInt(extentEl?.attributes["cx"] ?? "0", 10);
933
+ const cy = parseInt(extentEl?.attributes["cy"] ?? "0", 10);
934
+ const img = {
935
+ type: "image",
936
+ rId,
937
+ width: cx,
938
+ height: cy,
939
+ altText: docPrEl?.attributes["descr"],
940
+ name: docPrEl?.attributes["name"],
941
+ drawingId: docPrEl ? parseInt(docPrEl.attributes["id"] ?? "1", 10) : undefined
942
+ };
943
+ // Parse xfrm for rotation/flip
944
+ const spPrEl = picEl ? findChild(picEl, "pic:spPr") : undefined;
945
+ if (spPrEl) {
946
+ const xfrmEl = findChild(spPrEl, "a:xfrm");
947
+ if (xfrmEl) {
948
+ const rot = xfrmEl.attributes["rot"];
949
+ if (rot !== undefined && rot !== "") {
950
+ img.rotation = parseInt(rot, 10);
951
+ }
952
+ if (xfrmEl.attributes["flipH"] === "1") {
953
+ img.flipHorizontal = true;
954
+ }
955
+ if (xfrmEl.attributes["flipV"] === "1") {
956
+ img.flipVertical = true;
957
+ }
958
+ }
959
+ // Outline
960
+ const lnEl = findChild(spPrEl, "a:ln");
961
+ if (lnEl) {
962
+ const outline = {};
963
+ const w = lnEl.attributes["w"];
964
+ if (w) {
965
+ outline.width = parseInt(w, 10);
966
+ }
967
+ const sfEl = findChild(lnEl, "a:solidFill");
968
+ const srgbEl = sfEl ? findChild(sfEl, "a:srgbClr") : undefined;
969
+ if (srgbEl) {
970
+ outline.color = srgbEl.attributes["val"];
971
+ }
972
+ img.outline = outline;
973
+ }
974
+ }
975
+ // SVG blip in a:extLst
976
+ if (blipEl) {
977
+ const extLst = findChild(blipEl, "a:extLst");
978
+ if (extLst) {
979
+ for (const ext of findChildren(extLst, "a:ext")) {
980
+ const svgBlip = findChild(ext, "asvg:svgBlip") ?? findChildNs(ext, "svgBlip");
981
+ if (svgBlip) {
982
+ const svgEmbed = svgBlip.attributes["r:embed"];
983
+ if (svgEmbed) {
984
+ img.svgRId = svgEmbed;
985
+ }
986
+ }
987
+ }
988
+ }
989
+ }
990
+ content.push(img);
991
+ }
992
+ }
993
+ // =============================================================================
994
+ // Floating Image Parser
995
+ // =============================================================================
996
+ function parseFloatingImage(anchorEl) {
997
+ const docPrEl = findChild(anchorEl, "wp:docPr");
998
+ const extentEl = findChild(anchorEl, "wp:extent");
999
+ const graphicEl = findChild(anchorEl, "a:graphic");
1000
+ const graphicDataEl = graphicEl ? findChild(graphicEl, "a:graphicData") : undefined;
1001
+ const picEl = graphicDataEl ? findChild(graphicDataEl, "pic:pic") : undefined;
1002
+ const blipFillEl = picEl ? findChild(picEl, "pic:blipFill") : undefined;
1003
+ const blipEl = blipFillEl ? findChild(blipFillEl, "a:blip") : undefined;
1004
+ const rId = blipEl?.attributes["r:embed"];
1005
+ if (!rId) {
1006
+ return undefined;
1007
+ }
1008
+ const cx = parseInt(extentEl?.attributes["cx"] ?? "0", 10);
1009
+ const cy = parseInt(extentEl?.attributes["cy"] ?? "0", 10);
1010
+ const img = {
1011
+ type: "floatingImage",
1012
+ rId,
1013
+ width: cx,
1014
+ height: cy,
1015
+ altText: docPrEl?.attributes["descr"],
1016
+ name: docPrEl?.attributes["name"],
1017
+ drawingId: docPrEl ? parseInt(docPrEl.attributes["id"] ?? "1", 10) : undefined
1018
+ };
1019
+ // Attributes
1020
+ if (anchorEl.attributes["behindDoc"] === "1") {
1021
+ img.behindDoc = true;
1022
+ }
1023
+ if (anchorEl.attributes["locked"] === "1") {
1024
+ img.lockAnchor = true;
1025
+ }
1026
+ if (anchorEl.attributes["layoutInCell"] === "0") {
1027
+ img.layoutInCell = false;
1028
+ }
1029
+ if (anchorEl.attributes["allowOverlap"] === "0") {
1030
+ img.allowOverlap = false;
1031
+ }
1032
+ const rh = anchorEl.attributes["relativeHeight"];
1033
+ if (rh) {
1034
+ img.relativeHeight = parseInt(rh, 10);
1035
+ }
1036
+ // Dist*
1037
+ const distT = anchorEl.attributes["distT"];
1038
+ if (distT) {
1039
+ img.distT = parseInt(distT, 10);
1040
+ }
1041
+ const distB = anchorEl.attributes["distB"];
1042
+ if (distB) {
1043
+ img.distB = parseInt(distB, 10);
1044
+ }
1045
+ const distL = anchorEl.attributes["distL"];
1046
+ if (distL) {
1047
+ img.distL = parseInt(distL, 10);
1048
+ }
1049
+ const distR = anchorEl.attributes["distR"];
1050
+ if (distR) {
1051
+ img.distR = parseInt(distR, 10);
1052
+ }
1053
+ // Simple positioning
1054
+ if (anchorEl.attributes["simplePos"] === "1") {
1055
+ const sposEl = findChild(anchorEl, "wp:simplePos");
1056
+ if (sposEl) {
1057
+ const x = parseInt(sposEl.attributes["x"] ?? "0", 10);
1058
+ const y = parseInt(sposEl.attributes["y"] ?? "0", 10);
1059
+ img.simplePos = { x, y };
1060
+ }
1061
+ }
1062
+ // Horizontal position
1063
+ const hPosEl = findChild(anchorEl, "wp:positionH");
1064
+ if (hPosEl) {
1065
+ const h = { relativeTo: hPosEl.attributes["relativeFrom"] };
1066
+ const offsetEl = findChild(hPosEl, "wp:posOffset");
1067
+ if (offsetEl) {
1068
+ h.offset = parseInt(textContent(offsetEl), 10);
1069
+ }
1070
+ const alignEl = findChild(hPosEl, "wp:align");
1071
+ if (alignEl) {
1072
+ h.align = textContent(alignEl);
1073
+ }
1074
+ img.horizontalPosition = h;
1075
+ }
1076
+ // Vertical position
1077
+ const vPosEl = findChild(anchorEl, "wp:positionV");
1078
+ if (vPosEl) {
1079
+ const v = { relativeTo: vPosEl.attributes["relativeFrom"] };
1080
+ const offsetEl = findChild(vPosEl, "wp:posOffset");
1081
+ if (offsetEl) {
1082
+ v.offset = parseInt(textContent(offsetEl), 10);
1083
+ }
1084
+ const alignEl = findChild(vPosEl, "wp:align");
1085
+ if (alignEl) {
1086
+ v.align = textContent(alignEl);
1087
+ }
1088
+ img.verticalPosition = v;
1089
+ }
1090
+ // Wrap
1091
+ for (const wrapChild of anchorEl.children) {
1092
+ if (wrapChild.type !== "element") {
1093
+ continue;
1094
+ }
1095
+ const wn = wrapChild.name;
1096
+ if (wn === "wp:wrapSquare") {
1097
+ img.wrap = { style: "square", side: wrapChild.attributes["wrapText"] };
1098
+ }
1099
+ else if (wn === "wp:wrapTight") {
1100
+ img.wrap = { style: "tight", side: wrapChild.attributes["wrapText"] };
1101
+ }
1102
+ else if (wn === "wp:wrapThrough") {
1103
+ img.wrap = { style: "through", side: wrapChild.attributes["wrapText"] };
1104
+ }
1105
+ else if (wn === "wp:wrapTopAndBottom") {
1106
+ img.wrap = { style: "topAndBottom" };
1107
+ }
1108
+ else if (wn === "wp:wrapNone") {
1109
+ img.wrap = { style: "none" };
1110
+ }
1111
+ if (img.wrap) {
1112
+ // Parse wrap margins
1113
+ const distT = anchorEl.attributes["distT"];
1114
+ const distB = anchorEl.attributes["distB"];
1115
+ const distL = anchorEl.attributes["distL"];
1116
+ const distR = anchorEl.attributes["distR"];
1117
+ if (distT || distB || distL || distR) {
1118
+ const margins = {};
1119
+ if (distT) {
1120
+ margins.top = parseInt(distT, 10);
1121
+ }
1122
+ if (distB) {
1123
+ margins.bottom = parseInt(distB, 10);
1124
+ }
1125
+ if (distL) {
1126
+ margins.left = parseInt(distL, 10);
1127
+ }
1128
+ if (distR) {
1129
+ margins.right = parseInt(distR, 10);
1130
+ }
1131
+ img.wrap.margins = margins;
1132
+ }
1133
+ break;
1134
+ }
1135
+ }
1136
+ // Rotation/flip from spPr
1137
+ const spPrEl = picEl ? findChild(picEl, "pic:spPr") : undefined;
1138
+ if (spPrEl) {
1139
+ const xfrmEl = findChild(spPrEl, "a:xfrm");
1140
+ if (xfrmEl) {
1141
+ const rot = xfrmEl.attributes["rot"];
1142
+ if (rot !== undefined && rot !== "") {
1143
+ img.rotation = parseInt(rot, 10);
1144
+ }
1145
+ if (xfrmEl.attributes["flipH"] === "1") {
1146
+ img.flipHorizontal = true;
1147
+ }
1148
+ if (xfrmEl.attributes["flipV"] === "1") {
1149
+ img.flipVertical = true;
1150
+ }
1151
+ }
1152
+ const lnEl = findChild(spPrEl, "a:ln");
1153
+ if (lnEl) {
1154
+ const outline = {};
1155
+ const w = lnEl.attributes["w"];
1156
+ if (w) {
1157
+ outline.width = parseInt(w, 10);
1158
+ }
1159
+ const sfEl = findChild(lnEl, "a:solidFill");
1160
+ const srgbEl = sfEl ? findChild(sfEl, "a:srgbClr") : undefined;
1161
+ if (srgbEl) {
1162
+ outline.color = srgbEl.attributes["val"];
1163
+ }
1164
+ img.outline = outline;
1165
+ }
1166
+ }
1167
+ // SVG blip in a:extLst
1168
+ if (blipEl) {
1169
+ const extLst = findChild(blipEl, "a:extLst");
1170
+ if (extLst) {
1171
+ for (const ext of findChildren(extLst, "a:ext")) {
1172
+ const svgBlip = findChild(ext, "asvg:svgBlip") ?? findChildNs(ext, "svgBlip");
1173
+ if (svgBlip) {
1174
+ const svgEmbed = svgBlip.attributes["r:embed"];
1175
+ if (svgEmbed) {
1176
+ img.svgRId = svgEmbed;
1177
+ }
1178
+ }
1179
+ }
1180
+ }
1181
+ }
1182
+ // Source rectangle (crop)
1183
+ if (blipFillEl) {
1184
+ const srcRectEl = findChild(blipFillEl, "a:srcRect");
1185
+ if (srcRectEl) {
1186
+ const sr = {};
1187
+ const lAttr = srcRectEl.attributes["l"];
1188
+ const tAttr = srcRectEl.attributes["t"];
1189
+ const rAttr = srcRectEl.attributes["r"];
1190
+ const bAttr = srcRectEl.attributes["b"];
1191
+ if (lAttr !== undefined) {
1192
+ sr.l = parseInt(lAttr, 10);
1193
+ }
1194
+ if (tAttr !== undefined) {
1195
+ sr.t = parseInt(tAttr, 10);
1196
+ }
1197
+ if (rAttr !== undefined) {
1198
+ sr.r = parseInt(rAttr, 10);
1199
+ }
1200
+ if (bAttr !== undefined) {
1201
+ sr.b = parseInt(bAttr, 10);
1202
+ }
1203
+ if (Object.keys(sr).length > 0) {
1204
+ img.srcRect = sr;
1205
+ }
1206
+ }
1207
+ }
1208
+ return img;
1209
+ }
1210
+ // =============================================================================
1211
+ // DrawingML Shape Parser
1212
+ // =============================================================================
1213
+ function parseDrawingShape(anchorEl, wspEl) {
1214
+ const docPrEl = findChild(anchorEl, "wp:docPr");
1215
+ const extentEl = findChild(anchorEl, "wp:extent");
1216
+ const cx = parseInt(extentEl?.attributes["cx"] ?? "0", 10);
1217
+ const cy = parseInt(extentEl?.attributes["cy"] ?? "0", 10);
1218
+ // Parse preset shape type from wps:spPr > a:prstGeom
1219
+ const spPrEl = findChild(wspEl, "wps:spPr") ?? findChildNs(wspEl, "spPr");
1220
+ const prstGeomEl = spPrEl
1221
+ ? (findChild(spPrEl, "a:prstGeom") ?? findChildNs(spPrEl, "prstGeom"))
1222
+ : undefined;
1223
+ const shapeType = prstGeomEl?.attributes["prst"] ?? "rect";
1224
+ const shape = {
1225
+ type: "drawingShape",
1226
+ shapeType,
1227
+ width: cx,
1228
+ height: cy,
1229
+ altText: docPrEl?.attributes["descr"],
1230
+ name: docPrEl?.attributes["name"]
1231
+ };
1232
+ // Parse fill
1233
+ if (spPrEl) {
1234
+ const solidFill = findChild(spPrEl, "a:solidFill") ?? findChildNs(spPrEl, "solidFill");
1235
+ if (solidFill) {
1236
+ const srgb = findChild(solidFill, "a:srgbClr") ?? findChildNs(solidFill, "srgbClr");
1237
+ if (srgb) {
1238
+ shape.fillColor = srgb.attributes["val"];
1239
+ }
1240
+ }
1241
+ const noFill = findChild(spPrEl, "a:noFill") ?? findChildNs(spPrEl, "noFill");
1242
+ if (noFill) {
1243
+ shape.noFill = true;
1244
+ }
1245
+ // Parse outline
1246
+ const lnEl = findChild(spPrEl, "a:ln") ?? findChildNs(spPrEl, "ln");
1247
+ if (lnEl) {
1248
+ const w = lnEl.attributes["w"];
1249
+ if (w) {
1250
+ shape.outlineWidth = parseInt(w, 10);
1251
+ }
1252
+ const lnFill = findChild(lnEl, "a:solidFill") ?? findChildNs(lnEl, "solidFill");
1253
+ if (lnFill) {
1254
+ const srgb = findChild(lnFill, "a:srgbClr") ?? findChildNs(lnFill, "srgbClr");
1255
+ if (srgb) {
1256
+ shape.outlineColor = srgb.attributes["val"];
1257
+ }
1258
+ }
1259
+ const noLn = findChild(lnEl, "a:noFill") ?? findChildNs(lnEl, "noFill");
1260
+ if (noLn) {
1261
+ shape.noOutline = true;
1262
+ }
1263
+ }
1264
+ }
1265
+ // Parse text content (wps:txbx > w:txbxContent)
1266
+ const txbxEl = findChild(wspEl, "wps:txbx") ?? findChildNs(wspEl, "txbx");
1267
+ const txbxContentEl = txbxEl
1268
+ ? (findChild(txbxEl, "w:txbxContent") ?? findChildNs(txbxEl, "txbxContent"))
1269
+ : undefined;
1270
+ if (txbxContentEl) {
1271
+ const paras = [];
1272
+ for (const child of txbxContentEl.children) {
1273
+ if (child.type === "element" && child.name.replace(/^w:/, "") === "p") {
1274
+ paras.push(parseParagraph(child));
1275
+ }
1276
+ }
1277
+ if (paras.length > 0) {
1278
+ shape.textContent = paras;
1279
+ }
1280
+ }
1281
+ // Parse positioning
1282
+ const posH = findChild(anchorEl, "wp:positionH");
1283
+ if (posH) {
1284
+ const hp = { relativeTo: posH.attributes["relativeFrom"] };
1285
+ const offsetEl = findChild(posH, "wp:posOffset");
1286
+ if (offsetEl) {
1287
+ hp.offset = parseInt(textContent(offsetEl), 10);
1288
+ }
1289
+ const alignEl = findChild(posH, "wp:align");
1290
+ if (alignEl) {
1291
+ hp.align = textContent(alignEl);
1292
+ }
1293
+ shape.horizontalPosition = hp;
1294
+ }
1295
+ const posV = findChild(anchorEl, "wp:positionV");
1296
+ if (posV) {
1297
+ const vp = { relativeTo: posV.attributes["relativeFrom"] };
1298
+ const offsetEl = findChild(posV, "wp:posOffset");
1299
+ if (offsetEl) {
1300
+ vp.offset = parseInt(textContent(offsetEl), 10);
1301
+ }
1302
+ const alignEl = findChild(posV, "wp:align");
1303
+ if (alignEl) {
1304
+ vp.align = textContent(alignEl);
1305
+ }
1306
+ shape.verticalPosition = vp;
1307
+ }
1308
+ // Wrap
1309
+ for (const wrapChild of anchorEl.children) {
1310
+ if (wrapChild.type !== "element") {
1311
+ continue;
1312
+ }
1313
+ const wn = wrapChild.name;
1314
+ if (wn === "wp:wrapSquare") {
1315
+ shape.wrap = { style: "square", side: wrapChild.attributes["wrapText"] };
1316
+ }
1317
+ else if (wn === "wp:wrapTight") {
1318
+ shape.wrap = { style: "tight", side: wrapChild.attributes["wrapText"] };
1319
+ }
1320
+ else if (wn === "wp:wrapTopAndBottom") {
1321
+ shape.wrap = { style: "topAndBottom" };
1322
+ }
1323
+ else if (wn === "wp:wrapNone") {
1324
+ shape.wrap = { style: "none" };
1325
+ }
1326
+ }
1327
+ // Behind doc
1328
+ if (anchorEl.attributes["behindDoc"] === "1") {
1329
+ shape.behindDoc = true;
1330
+ }
1331
+ // Rotation
1332
+ if (spPrEl) {
1333
+ const xfrmEl = findChild(spPrEl, "a:xfrm") ?? findChildNs(spPrEl, "xfrm");
1334
+ if (xfrmEl?.attributes["rot"]) {
1335
+ shape.rotation = parseInt(xfrmEl.attributes["rot"], 10);
1336
+ }
1337
+ }
1338
+ return shape;
1339
+ }
1340
+ // =============================================================================
1341
+ // Math Parser
1342
+ // =============================================================================
1343
+ function findMathChild(el, localName) {
1344
+ return findChild(el, `m:${localName}`) ?? findChild(el, localName);
1345
+ }
1346
+ function mathAttrVal(el, name) {
1347
+ return el.attributes[`m:${name}`] ?? el.attributes[name];
1348
+ }
1349
+ function findMathChildren(el, localName) {
1350
+ const a = findChildren(el, `m:${localName}`);
1351
+ return a.length > 0 ? a : findChildren(el, localName);
1352
+ }
1353
+ function parseMathContent(el) {
1354
+ const result = [];
1355
+ for (const child of el.children) {
1356
+ if (child.type !== "element") {
1357
+ continue;
1358
+ }
1359
+ const name = child.name.replace(/^m:/, "");
1360
+ switch (name) {
1361
+ case "r": {
1362
+ // Math run
1363
+ const tEl = findMathChild(child, "t");
1364
+ const mrPrEl = findMathChild(child, "rPr");
1365
+ const mr = { type: "mathRun", text: tEl ? textContent(tEl) : "" };
1366
+ if (mrPrEl) {
1367
+ const props = {};
1368
+ const sty = findMathChild(mrPrEl, "sty");
1369
+ if (sty) {
1370
+ const v = sty.attributes["m:val"] ?? sty.attributes["val"];
1371
+ if (v === "p" || v === "b") {
1372
+ props.italic = false;
1373
+ }
1374
+ if (v === "b" || v === "bi") {
1375
+ props.bold = true;
1376
+ }
1377
+ }
1378
+ if (Object.keys(props).length > 0) {
1379
+ mr.properties = props;
1380
+ }
1381
+ }
1382
+ result.push(mr);
1383
+ break;
1384
+ }
1385
+ case "f": {
1386
+ const fPrEl = findMathChild(child, "fPr");
1387
+ const num = findMathChild(child, "num");
1388
+ const den = findMathChild(child, "den");
1389
+ const frac = {
1390
+ type: "mathFraction",
1391
+ numerator: num ? parseMathContent(num) : [],
1392
+ denominator: den ? parseMathContent(den) : []
1393
+ };
1394
+ if (fPrEl) {
1395
+ const typeEl = findMathChild(fPrEl, "type");
1396
+ if (typeEl) {
1397
+ frac.fractionType = typeEl.attributes["m:val"] ?? typeEl.attributes["val"];
1398
+ }
1399
+ }
1400
+ result.push(frac);
1401
+ break;
1402
+ }
1403
+ case "sSup": {
1404
+ const base = findMathChild(child, "e");
1405
+ const sup = findMathChild(child, "sup");
1406
+ result.push({
1407
+ type: "mathSuperScript",
1408
+ base: base ? parseMathContent(base) : [],
1409
+ superScript: sup ? parseMathContent(sup) : []
1410
+ });
1411
+ break;
1412
+ }
1413
+ case "sSub": {
1414
+ const base = findMathChild(child, "e");
1415
+ const sub = findMathChild(child, "sub");
1416
+ result.push({
1417
+ type: "mathSubScript",
1418
+ base: base ? parseMathContent(base) : [],
1419
+ subScript: sub ? parseMathContent(sub) : []
1420
+ });
1421
+ break;
1422
+ }
1423
+ case "sSubSup": {
1424
+ const base = findMathChild(child, "e");
1425
+ const sub = findMathChild(child, "sub");
1426
+ const sup = findMathChild(child, "sup");
1427
+ result.push({
1428
+ type: "mathSubSuperScript",
1429
+ base: base ? parseMathContent(base) : [],
1430
+ subScript: sub ? parseMathContent(sub) : [],
1431
+ superScript: sup ? parseMathContent(sup) : []
1432
+ });
1433
+ break;
1434
+ }
1435
+ case "sPre": {
1436
+ const base = findMathChild(child, "e");
1437
+ const sub = findMathChild(child, "sub");
1438
+ const sup = findMathChild(child, "sup");
1439
+ result.push({
1440
+ type: "mathPreSubSuperScript",
1441
+ base: base ? parseMathContent(base) : [],
1442
+ preSubScript: sub ? parseMathContent(sub) : [],
1443
+ preSuperScript: sup ? parseMathContent(sup) : []
1444
+ });
1445
+ break;
1446
+ }
1447
+ case "phant": {
1448
+ const eEl = findMathChild(child, "e");
1449
+ const phantPrEl = findMathChild(child, "phantPr");
1450
+ const ph = {
1451
+ type: "mathPhantom",
1452
+ content: eEl ? parseMathContent(eEl) : []
1453
+ };
1454
+ if (phantPrEl) {
1455
+ const boolAttr = (name) => {
1456
+ const el = findMathChild(phantPrEl, name);
1457
+ if (!el) {
1458
+ return false;
1459
+ }
1460
+ const v = mathAttrVal(el, "val");
1461
+ return v !== "0" && v !== "false";
1462
+ };
1463
+ if (boolAttr("show")) {
1464
+ ph.show = true;
1465
+ }
1466
+ if (boolAttr("zeroWid")) {
1467
+ ph.zeroWidth = true;
1468
+ }
1469
+ if (boolAttr("zeroAsc")) {
1470
+ ph.zeroAscent = true;
1471
+ }
1472
+ if (boolAttr("zeroDesc")) {
1473
+ ph.zeroDescent = true;
1474
+ }
1475
+ if (boolAttr("transp")) {
1476
+ ph.transparent = true;
1477
+ }
1478
+ }
1479
+ result.push(ph);
1480
+ break;
1481
+ }
1482
+ case "groupChr": {
1483
+ const eEl = findMathChild(child, "e");
1484
+ const prEl = findMathChild(child, "groupChrPr");
1485
+ const g = {
1486
+ type: "mathGroupChar",
1487
+ base: eEl ? parseMathContent(eEl) : []
1488
+ };
1489
+ if (prEl) {
1490
+ const chrEl = findMathChild(prEl, "chr");
1491
+ if (chrEl) {
1492
+ g.char = mathAttrVal(chrEl, "val");
1493
+ }
1494
+ const posEl = findMathChild(prEl, "pos");
1495
+ if (posEl) {
1496
+ const v = mathAttrVal(posEl, "val");
1497
+ if (v === "top" || v === "bottom") {
1498
+ g.position = v;
1499
+ }
1500
+ }
1501
+ const vjcEl = findMathChild(prEl, "vertJc");
1502
+ if (vjcEl) {
1503
+ const v = mathAttrVal(vjcEl, "val");
1504
+ if (v === "top" || v === "center" || v === "bottom") {
1505
+ g.verticalAlign = v;
1506
+ }
1507
+ }
1508
+ }
1509
+ result.push(g);
1510
+ break;
1511
+ }
1512
+ case "borderBox": {
1513
+ const eEl = findMathChild(child, "e");
1514
+ const prEl = findMathChild(child, "borderBoxPr");
1515
+ const b = {
1516
+ type: "mathBorderBox",
1517
+ content: eEl ? parseMathContent(eEl) : []
1518
+ };
1519
+ if (prEl) {
1520
+ const boolAttr = (name) => {
1521
+ const el = findMathChild(prEl, name);
1522
+ if (!el) {
1523
+ return false;
1524
+ }
1525
+ const v = mathAttrVal(el, "val");
1526
+ return v !== "0" && v !== "false";
1527
+ };
1528
+ if (boolAttr("hideTop")) {
1529
+ b.hideTop = true;
1530
+ }
1531
+ if (boolAttr("hideBot")) {
1532
+ b.hideBottom = true;
1533
+ }
1534
+ if (boolAttr("hideLeft")) {
1535
+ b.hideLeft = true;
1536
+ }
1537
+ if (boolAttr("hideRight")) {
1538
+ b.hideRight = true;
1539
+ }
1540
+ if (boolAttr("strikeBLTR")) {
1541
+ b.strikeBlTr = true;
1542
+ }
1543
+ if (boolAttr("strikeTLBR")) {
1544
+ b.strikeTlBr = true;
1545
+ }
1546
+ if (boolAttr("strikeH")) {
1547
+ b.strikeH = true;
1548
+ }
1549
+ if (boolAttr("strikeV")) {
1550
+ b.strikeV = true;
1551
+ }
1552
+ }
1553
+ result.push(b);
1554
+ break;
1555
+ }
1556
+ case "rad": {
1557
+ const radPrEl = findMathChild(child, "radPr");
1558
+ const deg = findMathChild(child, "deg");
1559
+ const e = findMathChild(child, "e");
1560
+ const rad = {
1561
+ type: "mathRadical",
1562
+ content: e ? parseMathContent(e) : []
1563
+ };
1564
+ if (deg) {
1565
+ rad.degree = parseMathContent(deg);
1566
+ }
1567
+ if (radPrEl) {
1568
+ const hd = findMathChild(radPrEl, "degHide");
1569
+ if (hd) {
1570
+ const v = hd.attributes["m:val"] ?? hd.attributes["val"];
1571
+ if (v === "1" || v === "on" || v === "true") {
1572
+ rad.hideDegree = true;
1573
+ }
1574
+ }
1575
+ }
1576
+ result.push(rad);
1577
+ break;
1578
+ }
1579
+ case "d": {
1580
+ const dPrEl = findMathChild(child, "dPr");
1581
+ const delim = { type: "mathDelimiter", content: [] };
1582
+ if (dPrEl) {
1583
+ const bc = findMathChild(dPrEl, "begChr");
1584
+ if (bc) {
1585
+ delim.beginChar = bc.attributes["m:val"] ?? bc.attributes["val"];
1586
+ }
1587
+ const ec = findMathChild(dPrEl, "endChr");
1588
+ if (ec) {
1589
+ delim.endChar = ec.attributes["m:val"] ?? ec.attributes["val"];
1590
+ }
1591
+ const sc = findMathChild(dPrEl, "sepChr");
1592
+ if (sc) {
1593
+ delim.separatorChar = sc.attributes["m:val"] ?? sc.attributes["val"];
1594
+ }
1595
+ }
1596
+ for (const eEl of findMathChildren(child, "e")) {
1597
+ delim.content.push(parseMathContent(eEl));
1598
+ }
1599
+ result.push(delim);
1600
+ break;
1601
+ }
1602
+ case "nary": {
1603
+ const nPrEl = findMathChild(child, "naryPr");
1604
+ const sub = findMathChild(child, "sub");
1605
+ const sup = findMathChild(child, "sup");
1606
+ const e = findMathChild(child, "e");
1607
+ const nary = {
1608
+ type: "mathNary",
1609
+ content: e ? parseMathContent(e) : []
1610
+ };
1611
+ if (sub) {
1612
+ nary.sub = parseMathContent(sub);
1613
+ }
1614
+ if (sup) {
1615
+ nary.sup = parseMathContent(sup);
1616
+ }
1617
+ if (nPrEl) {
1618
+ const chrEl = findMathChild(nPrEl, "chr");
1619
+ if (chrEl) {
1620
+ nary.char = chrEl.attributes["m:val"] ?? chrEl.attributes["val"];
1621
+ }
1622
+ const limLoc = findMathChild(nPrEl, "limLoc");
1623
+ if (limLoc) {
1624
+ nary.limitsLocation = limLoc.attributes["m:val"] ?? limLoc.attributes["val"];
1625
+ }
1626
+ const sh = findMathChild(nPrEl, "supHide");
1627
+ if (sh && (sh.attributes["m:val"] ?? sh.attributes["val"]) === "1") {
1628
+ nary.supHide = true;
1629
+ }
1630
+ const sbh = findMathChild(nPrEl, "subHide");
1631
+ if (sbh && (sbh.attributes["m:val"] ?? sbh.attributes["val"]) === "1") {
1632
+ nary.subHide = true;
1633
+ }
1634
+ }
1635
+ result.push(nary);
1636
+ break;
1637
+ }
1638
+ case "func": {
1639
+ const fName = findMathChild(child, "fName");
1640
+ const e = findMathChild(child, "e");
1641
+ result.push({
1642
+ type: "mathFunction",
1643
+ name: fName ? parseMathContent(fName) : [],
1644
+ content: e ? parseMathContent(e) : []
1645
+ });
1646
+ break;
1647
+ }
1648
+ case "limLow": {
1649
+ const base = findMathChild(child, "e");
1650
+ const lim = findMathChild(child, "lim");
1651
+ result.push({
1652
+ type: "mathLimit",
1653
+ limitType: "lower",
1654
+ base: base ? parseMathContent(base) : [],
1655
+ limit: lim ? parseMathContent(lim) : []
1656
+ });
1657
+ break;
1658
+ }
1659
+ case "limUpp": {
1660
+ const base = findMathChild(child, "e");
1661
+ const lim = findMathChild(child, "lim");
1662
+ result.push({
1663
+ type: "mathLimit",
1664
+ limitType: "upper",
1665
+ base: base ? parseMathContent(base) : [],
1666
+ limit: lim ? parseMathContent(lim) : []
1667
+ });
1668
+ break;
1669
+ }
1670
+ case "m": {
1671
+ // Matrix
1672
+ const rows = [];
1673
+ for (const mrEl of findMathChildren(child, "mr")) {
1674
+ const row = [];
1675
+ for (const eEl of findMathChildren(mrEl, "e")) {
1676
+ row.push(parseMathContent(eEl));
1677
+ }
1678
+ rows.push(row);
1679
+ }
1680
+ result.push({ type: "mathMatrix", rows });
1681
+ break;
1682
+ }
1683
+ case "acc": {
1684
+ const accPrEl = findMathChild(child, "accPr");
1685
+ const e = findMathChild(child, "e");
1686
+ const acc = {
1687
+ type: "mathAccent",
1688
+ content: e ? parseMathContent(e) : []
1689
+ };
1690
+ if (accPrEl) {
1691
+ const chr = findMathChild(accPrEl, "chr");
1692
+ if (chr) {
1693
+ acc.char = chr.attributes["m:val"] ?? chr.attributes["val"];
1694
+ }
1695
+ }
1696
+ result.push(acc);
1697
+ break;
1698
+ }
1699
+ case "bar": {
1700
+ const barPrEl = findMathChild(child, "barPr");
1701
+ const e = findMathChild(child, "e");
1702
+ let position = "top";
1703
+ if (barPrEl) {
1704
+ const pos = findMathChild(barPrEl, "pos");
1705
+ if (pos) {
1706
+ const v = pos.attributes["m:val"] ?? pos.attributes["val"];
1707
+ if (v === "bot") {
1708
+ position = "bottom";
1709
+ }
1710
+ }
1711
+ }
1712
+ result.push({
1713
+ type: "mathBar",
1714
+ position,
1715
+ content: e ? parseMathContent(e) : []
1716
+ });
1717
+ break;
1718
+ }
1719
+ case "box": {
1720
+ const e = findMathChild(child, "e");
1721
+ result.push({
1722
+ type: "mathBox",
1723
+ content: e ? parseMathContent(e) : []
1724
+ });
1725
+ break;
1726
+ }
1727
+ case "eqArr": {
1728
+ const rows = [];
1729
+ for (const eEl of findMathChildren(child, "e")) {
1730
+ rows.push(parseMathContent(eEl));
1731
+ }
1732
+ result.push({ type: "mathEquationArray", rows });
1733
+ break;
1734
+ }
1735
+ // Recurse into oMath elements
1736
+ case "oMath": {
1737
+ result.push(...parseMathContent(child));
1738
+ break;
1739
+ }
1740
+ }
1741
+ }
1742
+ return result;
1743
+ }
1744
+ function parseMathBlock(oMathParaEl) {
1745
+ const content = [];
1746
+ for (const child of oMathParaEl.children) {
1747
+ if (child.type === "element") {
1748
+ const n = child.name.replace(/^m:/, "");
1749
+ if (n === "oMath") {
1750
+ content.push(...parseMathContent(child));
1751
+ }
1752
+ }
1753
+ }
1754
+ return { type: "math", content };
1755
+ }
1756
+ // =============================================================================
1757
+ // TextBox Parser
1758
+ // =============================================================================
1759
+ function parseTextBox(pictEl) {
1760
+ // Look for v:shape > v:textbox > w:txbxContent
1761
+ let txbxContentEl;
1762
+ let shapeEl;
1763
+ for (const child of pictEl.children) {
1764
+ if (child.type === "element" && (child.name === "v:shape" || child.name === "v:rect")) {
1765
+ shapeEl = child;
1766
+ for (const sc of child.children) {
1767
+ if (sc.type === "element" && sc.name === "v:textbox") {
1768
+ for (const tc of sc.children) {
1769
+ if (tc.type === "element" &&
1770
+ (tc.name === "w:txbxContent" || tc.name === "txbxContent")) {
1771
+ txbxContentEl = tc;
1772
+ }
1773
+ }
1774
+ }
1775
+ }
1776
+ }
1777
+ }
1778
+ if (!txbxContentEl) {
1779
+ return undefined;
1780
+ }
1781
+ const paragraphs = [];
1782
+ for (const c of txbxContentEl.children) {
1783
+ if (c.type === "element" && c.name.replace(/^w:/, "") === "p") {
1784
+ paragraphs.push(parseParagraph(c));
1785
+ }
1786
+ }
1787
+ const tb = { type: "textBox", content: paragraphs };
1788
+ if (shapeEl) {
1789
+ const style = shapeEl.attributes["style"];
1790
+ if (style) {
1791
+ tb.style = style;
1792
+ }
1793
+ const sc = shapeEl.attributes["strokecolor"];
1794
+ if (sc) {
1795
+ tb.strokeColor = sc;
1796
+ }
1797
+ const fc = shapeEl.attributes["fillcolor"];
1798
+ if (fc) {
1799
+ tb.fillColor = fc;
1800
+ }
1801
+ if (shapeEl.attributes["stroked"] === "f") {
1802
+ tb.stroke = false;
1803
+ }
1804
+ if (shapeEl.attributes["filled"] === "f") {
1805
+ tb.fill = false;
1806
+ }
1807
+ }
1808
+ return tb;
1809
+ }
1810
+ // =============================================================================
1811
+ // SDT / CheckBox / TOC Parser
1812
+ // =============================================================================
1813
+ function parseSdt(sdtEl) {
1814
+ const sdtPrEl = findChildNs(sdtEl, "sdtPr");
1815
+ const sdtContentEl = findChildNs(sdtEl, "sdtContent");
1816
+ // Check for checkbox (w14:checkbox)
1817
+ if (sdtPrEl) {
1818
+ const checkBoxEl = findChild(sdtPrEl, "w14:checkbox");
1819
+ if (checkBoxEl) {
1820
+ return parseCheckBox(checkBoxEl);
1821
+ }
1822
+ }
1823
+ // Check for TOC (contains docPartObj with docPartGallery "Table of Contents")
1824
+ if (sdtPrEl) {
1825
+ const docPartObjEl = findChildNs(sdtPrEl, "docPartObj");
1826
+ if (docPartObjEl) {
1827
+ const galleryEl = findChildNs(docPartObjEl, "docPartGallery");
1828
+ const galleryVal = galleryEl ? attrVal(galleryEl, "val") : undefined;
1829
+ if (galleryVal === "Table of Contents") {
1830
+ return parseTocFromSdt(sdtContentEl);
1831
+ }
1832
+ }
1833
+ }
1834
+ // Generic SDT
1835
+ const props = {};
1836
+ if (sdtPrEl) {
1837
+ const tagEl = findChildNs(sdtPrEl, "tag");
1838
+ if (tagEl) {
1839
+ props.tag = attrVal(tagEl, "val");
1840
+ }
1841
+ const aliasEl = findChildNs(sdtPrEl, "alias");
1842
+ if (aliasEl) {
1843
+ props.alias = attrVal(aliasEl, "val");
1844
+ }
1845
+ const lockEl = findChildNs(sdtPrEl, "lock");
1846
+ if (lockEl) {
1847
+ const v = attrVal(lockEl, "val");
1848
+ if (v === "contentLocked" || v === "sdtContentLocked") {
1849
+ props.lockContent = true;
1850
+ }
1851
+ if (v === "sdtLocked" || v === "sdtContentLocked") {
1852
+ props.lockSdt = true;
1853
+ }
1854
+ }
1855
+ // Plain text
1856
+ if (findChildNs(sdtPrEl, "text")) {
1857
+ props.plainText = true;
1858
+ }
1859
+ // showingPlcHdr is a toggle, not a property with a val
1860
+ if (findChildNs(sdtPrEl, "showingPlcHdr")) {
1861
+ const v = boolToggle(sdtPrEl, "showingPlcHdr");
1862
+ if (v !== false) {
1863
+ props.showingPlaceholder = true;
1864
+ }
1865
+ }
1866
+ // w15:appearance (replaces the old misused showingPlcHdr)
1867
+ const appearanceEl = findChild(sdtPrEl, "w15:appearance");
1868
+ if (appearanceEl) {
1869
+ const v = appearanceEl.attributes["w15:val"] ?? appearanceEl.attributes["val"];
1870
+ if (v === "boundingBox" || v === "tags" || v === "hidden") {
1871
+ props.appearance = v;
1872
+ }
1873
+ }
1874
+ // Dropdown list
1875
+ const ddlEl = findChildNs(sdtPrEl, "dropDownList");
1876
+ if (ddlEl) {
1877
+ const items = [];
1878
+ for (const li of findChildrenNs(ddlEl, "listItem")) {
1879
+ const item = { value: attrVal(li, "value") ?? "" };
1880
+ const dt = attrVal(li, "displayText");
1881
+ if (dt) {
1882
+ item.displayText = dt;
1883
+ }
1884
+ items.push(item);
1885
+ }
1886
+ props.dropdownList = items;
1887
+ }
1888
+ // ComboBox
1889
+ const cbEl = findChildNs(sdtPrEl, "comboBox");
1890
+ if (cbEl) {
1891
+ const items = [];
1892
+ for (const li of findChildrenNs(cbEl, "listItem")) {
1893
+ const item = { value: attrVal(li, "value") ?? "" };
1894
+ const dt = attrVal(li, "displayText");
1895
+ if (dt) {
1896
+ item.displayText = dt;
1897
+ }
1898
+ items.push(item);
1899
+ }
1900
+ props.comboBox = items;
1901
+ }
1902
+ // Date picker
1903
+ const dateEl = findChildNs(sdtPrEl, "date");
1904
+ if (dateEl) {
1905
+ const dateProp = {};
1906
+ const fullDate = attrVal(dateEl, "fullDate");
1907
+ if (fullDate) {
1908
+ dateProp.fullDate = fullDate;
1909
+ }
1910
+ const dfEl = findChildNs(dateEl, "dateFormat");
1911
+ if (dfEl) {
1912
+ dateProp.dateFormat = attrVal(dfEl, "val");
1913
+ }
1914
+ const lidEl = findChildNs(dateEl, "lid");
1915
+ if (lidEl) {
1916
+ dateProp.lid = attrVal(lidEl, "val");
1917
+ }
1918
+ const storeEl = findChildNs(dateEl, "storeMappedDataAs");
1919
+ if (storeEl) {
1920
+ dateProp.storeMappedDataAs = attrVal(storeEl, "val");
1921
+ }
1922
+ props.date = dateProp;
1923
+ }
1924
+ // ID
1925
+ const idEl = findChildNs(sdtPrEl, "id");
1926
+ if (idEl) {
1927
+ const v = attrInt(idEl, "val");
1928
+ if (v !== undefined) {
1929
+ props.id = v;
1930
+ }
1931
+ }
1932
+ // Data binding
1933
+ const dbEl = findChildNs(sdtPrEl, "dataBinding");
1934
+ if (dbEl) {
1935
+ const xpath = attrVal(dbEl, "xpath");
1936
+ const storeItemId = attrVal(dbEl, "storeItemID");
1937
+ if (xpath && storeItemId) {
1938
+ const binding = { xpath, storeItemId };
1939
+ const prefixMappings = attrVal(dbEl, "prefixMappings");
1940
+ if (prefixMappings) {
1941
+ binding.prefixMappings = prefixMappings;
1942
+ }
1943
+ props.dataBinding = binding;
1944
+ }
1945
+ }
1946
+ // Placeholder
1947
+ const phEl = findChildNs(sdtPrEl, "placeholder");
1948
+ if (phEl) {
1949
+ const docPartEl = findChildNs(phEl, "docPart");
1950
+ if (docPartEl) {
1951
+ props.placeholder = attrVal(docPartEl, "val");
1952
+ }
1953
+ }
1954
+ // Boolean marker elements
1955
+ if (findChildNs(sdtPrEl, "richText")) {
1956
+ props.richText = true;
1957
+ }
1958
+ if (findChildNs(sdtPrEl, "picture")) {
1959
+ props.picture = true;
1960
+ }
1961
+ if (findChildNs(sdtPrEl, "group")) {
1962
+ props.group = true;
1963
+ }
1964
+ if (findChildNs(sdtPrEl, "equation")) {
1965
+ props.equation = true;
1966
+ }
1967
+ if (findChildNs(sdtPrEl, "citation")) {
1968
+ props.citation = true;
1969
+ }
1970
+ if (findChildNs(sdtPrEl, "bibliography")) {
1971
+ props.bibliography = true;
1972
+ }
1973
+ if (findChildNs(sdtPrEl, "temporary")) {
1974
+ props.temporary = true;
1975
+ }
1976
+ // w15: repeating section
1977
+ const rsEl = findChild(sdtPrEl, "w15:repeatingSection");
1978
+ if (rsEl) {
1979
+ const rs = {};
1980
+ // Read from child elements (correct per schema)
1981
+ const titleEl = findChild(rsEl, "w15:sectionTitle");
1982
+ if (titleEl) {
1983
+ const v = titleEl.attributes["w15:val"] ?? titleEl.attributes["val"];
1984
+ if (v !== undefined) {
1985
+ rs.sectionTitle = v;
1986
+ }
1987
+ }
1988
+ if (findChild(rsEl, "w15:doNotAllowInsertDeleteSection")) {
1989
+ rs.allowInsertDelete = false;
1990
+ }
1991
+ // Also accept attribute form for backwards compatibility
1992
+ const stAttr = rsEl.attributes["w15:sectionTitle"];
1993
+ if (stAttr !== undefined && rs.sectionTitle === undefined) {
1994
+ rs.sectionTitle = stAttr;
1995
+ }
1996
+ const noInsDelAttr = rsEl.attributes["w15:doNotAllowInsertDeleteSection"];
1997
+ if (noInsDelAttr !== undefined && rs.allowInsertDelete === undefined) {
1998
+ rs.allowInsertDelete = noInsDelAttr === "0";
1999
+ }
2000
+ props.repeatingSection = rs;
2001
+ }
2002
+ if (findChild(sdtPrEl, "w15:repeatingSectionItem")) {
2003
+ props.repeatingSectionItem = true;
2004
+ }
2005
+ }
2006
+ const content = [];
2007
+ if (sdtContentEl) {
2008
+ for (const child of sdtContentEl.children) {
2009
+ if (child.type !== "element") {
2010
+ continue;
2011
+ }
2012
+ const n = child.name.replace(/^w:/, "");
2013
+ if (n === "p") {
2014
+ content.push(parseParagraph(child));
2015
+ }
2016
+ else if (n === "tbl") {
2017
+ content.push(parseTable(child));
2018
+ }
2019
+ else if (n === "r") {
2020
+ content.push(parseRun(child));
2021
+ }
2022
+ }
2023
+ }
2024
+ return { type: "sdt", properties: props, content };
2025
+ }
2026
+ function parseCheckBox(checkBoxEl) {
2027
+ const cb = { type: "checkBox" };
2028
+ const checkedEl = findChild(checkBoxEl, "w14:checked");
2029
+ if (checkedEl) {
2030
+ const v = checkedEl.attributes["w14:val"] ?? checkedEl.attributes["val"];
2031
+ cb.checked = v === "1" || v === "true";
2032
+ }
2033
+ const checkedStateEl = findChild(checkBoxEl, "w14:checkedState");
2034
+ if (checkedStateEl) {
2035
+ cb.checkedState = {
2036
+ value: checkedStateEl.attributes["w14:val"] ?? checkedStateEl.attributes["val"] ?? "",
2037
+ font: checkedStateEl.attributes["w14:font"] ?? checkedStateEl.attributes["font"]
2038
+ };
2039
+ }
2040
+ const uncheckedStateEl = findChild(checkBoxEl, "w14:uncheckedState");
2041
+ if (uncheckedStateEl) {
2042
+ cb.uncheckedState = {
2043
+ value: uncheckedStateEl.attributes["w14:val"] ?? uncheckedStateEl.attributes["val"] ?? "",
2044
+ font: uncheckedStateEl.attributes["w14:font"] ?? uncheckedStateEl.attributes["font"]
2045
+ };
2046
+ }
2047
+ return cb;
2048
+ }
2049
+ function parseTocFromSdt(sdtContentEl) {
2050
+ const toc = { type: "tableOfContents" };
2051
+ const cachedParagraphs = [];
2052
+ if (sdtContentEl) {
2053
+ // Collect all instrText to assemble the complete TOC field instruction
2054
+ let instrText = "";
2055
+ const collectInstr = (el) => {
2056
+ for (const child of el.children) {
2057
+ if (child.type !== "element") {
2058
+ continue;
2059
+ }
2060
+ const name = child.name.replace(/^w:/, "");
2061
+ if (name === "instrText") {
2062
+ instrText += textContent(child);
2063
+ }
2064
+ else {
2065
+ collectInstr(child);
2066
+ }
2067
+ }
2068
+ };
2069
+ collectInstr(sdtContentEl);
2070
+ if (instrText.trim()) {
2071
+ parseTocInstruction(instrText, toc);
2072
+ }
2073
+ for (const child of sdtContentEl.children) {
2074
+ if (child.type !== "element") {
2075
+ continue;
2076
+ }
2077
+ const n = child.name.replace(/^w:/, "");
2078
+ if (n === "p") {
2079
+ cachedParagraphs.push(parseParagraph(child));
2080
+ }
2081
+ }
2082
+ }
2083
+ if (cachedParagraphs.length > 0) {
2084
+ toc.cachedParagraphs = cachedParagraphs;
2085
+ }
2086
+ return toc;
2087
+ }
2088
+ /** Parse a TOC field instruction string (e.g. `TOC \o "1-3" \h \t "Style,1" \c "Figure"`). */
2089
+ function parseTocInstruction(instr, toc) {
2090
+ const trimmed = instr.trim();
2091
+ if (!/^TOC\b/i.test(trimmed)) {
2092
+ return;
2093
+ }
2094
+ // Match switches: \<letter> followed by either "quoted" or non-quoted non-switch token.
2095
+ // The next-switch boundary must be respected: an unquoted value cannot start with \.
2096
+ const switchRe = /\\(\w)(?:\s+"([^"]*)"|\s+([^\\\s][^\s]*))?/g;
2097
+ let match;
2098
+ while ((match = switchRe.exec(trimmed)) !== null) {
2099
+ const switchName = match[1].toLowerCase();
2100
+ const value = match[2] ?? match[3];
2101
+ switch (switchName) {
2102
+ case "o": // Heading level range e.g. "1-3"
2103
+ if (value) {
2104
+ toc.headingStyleRange = value;
2105
+ }
2106
+ break;
2107
+ case "h": // Hyperlinks
2108
+ toc.hyperlink = true;
2109
+ break;
2110
+ case "c": // Caption label (table of figures)
2111
+ if (value) {
2112
+ toc.captionLabel = value;
2113
+ }
2114
+ break;
2115
+ case "s": // Sequence field identifier
2116
+ if (value) {
2117
+ toc.sequenceFieldIdentifier = value;
2118
+ }
2119
+ break;
2120
+ case "p": // Page-number leader or style separator
2121
+ // In real TOC fields, \p is sometimes used for tab leader.
2122
+ // Common values: "." "-" "_"
2123
+ if (value === "." || value === "-" || value === "_") {
2124
+ toc.leader = "dot";
2125
+ if (value === "-") {
2126
+ toc.leader = "hyphen";
2127
+ }
2128
+ else if (value === "_") {
2129
+ toc.leader = "underscore";
2130
+ }
2131
+ }
2132
+ break;
2133
+ case "t": {
2134
+ // Styles with levels: "StyleName1,Level1;StyleName2,Level2;..."
2135
+ if (!value) {
2136
+ break;
2137
+ }
2138
+ const items = [];
2139
+ for (const part of value.split(";")) {
2140
+ const [styleName, levelStr] = part.split(",");
2141
+ if (styleName && levelStr) {
2142
+ items.push({ styleName: styleName.trim(), level: parseInt(levelStr, 10) });
2143
+ }
2144
+ }
2145
+ if (items.length > 0) {
2146
+ toc.stylesWithLevels = items;
2147
+ }
2148
+ break;
2149
+ }
2150
+ }
2151
+ }
2152
+ }
2153
+ // =============================================================================
2154
+ // Paragraph Parser
2155
+ // =============================================================================
2156
+ function parseRun(el) {
2157
+ const rPrEl = findChildNs(el, "rPr");
2158
+ return {
2159
+ properties: rPrEl ? parseRunProperties(rPrEl) : undefined,
2160
+ content: parseRunContent(el)
2161
+ };
2162
+ }
2163
+ function parseParagraph(pEl) {
2164
+ const pPrEl = findChildNs(pEl, "pPr");
2165
+ const children = [];
2166
+ // fldChar state machine: tracks field code assembly across runs
2167
+ let fieldState = "none";
2168
+ let fieldInstr = "";
2169
+ let fieldCached = "";
2170
+ let fieldRunProps;
2171
+ let fieldFormField;
2172
+ for (const child of pEl.children) {
2173
+ if (child.type !== "element") {
2174
+ continue;
2175
+ }
2176
+ // Handle mc:AlternateContent — pick mc:Choice, fall back to mc:Fallback
2177
+ let resolved = child;
2178
+ if (child.name === "mc:AlternateContent") {
2179
+ const choice = findChild(child, "mc:Choice");
2180
+ const fallback = findChild(child, "mc:Fallback");
2181
+ const chosen = choice ?? fallback;
2182
+ if (chosen && chosen.children.length > 0) {
2183
+ // The first element child inside Choice/Fallback is the real element
2184
+ const inner = chosen.children.find(c => c.type === "element");
2185
+ if (inner) {
2186
+ resolved = inner;
2187
+ }
2188
+ else {
2189
+ continue;
2190
+ }
2191
+ }
2192
+ else {
2193
+ continue;
2194
+ }
2195
+ }
2196
+ const name = resolved.name.replace(/^w:/, "");
2197
+ switch (name) {
2198
+ case "r": {
2199
+ // Check for fldChar and instrText inside the run
2200
+ let hasFldChar = false;
2201
+ for (const rc of resolved.children) {
2202
+ if (rc.type !== "element") {
2203
+ continue;
2204
+ }
2205
+ const rcName = rc.name.replace(/^w:/, "");
2206
+ if (rcName === "fldChar") {
2207
+ hasFldChar = true;
2208
+ const fldCharType = attrVal(rc, "fldCharType");
2209
+ if (fldCharType === "begin") {
2210
+ fieldState = "instrText";
2211
+ fieldInstr = "";
2212
+ fieldCached = "";
2213
+ // Capture run properties from this run for the field
2214
+ const rPrEl = findChildNs(resolved, "rPr");
2215
+ fieldRunProps = rPrEl ? parseRunProperties(rPrEl) : undefined;
2216
+ // Parse ffData for legacy form fields
2217
+ const ffDataEl = findChildNs(rc, "ffData");
2218
+ if (ffDataEl) {
2219
+ fieldFormField = parseFfData(ffDataEl);
2220
+ }
2221
+ else {
2222
+ fieldFormField = undefined;
2223
+ }
2224
+ }
2225
+ else if (fldCharType === "separate") {
2226
+ fieldState = "cached";
2227
+ }
2228
+ else if (fldCharType === "end") {
2229
+ // Emit the assembled field as a Run with FieldContent
2230
+ const fc = {
2231
+ type: "field",
2232
+ instruction: fieldInstr.trim(),
2233
+ cachedValue: fieldCached || undefined,
2234
+ formField: fieldFormField
2235
+ };
2236
+ children.push({
2237
+ properties: fieldRunProps,
2238
+ content: [fc]
2239
+ });
2240
+ fieldState = "none";
2241
+ fieldInstr = "";
2242
+ fieldCached = "";
2243
+ fieldRunProps = undefined;
2244
+ }
2245
+ }
2246
+ else if (rcName === "instrText" && fieldState === "instrText") {
2247
+ hasFldChar = true;
2248
+ fieldInstr += textContent(rc);
2249
+ }
2250
+ }
2251
+ if (fieldState === "cached") {
2252
+ // Collect cached text from this run
2253
+ for (const rc of resolved.children) {
2254
+ if (rc.type !== "element") {
2255
+ continue;
2256
+ }
2257
+ const rcName = rc.name.replace(/^w:/, "");
2258
+ if (rcName === "t") {
2259
+ fieldCached += textContent(rc);
2260
+ }
2261
+ else if (rcName === "fldChar") {
2262
+ // Already handled above
2263
+ }
2264
+ }
2265
+ if (!hasFldChar) {
2266
+ continue; // Skip adding this run normally
2267
+ }
2268
+ }
2269
+ if (fieldState === "instrText" && hasFldChar) {
2270
+ continue; // Don't add begin/instrText runs as normal content
2271
+ }
2272
+ if (fieldState === "none" && !hasFldChar) {
2273
+ children.push(parseRun(resolved));
2274
+ }
2275
+ break;
2276
+ }
2277
+ case "fldSimple": {
2278
+ // Simple field: <w:fldSimple w:instr=" PAGE "><w:r>...</w:r></w:fldSimple>
2279
+ const instr = attrVal(resolved, "instr") ?? "";
2280
+ let cached = "";
2281
+ for (const fc of resolved.children) {
2282
+ if (fc.type === "element" && fc.name.replace(/^w:/, "") === "r") {
2283
+ for (const rc of fc.children) {
2284
+ if (rc.type === "element" && rc.name.replace(/^w:/, "") === "t") {
2285
+ cached += textContent(rc);
2286
+ }
2287
+ }
2288
+ }
2289
+ }
2290
+ const fc = {
2291
+ type: "field",
2292
+ instruction: instr.trim(),
2293
+ cachedValue: cached || undefined
2294
+ };
2295
+ children.push({
2296
+ properties: undefined,
2297
+ content: [fc]
2298
+ });
2299
+ break;
2300
+ }
2301
+ case "hyperlink": {
2302
+ const rId = resolved.attributes["r:id"];
2303
+ const anchor = resolved.attributes["w:anchor"] ?? resolved.attributes["anchor"];
2304
+ const tooltip = resolved.attributes["w:tooltip"] ?? resolved.attributes["tooltip"];
2305
+ const historyAttr = resolved.attributes["w:history"] ?? resolved.attributes["history"];
2306
+ const tgtFrame = resolved.attributes["w:tgtFrame"] ?? resolved.attributes["tgtFrame"];
2307
+ const docLocation = resolved.attributes["w:docLocation"] ?? resolved.attributes["docLocation"];
2308
+ const hRuns = [];
2309
+ for (const hChild of resolved.children) {
2310
+ if (hChild.type === "element" && hChild.name.replace(/^w:/, "") === "r") {
2311
+ hRuns.push(parseRun(hChild));
2312
+ }
2313
+ }
2314
+ // Resolve URL from relMap
2315
+ let url;
2316
+ if (rId) {
2317
+ const rel = _parseRelMap.get(rId);
2318
+ if (rel && rel.targetMode === "External") {
2319
+ url = rel.target;
2320
+ }
2321
+ }
2322
+ const hyperlink = {
2323
+ type: "hyperlink",
2324
+ rId,
2325
+ anchor,
2326
+ url,
2327
+ tooltip,
2328
+ children: hRuns
2329
+ };
2330
+ if (historyAttr === "1" || historyAttr === "true") {
2331
+ hyperlink.history = true;
2332
+ }
2333
+ if (tgtFrame) {
2334
+ hyperlink.tgtFrame = tgtFrame;
2335
+ }
2336
+ if (docLocation) {
2337
+ hyperlink.docLocation = docLocation;
2338
+ }
2339
+ children.push(hyperlink);
2340
+ break;
2341
+ }
2342
+ case "bookmarkStart": {
2343
+ const bm = {
2344
+ type: "bookmarkStart",
2345
+ id: parseInt(resolved.attributes["w:id"] ?? resolved.attributes["id"] ?? "0", 10),
2346
+ name: resolved.attributes["w:name"] ?? resolved.attributes["name"] ?? ""
2347
+ };
2348
+ const colFirst = resolved.attributes["w:colFirst"] ?? resolved.attributes["colFirst"];
2349
+ if (colFirst !== undefined) {
2350
+ bm.colFirst = parseInt(colFirst, 10);
2351
+ }
2352
+ const colLast = resolved.attributes["w:colLast"] ?? resolved.attributes["colLast"];
2353
+ if (colLast !== undefined) {
2354
+ bm.colLast = parseInt(colLast, 10);
2355
+ }
2356
+ const dcx = resolved.attributes["w:displacedByCustomXml"] ??
2357
+ resolved.attributes["displacedByCustomXml"];
2358
+ if (dcx === "next" || dcx === "prev") {
2359
+ bm.displacedByCustomXml = dcx;
2360
+ }
2361
+ children.push(bm);
2362
+ break;
2363
+ }
2364
+ case "bookmarkEnd":
2365
+ children.push({
2366
+ type: "bookmarkEnd",
2367
+ id: parseInt(resolved.attributes["w:id"] ?? resolved.attributes["id"] ?? "0", 10)
2368
+ });
2369
+ break;
2370
+ case "commentRangeStart":
2371
+ children.push({
2372
+ type: "commentRangeStart",
2373
+ id: parseInt(resolved.attributes["w:id"] ?? resolved.attributes["id"] ?? "0", 10)
2374
+ });
2375
+ break;
2376
+ case "commentRangeEnd":
2377
+ children.push({
2378
+ type: "commentRangeEnd",
2379
+ id: parseInt(resolved.attributes["w:id"] ?? resolved.attributes["id"] ?? "0", 10)
2380
+ });
2381
+ break;
2382
+ case "commentReference":
2383
+ children.push({
2384
+ type: "commentReference",
2385
+ id: parseInt(resolved.attributes["w:id"] ?? resolved.attributes["id"] ?? "0", 10)
2386
+ });
2387
+ break;
2388
+ case "ins": {
2389
+ // Inserted run (track changes)
2390
+ const rev = parseRevisionInfo(resolved);
2391
+ if (rev) {
2392
+ for (const insChild of resolved.children) {
2393
+ if (insChild.type === "element" && insChild.name.replace(/^w:/, "") === "r") {
2394
+ children.push({
2395
+ type: "insertedRun",
2396
+ revision: rev,
2397
+ run: parseRun(insChild)
2398
+ });
2399
+ }
2400
+ }
2401
+ }
2402
+ break;
2403
+ }
2404
+ case "del": {
2405
+ // Deleted run (track changes)
2406
+ const rev = parseRevisionInfo(resolved);
2407
+ if (rev) {
2408
+ for (const delChild of resolved.children) {
2409
+ if (delChild.type === "element" && delChild.name.replace(/^w:/, "") === "r") {
2410
+ children.push({
2411
+ type: "deletedRun",
2412
+ revision: rev,
2413
+ run: parseDeletedRun(delChild)
2414
+ });
2415
+ }
2416
+ }
2417
+ }
2418
+ break;
2419
+ }
2420
+ case "moveFrom": {
2421
+ const rev = parseRevisionInfo(resolved);
2422
+ if (rev) {
2423
+ for (const mfChild of resolved.children) {
2424
+ if (mfChild.type === "element" && mfChild.name.replace(/^w:/, "") === "r") {
2425
+ children.push({
2426
+ type: "movedFromRun",
2427
+ revision: rev,
2428
+ run: parseRun(mfChild)
2429
+ });
2430
+ }
2431
+ }
2432
+ }
2433
+ break;
2434
+ }
2435
+ case "moveTo": {
2436
+ const rev = parseRevisionInfo(resolved);
2437
+ if (rev) {
2438
+ for (const mtChild of resolved.children) {
2439
+ if (mtChild.type === "element" && mtChild.name.replace(/^w:/, "") === "r") {
2440
+ children.push({
2441
+ type: "movedToRun",
2442
+ revision: rev,
2443
+ run: parseRun(mtChild)
2444
+ });
2445
+ }
2446
+ }
2447
+ }
2448
+ break;
2449
+ }
2450
+ case "moveFromRangeStart":
2451
+ case "moveFromRangeEnd":
2452
+ case "moveToRangeStart":
2453
+ case "moveToRangeEnd": {
2454
+ const id = attrInt(resolved, "id");
2455
+ if (id !== undefined) {
2456
+ const marker = {
2457
+ type: name,
2458
+ id
2459
+ };
2460
+ const author = attrVal(resolved, "author");
2461
+ if (author) {
2462
+ marker.author = author;
2463
+ }
2464
+ const date = attrVal(resolved, "date");
2465
+ if (date) {
2466
+ marker.date = date;
2467
+ }
2468
+ const mName = attrVal(resolved, "name");
2469
+ if (mName) {
2470
+ marker.name = mName;
2471
+ }
2472
+ children.push(marker);
2473
+ }
2474
+ break;
2475
+ }
2476
+ case "customXmlInsRangeStart":
2477
+ case "customXmlInsRangeEnd":
2478
+ case "customXmlDelRangeStart":
2479
+ case "customXmlDelRangeEnd":
2480
+ case "customXmlMoveFromRangeStart":
2481
+ case "customXmlMoveFromRangeEnd":
2482
+ case "customXmlMoveToRangeStart":
2483
+ case "customXmlMoveToRangeEnd": {
2484
+ const id = attrInt(resolved, "id");
2485
+ if (id !== undefined) {
2486
+ const marker = { type: name, id };
2487
+ const author = attrVal(resolved, "author");
2488
+ if (author) {
2489
+ marker.author = author;
2490
+ }
2491
+ const date = attrVal(resolved, "date");
2492
+ if (date) {
2493
+ marker.date = date;
2494
+ }
2495
+ children.push(marker);
2496
+ }
2497
+ break;
2498
+ }
2499
+ case "smartTag":
2500
+ case "customXml":
2501
+ case "dir": {
2502
+ // Semantic wrappers: flatten their children into the current paragraph.
2503
+ // A smartTag/customXml/dir can contain runs, hyperlinks, nested wrappers, etc.
2504
+ // Re-use parseParagraph to recursively parse the contained children.
2505
+ const subPara = parseParagraph(resolved);
2506
+ for (const sub of subPara.children) {
2507
+ children.push(sub);
2508
+ }
2509
+ break;
2510
+ }
2511
+ case "proofErr":
2512
+ case "permStart":
2513
+ case "permEnd":
2514
+ case "lastRenderedPageBreak":
2515
+ // Non-semantic markers; safely ignored
2516
+ break;
2517
+ }
2518
+ }
2519
+ const paraId = pEl.attributes["w14:paraId"];
2520
+ const textId = pEl.attributes["w14:textId"];
2521
+ const result = {
2522
+ type: "paragraph",
2523
+ properties: pPrEl ? parseParagraphProperties(pPrEl) : undefined,
2524
+ children
2525
+ };
2526
+ if (paraId) {
2527
+ result.paraId = paraId;
2528
+ }
2529
+ if (textId) {
2530
+ result.textId = textId;
2531
+ }
2532
+ return result;
2533
+ }
2534
+ /** Parse a deleted run (w:delText instead of w:t). */
2535
+ function parseDeletedRun(el) {
2536
+ const rPrEl = findChildNs(el, "rPr");
2537
+ const content = [];
2538
+ for (const child of el.children) {
2539
+ if (child.type !== "element") {
2540
+ continue;
2541
+ }
2542
+ const name = child.name.replace(/^w:/, "");
2543
+ if (name === "delText") {
2544
+ content.push({ type: "text", text: textContent(child) });
2545
+ }
2546
+ else if (name === "t") {
2547
+ content.push({ type: "text", text: textContent(child) });
2548
+ }
2549
+ else if (name === "br") {
2550
+ content.push({ type: "break", breakType: attrVal(child, "type") });
2551
+ }
2552
+ else if (name === "tab") {
2553
+ content.push({ type: "tab" });
2554
+ }
2555
+ }
2556
+ return {
2557
+ properties: rPrEl ? parseRunProperties(rPrEl) : undefined,
2558
+ content
2559
+ };
2560
+ }
2561
+ // =============================================================================
2562
+ // Table Parser
2563
+ // =============================================================================
2564
+ function parseTableBorders(el) {
2565
+ const borders = {};
2566
+ for (const side of [
2567
+ "top",
2568
+ "left",
2569
+ "bottom",
2570
+ "right",
2571
+ "insideH",
2572
+ "insideV",
2573
+ "start",
2574
+ "end",
2575
+ "tl2br",
2576
+ "tr2bl"
2577
+ ]) {
2578
+ const sideEl = findChildNs(el, side);
2579
+ if (sideEl) {
2580
+ borders[side] = parseBorder(sideEl);
2581
+ }
2582
+ }
2583
+ return borders;
2584
+ }
2585
+ function parseTableCellMargins(el) {
2586
+ const margins = {};
2587
+ for (const side of ["top", "left", "bottom", "right", "start", "end"]) {
2588
+ const sideEl = findChildNs(el, side);
2589
+ if (sideEl) {
2590
+ margins[side] = parseTableWidth(sideEl);
2591
+ }
2592
+ }
2593
+ return margins;
2594
+ }
2595
+ function parseTableProperties(el) {
2596
+ const props = {};
2597
+ const styleEl = findChildNs(el, "tblStyle");
2598
+ if (styleEl) {
2599
+ props.style = attrVal(styleEl, "val");
2600
+ }
2601
+ const wEl = findChildNs(el, "tblW");
2602
+ if (wEl) {
2603
+ props.width = parseTableWidth(wEl);
2604
+ }
2605
+ const jcEl = findChildNs(el, "jc");
2606
+ if (jcEl) {
2607
+ props.alignment = attrVal(jcEl, "val");
2608
+ }
2609
+ const indEl = findChildNs(el, "tblInd");
2610
+ if (indEl) {
2611
+ props.indent = parseInt(indEl.attributes["w:w"] ?? indEl.attributes["w"] ?? "0", 10);
2612
+ }
2613
+ const bordersEl = findChildNs(el, "tblBorders");
2614
+ if (bordersEl) {
2615
+ props.borders = parseTableBorders(bordersEl);
2616
+ }
2617
+ const layoutEl = findChildNs(el, "tblLayout");
2618
+ if (layoutEl) {
2619
+ props.layout = attrVal(layoutEl, "type");
2620
+ }
2621
+ const cellMarEl = findChildNs(el, "tblCellMar");
2622
+ if (cellMarEl) {
2623
+ props.cellMargins = parseTableCellMargins(cellMarEl);
2624
+ }
2625
+ // TableLook
2626
+ const lookEl = findChildNs(el, "tblLook");
2627
+ if (lookEl) {
2628
+ const look = {};
2629
+ // Read individual attributes first (authoritative when explicit "0"/"1")
2630
+ const readFlag = (name) => {
2631
+ const v = attrVal(lookEl, name);
2632
+ if (v === "1" || v === "true") {
2633
+ return true;
2634
+ }
2635
+ if (v === "0" || v === "false") {
2636
+ return false;
2637
+ }
2638
+ return undefined;
2639
+ };
2640
+ const firstRow = readFlag("firstRow");
2641
+ const lastRow = readFlag("lastRow");
2642
+ const firstColumn = readFlag("firstColumn");
2643
+ const lastColumn = readFlag("lastColumn");
2644
+ const noHBand = readFlag("noHBand");
2645
+ const noVBand = readFlag("noVBand");
2646
+ if (firstRow !== undefined) {
2647
+ look.firstRow = firstRow;
2648
+ }
2649
+ if (lastRow !== undefined) {
2650
+ look.lastRow = lastRow;
2651
+ }
2652
+ if (firstColumn !== undefined) {
2653
+ look.firstColumn = firstColumn;
2654
+ }
2655
+ if (lastColumn !== undefined) {
2656
+ look.lastColumn = lastColumn;
2657
+ }
2658
+ if (noHBand !== undefined) {
2659
+ look.noHBand = noHBand;
2660
+ }
2661
+ if (noVBand !== undefined) {
2662
+ look.noVBand = noVBand;
2663
+ }
2664
+ // Fall back to w:val bitmask ONLY if no individual attrs were specified
2665
+ if (Object.keys(look).length === 0) {
2666
+ const val = attrVal(lookEl, "val");
2667
+ if (val) {
2668
+ const v = parseInt(val, 16);
2669
+ if (v & 0x0020) {
2670
+ look.firstRow = true;
2671
+ }
2672
+ if (v & 0x0040) {
2673
+ look.lastRow = true;
2674
+ }
2675
+ if (v & 0x0080) {
2676
+ look.firstColumn = true;
2677
+ }
2678
+ if (v & 0x0100) {
2679
+ look.lastColumn = true;
2680
+ }
2681
+ if (v & 0x0200) {
2682
+ look.noHBand = true;
2683
+ }
2684
+ if (v & 0x0400) {
2685
+ look.noVBand = true;
2686
+ }
2687
+ }
2688
+ }
2689
+ if (Object.keys(look).length > 0) {
2690
+ props.look = look;
2691
+ }
2692
+ }
2693
+ // TableFloat
2694
+ const tblpPrEl = findChildNs(el, "tblpPr");
2695
+ if (tblpPrEl) {
2696
+ const tf = {};
2697
+ const f = tf;
2698
+ const hAnchor = attrVal(tblpPrEl, "horzAnchor");
2699
+ if (hAnchor) {
2700
+ f.horizontalAnchor = hAnchor;
2701
+ }
2702
+ const vAnchor = attrVal(tblpPrEl, "vertAnchor");
2703
+ if (vAnchor) {
2704
+ f.verticalAnchor = vAnchor;
2705
+ }
2706
+ const tblpX = attrInt(tblpPrEl, "tblpX");
2707
+ if (tblpX !== undefined) {
2708
+ f.absoluteHorizontalPosition = tblpX;
2709
+ }
2710
+ const tblpY = attrInt(tblpPrEl, "tblpY");
2711
+ if (tblpY !== undefined) {
2712
+ f.absoluteVerticalPosition = tblpY;
2713
+ }
2714
+ const tblpXSpec = attrVal(tblpPrEl, "tblpXSpec");
2715
+ if (tblpXSpec) {
2716
+ f.relativeHorizontalPosition = tblpXSpec;
2717
+ }
2718
+ const tblpYSpec = attrVal(tblpPrEl, "tblpYSpec");
2719
+ if (tblpYSpec) {
2720
+ f.relativeVerticalPosition = tblpYSpec;
2721
+ }
2722
+ const topFromText = attrInt(tblpPrEl, "topFromText");
2723
+ if (topFromText !== undefined) {
2724
+ f.topFromText = topFromText;
2725
+ }
2726
+ const bottomFromText = attrInt(tblpPrEl, "bottomFromText");
2727
+ if (bottomFromText !== undefined) {
2728
+ f.bottomFromText = bottomFromText;
2729
+ }
2730
+ const leftFromText = attrInt(tblpPrEl, "leftFromText");
2731
+ if (leftFromText !== undefined) {
2732
+ f.leftFromText = leftFromText;
2733
+ }
2734
+ const rightFromText = attrInt(tblpPrEl, "rightFromText");
2735
+ if (rightFromText !== undefined) {
2736
+ f.rightFromText = rightFromText;
2737
+ }
2738
+ const overlap = attrVal(tblpPrEl, "overlap");
2739
+ if (overlap) {
2740
+ f.overlap = overlap;
2741
+ }
2742
+ props.float = tf;
2743
+ }
2744
+ // w:tblOverlap is a separate sibling element of w:tblpPr (value "never"|"overlap")
2745
+ const tblOverlapEl = findChildNs(el, "tblOverlap");
2746
+ if (tblOverlapEl && props.float) {
2747
+ const v = attrVal(tblOverlapEl, "val");
2748
+ if (v === "never" || v === "overlap") {
2749
+ props.float.overlap = v;
2750
+ }
2751
+ }
2752
+ // Cell spacing
2753
+ const csEl = findChildNs(el, "tblCellSpacing");
2754
+ if (csEl) {
2755
+ props.cellSpacing = parseTableWidth(csEl);
2756
+ }
2757
+ // Bidi visual
2758
+ if (findChildNs(el, "bidiVisual")) {
2759
+ props.visuallyRightToLeft = true;
2760
+ }
2761
+ // Shading
2762
+ const shdEl = findChildNs(el, "shd");
2763
+ if (shdEl) {
2764
+ props.shading = parseShading(shdEl);
2765
+ }
2766
+ // Accessibility: caption and description
2767
+ const captionEl = findChildNs(el, "tblCaption");
2768
+ if (captionEl) {
2769
+ props.caption = attrVal(captionEl, "val");
2770
+ }
2771
+ const descEl = findChildNs(el, "tblDescription");
2772
+ if (descEl) {
2773
+ props.description = attrVal(descEl, "val");
2774
+ }
2775
+ // Table property change
2776
+ const tblPrChangeEl = findChildNs(el, "tblPrChange");
2777
+ if (tblPrChangeEl) {
2778
+ const rev = parseRevisionInfo(tblPrChangeEl);
2779
+ if (rev) {
2780
+ const prev = findChildNs(tblPrChangeEl, "tblPr");
2781
+ props.propertyChange = {
2782
+ revision: rev,
2783
+ previousProperties: prev ? parseTableProperties(prev) : undefined
2784
+ };
2785
+ }
2786
+ }
2787
+ return props;
2788
+ }
2789
+ function parseTableCell(el) {
2790
+ const tcPrEl = findChildNs(el, "tcPr");
2791
+ const content = [];
2792
+ for (const child of el.children) {
2793
+ if (child.type !== "element") {
2794
+ continue;
2795
+ }
2796
+ const name = child.name.replace(/^w:/, "");
2797
+ if (name === "p") {
2798
+ content.push(parseParagraph(child));
2799
+ }
2800
+ else if (name === "tbl") {
2801
+ content.push(parseTable(child));
2802
+ }
2803
+ }
2804
+ let props;
2805
+ if (tcPrEl) {
2806
+ const p = {};
2807
+ const wEl = findChildNs(tcPrEl, "tcW");
2808
+ if (wEl) {
2809
+ p.width = parseTableWidth(wEl);
2810
+ }
2811
+ const gsEl = findChildNs(tcPrEl, "gridSpan");
2812
+ if (gsEl) {
2813
+ p.gridSpan = attrInt(gsEl, "val");
2814
+ }
2815
+ const vmEl = findChildNs(tcPrEl, "vMerge");
2816
+ if (vmEl) {
2817
+ p.verticalMerge = attrVal(vmEl, "val") ?? "continue";
2818
+ }
2819
+ const bordersEl = findChildNs(tcPrEl, "tcBorders");
2820
+ if (bordersEl) {
2821
+ p.borders = parseTableBorders(bordersEl);
2822
+ }
2823
+ const shdEl = findChildNs(tcPrEl, "shd");
2824
+ if (shdEl) {
2825
+ p.shading = parseShading(shdEl);
2826
+ }
2827
+ const vAlignEl = findChildNs(tcPrEl, "vAlign");
2828
+ if (vAlignEl) {
2829
+ p.verticalAlign = attrVal(vAlignEl, "val");
2830
+ }
2831
+ if (findChildNs(tcPrEl, "noWrap")) {
2832
+ p.noWrap = true;
2833
+ }
2834
+ const textDirEl = findChildNs(tcPrEl, "textDirection");
2835
+ if (textDirEl) {
2836
+ p.textDirection = attrVal(textDirEl, "val");
2837
+ }
2838
+ const marginsEl = findChildNs(tcPrEl, "tcMar");
2839
+ if (marginsEl) {
2840
+ p.margins = parseTableCellMargins(marginsEl);
2841
+ }
2842
+ // Conditional formatting
2843
+ const cnfEl = findChildNs(tcPrEl, "cnfStyle");
2844
+ if (cnfEl) {
2845
+ p.cnfStyle = attrVal(cnfEl, "val");
2846
+ }
2847
+ // Hide cell end-of-cell marker
2848
+ if (findChildNs(tcPrEl, "hideMark")) {
2849
+ p.hideMark = true;
2850
+ }
2851
+ // Fit text
2852
+ if (findChildNs(tcPrEl, "tcFitText")) {
2853
+ p.fitText = true;
2854
+ }
2855
+ // Cell-level revisions
2856
+ const cellInsEl = findChildNs(tcPrEl, "cellIns");
2857
+ if (cellInsEl) {
2858
+ const rev = parseRevisionInfo(cellInsEl);
2859
+ if (rev) {
2860
+ p.inserted = { revision: rev };
2861
+ }
2862
+ }
2863
+ const cellDelEl = findChildNs(tcPrEl, "cellDel");
2864
+ if (cellDelEl) {
2865
+ const rev = parseRevisionInfo(cellDelEl);
2866
+ if (rev) {
2867
+ p.deleted = { revision: rev };
2868
+ }
2869
+ }
2870
+ const cellMergeEl = findChildNs(tcPrEl, "cellMerge");
2871
+ if (cellMergeEl) {
2872
+ const vMerge = attrVal(cellMergeEl, "vMerge");
2873
+ const rev = parseRevisionInfo(cellMergeEl);
2874
+ if (rev && (vMerge === "cont" || vMerge === "rest")) {
2875
+ p.cellMerge = { vMerge, revision: rev };
2876
+ }
2877
+ }
2878
+ // tcPrChange
2879
+ const tcPrChangeEl = findChildNs(tcPrEl, "tcPrChange");
2880
+ if (tcPrChangeEl) {
2881
+ const rev = parseRevisionInfo(tcPrChangeEl);
2882
+ if (rev) {
2883
+ const prev = findChildNs(tcPrChangeEl, "tcPr");
2884
+ p.propertyChange = { revision: rev };
2885
+ if (prev) {
2886
+ // Minimal: previousProperties won't recurse (avoid infinite recursion).
2887
+ // Just capture the presence of the change marker here.
2888
+ }
2889
+ }
2890
+ }
2891
+ props = p;
2892
+ }
2893
+ return { properties: props, content };
2894
+ }
2895
+ function parseTableRow(el) {
2896
+ const trPrEl = findChildNs(el, "trPr");
2897
+ const tblPrExEl = findChildNs(el, "tblPrEx");
2898
+ const cells = [];
2899
+ for (const child of el.children) {
2900
+ if (child.type === "element" && child.name.replace(/^w:/, "") === "tc") {
2901
+ cells.push(parseTableCell(child));
2902
+ }
2903
+ }
2904
+ let props;
2905
+ if (trPrEl || tblPrExEl) {
2906
+ const p = {};
2907
+ if (tblPrExEl) {
2908
+ p.tblPrEx = parseTableProperties(tblPrExEl);
2909
+ }
2910
+ if (trPrEl) {
2911
+ const heightEl = findChildNs(trPrEl, "trHeight");
2912
+ if (heightEl) {
2913
+ p.height = {
2914
+ value: attrInt(heightEl, "val") ?? 0,
2915
+ rule: attrVal(heightEl, "hRule")
2916
+ };
2917
+ }
2918
+ if (findChildNs(trPrEl, "tblHeader")) {
2919
+ p.tableHeader = true;
2920
+ }
2921
+ if (findChildNs(trPrEl, "cantSplit")) {
2922
+ p.cantSplit = true;
2923
+ }
2924
+ if (findChildNs(trPrEl, "hidden")) {
2925
+ p.hidden = true;
2926
+ }
2927
+ const csEl = findChildNs(trPrEl, "tblCellSpacing");
2928
+ if (csEl) {
2929
+ p.cellSpacing = parseTableWidth(csEl);
2930
+ }
2931
+ const insEl = findChildNs(trPrEl, "ins");
2932
+ if (insEl) {
2933
+ const rev = parseRevisionInfo(insEl);
2934
+ if (rev) {
2935
+ p.inserted = { revision: rev };
2936
+ }
2937
+ }
2938
+ const delEl = findChildNs(trPrEl, "del");
2939
+ if (delEl) {
2940
+ const rev = parseRevisionInfo(delEl);
2941
+ if (rev) {
2942
+ p.deleted = { revision: rev };
2943
+ }
2944
+ }
2945
+ const gbEl = findChildNs(trPrEl, "gridBefore");
2946
+ if (gbEl) {
2947
+ p.gridBefore = attrInt(gbEl, "val");
2948
+ }
2949
+ const gaEl = findChildNs(trPrEl, "gridAfter");
2950
+ if (gaEl) {
2951
+ p.gridAfter = attrInt(gaEl, "val");
2952
+ }
2953
+ const wbEl = findChildNs(trPrEl, "wBefore");
2954
+ if (wbEl) {
2955
+ p.widthBefore = parseTableWidth(wbEl);
2956
+ }
2957
+ const waEl = findChildNs(trPrEl, "wAfter");
2958
+ if (waEl) {
2959
+ p.widthAfter = parseTableWidth(waEl);
2960
+ }
2961
+ const cnfEl = findChildNs(trPrEl, "cnfStyle");
2962
+ if (cnfEl) {
2963
+ p.cnfStyle = attrVal(cnfEl, "val");
2964
+ }
2965
+ const trPrChangeEl = findChildNs(trPrEl, "trPrChange");
2966
+ if (trPrChangeEl) {
2967
+ const rev = parseRevisionInfo(trPrChangeEl);
2968
+ if (rev) {
2969
+ const prevTrPr = findChildNs(trPrChangeEl, "trPr");
2970
+ p.propertyChange = {
2971
+ revision: rev,
2972
+ previousProperties: prevTrPr ? parseRowPrInner(prevTrPr) : undefined
2973
+ };
2974
+ }
2975
+ }
2976
+ }
2977
+ props = p;
2978
+ }
2979
+ return { properties: props, cells };
2980
+ }
2981
+ /** Inner parse for row properties content (used by propertyChange recursion). */
2982
+ function parseRowPrInner(trPrEl) {
2983
+ const p = {};
2984
+ const heightEl = findChildNs(trPrEl, "trHeight");
2985
+ if (heightEl) {
2986
+ p.height = { value: attrInt(heightEl, "val") ?? 0, rule: attrVal(heightEl, "hRule") };
2987
+ }
2988
+ if (findChildNs(trPrEl, "tblHeader")) {
2989
+ p.tableHeader = true;
2990
+ }
2991
+ if (findChildNs(trPrEl, "cantSplit")) {
2992
+ p.cantSplit = true;
2993
+ }
2994
+ return p;
2995
+ }
2996
+ function parseTable(tblEl) {
2997
+ const tblPrEl = findChildNs(tblEl, "tblPr");
2998
+ const gridEl = findChildNs(tblEl, "tblGrid");
2999
+ const rows = [];
3000
+ for (const child of tblEl.children) {
3001
+ if (child.type === "element" && child.name.replace(/^w:/, "") === "tr") {
3002
+ rows.push(parseTableRow(child));
3003
+ }
3004
+ }
3005
+ let columnWidths;
3006
+ if (gridEl) {
3007
+ columnWidths = [];
3008
+ for (const col of findChildrenNs(gridEl, "gridCol")) {
3009
+ columnWidths.push(parseInt(col.attributes["w:w"] ?? col.attributes["w"] ?? "0", 10));
3010
+ }
3011
+ }
3012
+ return {
3013
+ type: "table",
3014
+ properties: tblPrEl ? parseTableProperties(tblPrEl) : undefined,
3015
+ columnWidths,
3016
+ rows
3017
+ };
3018
+ }
3019
+ // =============================================================================
3020
+ // Section Properties Parser
3021
+ // =============================================================================
3022
+ function parseSectionProperties(sectPrEl) {
3023
+ const sect = {};
3024
+ const pgSzEl = findChildNs(sectPrEl, "pgSz");
3025
+ if (pgSzEl) {
3026
+ // Per ECMA-376, w:orient defaults to "portrait" when absent
3027
+ const orient = attrVal(pgSzEl, "orient");
3028
+ sect.pageSize = {
3029
+ width: attrInt(pgSzEl, "w"),
3030
+ height: attrInt(pgSzEl, "h"),
3031
+ orientation: (orient === "landscape" ? "landscape" : "portrait")
3032
+ };
3033
+ }
3034
+ const pgMarEl = findChildNs(sectPrEl, "pgMar");
3035
+ if (pgMarEl) {
3036
+ sect.margins = {
3037
+ top: attrInt(pgMarEl, "top"),
3038
+ right: attrInt(pgMarEl, "right"),
3039
+ bottom: attrInt(pgMarEl, "bottom"),
3040
+ left: attrInt(pgMarEl, "left"),
3041
+ header: attrInt(pgMarEl, "header"),
3042
+ footer: attrInt(pgMarEl, "footer"),
3043
+ gutter: attrInt(pgMarEl, "gutter")
3044
+ };
3045
+ }
3046
+ const typeEl = findChildNs(sectPrEl, "type");
3047
+ if (typeEl) {
3048
+ sect.breakType = attrVal(typeEl, "val");
3049
+ }
3050
+ const colsEl = findChildNs(sectPrEl, "cols");
3051
+ if (colsEl) {
3052
+ const cols = {};
3053
+ cols.space = attrInt(colsEl, "space");
3054
+ cols.count = attrInt(colsEl, "num");
3055
+ const eqw = attrVal(colsEl, "equalWidth");
3056
+ if (eqw !== undefined) {
3057
+ cols.equalWidth = eqw === "1";
3058
+ }
3059
+ const sep = attrVal(colsEl, "sep");
3060
+ if (sep === "1" || sep === "true") {
3061
+ cols.separator = true;
3062
+ }
3063
+ const colDefs = findChildrenNs(colsEl, "col");
3064
+ if (colDefs.length > 0) {
3065
+ cols.columns = colDefs.map(c => ({
3066
+ width: attrInt(c, "w") ?? 0,
3067
+ space: attrInt(c, "space")
3068
+ }));
3069
+ }
3070
+ sect.columns = cols;
3071
+ }
3072
+ if (findChildNs(sectPrEl, "titlePg")) {
3073
+ sect.titlePage = true;
3074
+ }
3075
+ const pgNumEl = findChildNs(sectPrEl, "pgNumType");
3076
+ if (pgNumEl) {
3077
+ sect.pageNumbering = {
3078
+ start: attrInt(pgNumEl, "start"),
3079
+ format: attrVal(pgNumEl, "fmt")
3080
+ };
3081
+ }
3082
+ // Page borders
3083
+ const pgBordersEl = findChildNs(sectPrEl, "pgBorders");
3084
+ if (pgBordersEl) {
3085
+ const pb = {};
3086
+ const p = pb;
3087
+ for (const side of ["top", "left", "bottom", "right"]) {
3088
+ const sideEl = findChildNs(pgBordersEl, side);
3089
+ if (sideEl) {
3090
+ p[side] = parseBorder(sideEl);
3091
+ }
3092
+ }
3093
+ const display = attrVal(pgBordersEl, "display");
3094
+ if (display) {
3095
+ p.display = display;
3096
+ }
3097
+ const offsetFrom = attrVal(pgBordersEl, "offsetFrom");
3098
+ if (offsetFrom) {
3099
+ p.offsetFrom = offsetFrom;
3100
+ }
3101
+ const zOrder = attrVal(pgBordersEl, "zOrder");
3102
+ if (zOrder) {
3103
+ p.zOrder = zOrder;
3104
+ }
3105
+ sect.pageBorders = pb;
3106
+ }
3107
+ // Vertical alignment
3108
+ const vAlignEl = findChildNs(sectPrEl, "vAlign");
3109
+ if (vAlignEl) {
3110
+ sect.verticalAlign = attrVal(vAlignEl, "val");
3111
+ }
3112
+ // Text direction
3113
+ const textDirEl = findChildNs(sectPrEl, "textDirection");
3114
+ if (textDirEl) {
3115
+ sect.textDirection = attrVal(textDirEl, "val");
3116
+ }
3117
+ // Bidi
3118
+ const bidiToggle = boolToggle(sectPrEl, "bidi");
3119
+ if (bidiToggle !== undefined) {
3120
+ sect.bidi = bidiToggle;
3121
+ }
3122
+ // RTL gutter
3123
+ const rtlGutterEl = findChildNs(sectPrEl, "rtlGutter");
3124
+ if (rtlGutterEl) {
3125
+ sect.rtlGutter = true;
3126
+ }
3127
+ // Form protection
3128
+ const formProtEl = findChildNs(sectPrEl, "formProt");
3129
+ if (formProtEl) {
3130
+ const v = attrVal(formProtEl, "val");
3131
+ sect.formProtection = v === "1" || v === "true";
3132
+ }
3133
+ // Document grid
3134
+ const docGridEl = findChildNs(sectPrEl, "docGrid");
3135
+ if (docGridEl) {
3136
+ const dg = {};
3137
+ const linePitch = attrInt(docGridEl, "linePitch");
3138
+ if (linePitch !== undefined) {
3139
+ dg.linePitch = linePitch;
3140
+ }
3141
+ const charSpace = attrInt(docGridEl, "charSpace");
3142
+ if (charSpace !== undefined) {
3143
+ dg.charSpace = charSpace;
3144
+ }
3145
+ const gridType = attrVal(docGridEl, "type");
3146
+ if (gridType) {
3147
+ dg.type = gridType;
3148
+ }
3149
+ sect.docGrid = dg;
3150
+ }
3151
+ // Line numbers
3152
+ const lnNumEl = findChildNs(sectPrEl, "lnNumType");
3153
+ if (lnNumEl) {
3154
+ const ln = {};
3155
+ const countBy = attrInt(lnNumEl, "countBy");
3156
+ if (countBy !== undefined) {
3157
+ ln.countBy = countBy;
3158
+ }
3159
+ const start = attrInt(lnNumEl, "start");
3160
+ if (start !== undefined) {
3161
+ ln.start = start;
3162
+ }
3163
+ const restart = attrVal(lnNumEl, "restart");
3164
+ if (restart) {
3165
+ ln.restart = restart;
3166
+ }
3167
+ const distance = attrInt(lnNumEl, "distance");
3168
+ if (distance !== undefined) {
3169
+ ln.distance = distance;
3170
+ }
3171
+ sect.lineNumbers = ln;
3172
+ }
3173
+ // Footnote properties
3174
+ const fnPrEl = findChildNs(sectPrEl, "footnotePr");
3175
+ if (fnPrEl) {
3176
+ sect.footnoteProperties = parseNoteProperties(fnPrEl);
3177
+ }
3178
+ // Endnote properties
3179
+ const enPrEl = findChildNs(sectPrEl, "endnotePr");
3180
+ if (enPrEl) {
3181
+ sect.endnoteProperties = parseNoteProperties(enPrEl);
3182
+ }
3183
+ // Headers/Footers refs
3184
+ const headerRefs = [];
3185
+ for (const hRef of findChildrenNs(sectPrEl, "headerReference")) {
3186
+ headerRefs.push({
3187
+ type: (attrVal(hRef, "type") ?? "default"),
3188
+ rId: hRef.attributes["r:id"] ?? ""
3189
+ });
3190
+ }
3191
+ if (headerRefs.length > 0) {
3192
+ sect.headers = headerRefs;
3193
+ }
3194
+ const footerRefs = [];
3195
+ for (const fRef of findChildrenNs(sectPrEl, "footerReference")) {
3196
+ footerRefs.push({
3197
+ type: (attrVal(fRef, "type") ?? "default"),
3198
+ rId: fRef.attributes["r:id"] ?? ""
3199
+ });
3200
+ }
3201
+ if (footerRefs.length > 0) {
3202
+ sect.footers = footerRefs;
3203
+ }
3204
+ // sectPrChange (track changes for section properties)
3205
+ const sectPrChangeEl = findChildNs(sectPrEl, "sectPrChange");
3206
+ if (sectPrChangeEl) {
3207
+ const rev = parseRevisionInfo(sectPrChangeEl);
3208
+ if (rev) {
3209
+ const prevSectPrEl = findChildNs(sectPrChangeEl, "sectPr");
3210
+ const change = {
3211
+ revision: rev,
3212
+ previousProperties: prevSectPrEl ? parseSectionProperties(prevSectPrEl) : undefined
3213
+ };
3214
+ sect.propertyChange = change;
3215
+ }
3216
+ }
3217
+ return sect;
3218
+ }
3219
+ // =============================================================================
3220
+ // Styles Parser
3221
+ // =============================================================================
3222
+ function parseStyles(xmlStr) {
3223
+ const doc = parseXml(xmlStr);
3224
+ const root = doc.root;
3225
+ let docDefaults;
3226
+ const styles = [];
3227
+ const ddEl = findChildNs(root, "docDefaults");
3228
+ if (ddEl) {
3229
+ const dd = {};
3230
+ const rPrDefaultEl = findChildNs(ddEl, "rPrDefault");
3231
+ if (rPrDefaultEl) {
3232
+ const rPrEl = findChildNs(rPrDefaultEl, "rPr");
3233
+ if (rPrEl) {
3234
+ dd.runProperties = parseRunProperties(rPrEl);
3235
+ }
3236
+ }
3237
+ const pPrDefaultEl = findChildNs(ddEl, "pPrDefault");
3238
+ if (pPrDefaultEl) {
3239
+ const pPrEl = findChildNs(pPrDefaultEl, "pPr");
3240
+ if (pPrEl) {
3241
+ dd.paragraphProperties = parseParagraphProperties(pPrEl);
3242
+ }
3243
+ }
3244
+ docDefaults = dd;
3245
+ }
3246
+ for (const styleEl of findChildrenNs(root, "style")) {
3247
+ const s = {};
3248
+ s.type = attrVal(styleEl, "type");
3249
+ s.styleId = attrVal(styleEl, "styleId");
3250
+ s.isDefault = attrVal(styleEl, "default") === "1";
3251
+ if (attrVal(styleEl, "customStyle") === "1") {
3252
+ s.customStyle = true;
3253
+ }
3254
+ const nameEl = findChildNs(styleEl, "name");
3255
+ s.name = nameEl ? (attrVal(nameEl, "val") ?? "") : "";
3256
+ const basedOnEl = findChildNs(styleEl, "basedOn");
3257
+ if (basedOnEl) {
3258
+ s.basedOn = attrVal(basedOnEl, "val");
3259
+ }
3260
+ const nextEl = findChildNs(styleEl, "next");
3261
+ if (nextEl) {
3262
+ s.next = attrVal(nextEl, "val");
3263
+ }
3264
+ const linkEl = findChildNs(styleEl, "link");
3265
+ if (linkEl) {
3266
+ s.link = attrVal(linkEl, "val");
3267
+ }
3268
+ const uiPrEl = findChildNs(styleEl, "uiPriority");
3269
+ if (uiPrEl) {
3270
+ s.uiPriority = attrInt(uiPrEl, "val");
3271
+ }
3272
+ if (findChildNs(styleEl, "qFormat")) {
3273
+ s.qFormat = true;
3274
+ }
3275
+ if (findChildNs(styleEl, "semiHidden")) {
3276
+ s.semiHidden = true;
3277
+ }
3278
+ if (findChildNs(styleEl, "unhideWhenUsed")) {
3279
+ s.unhideWhenUsed = true;
3280
+ }
3281
+ if (findChildNs(styleEl, "hidden")) {
3282
+ s.hidden = true;
3283
+ }
3284
+ if (findChildNs(styleEl, "locked")) {
3285
+ s.locked = true;
3286
+ }
3287
+ if (findChildNs(styleEl, "autoRedefine")) {
3288
+ s.autoRedefine = true;
3289
+ }
3290
+ const pPrEl = findChildNs(styleEl, "pPr");
3291
+ if (pPrEl) {
3292
+ s.paragraphProperties = parseParagraphProperties(pPrEl);
3293
+ }
3294
+ const rPrEl = findChildNs(styleEl, "rPr");
3295
+ if (rPrEl) {
3296
+ s.runProperties = parseRunProperties(rPrEl);
3297
+ }
3298
+ // Table properties for table styles
3299
+ const tblPrEl = findChildNs(styleEl, "tblPr");
3300
+ if (tblPrEl) {
3301
+ s.tableProperties = parseTableProperties(tblPrEl);
3302
+ }
3303
+ // Table style conditional formats
3304
+ const tblStylePrs = findChildrenNs(styleEl, "tblStylePr");
3305
+ if (tblStylePrs.length > 0) {
3306
+ const conditions = [];
3307
+ for (const tsp of tblStylePrs) {
3308
+ const cond = { type: attrVal(tsp, "type") };
3309
+ const cpPr = findChildNs(tsp, "pPr");
3310
+ if (cpPr) {
3311
+ cond.paragraphProperties = parseParagraphProperties(cpPr);
3312
+ }
3313
+ const crPr = findChildNs(tsp, "rPr");
3314
+ if (crPr) {
3315
+ cond.runProperties = parseRunProperties(crPr);
3316
+ }
3317
+ const ctblPr = findChildNs(tsp, "tblPr");
3318
+ if (ctblPr) {
3319
+ cond.tableProperties = parseTableProperties(ctblPr);
3320
+ }
3321
+ const ctrPr = findChildNs(tsp, "trPr");
3322
+ if (ctrPr) {
3323
+ const rp = {};
3324
+ const hEl = findChildNs(ctrPr, "trHeight");
3325
+ if (hEl) {
3326
+ rp.height = { value: attrInt(hEl, "val") ?? 0, rule: attrVal(hEl, "hRule") };
3327
+ }
3328
+ cond.rowProperties = rp;
3329
+ }
3330
+ const ctcPr = findChildNs(tsp, "tcPr");
3331
+ if (ctcPr) {
3332
+ const cp = {};
3333
+ const bEl = findChildNs(ctcPr, "tcBorders");
3334
+ if (bEl) {
3335
+ cp.borders = parseTableBorders(bEl);
3336
+ }
3337
+ const shd = findChildNs(ctcPr, "shd");
3338
+ if (shd) {
3339
+ cp.shading = parseShading(shd);
3340
+ }
3341
+ cond.cellProperties = cp;
3342
+ }
3343
+ conditions.push(cond);
3344
+ }
3345
+ s.tableStyleConditions = conditions;
3346
+ }
3347
+ styles.push(s);
3348
+ }
3349
+ return { docDefaults, styles };
3350
+ }
3351
+ // =============================================================================
3352
+ // Numbering Parser
3353
+ // =============================================================================
3354
+ function parseNumberingXml(xmlStr) {
3355
+ const doc = parseXml(xmlStr);
3356
+ const root = doc.root;
3357
+ const abstractNums = [];
3358
+ const instances = [];
3359
+ const numPicBullets = [];
3360
+ // Parse picture bullets
3361
+ for (const pbEl of findChildrenNs(root, "numPicBullet")) {
3362
+ const id = attrInt(pbEl, "numPicBulletId");
3363
+ if (id === undefined) {
3364
+ continue;
3365
+ }
3366
+ const pb = { id };
3367
+ // Try to extract VML shape info
3368
+ const pictEl = findChildNs(pbEl, "pict");
3369
+ if (pictEl) {
3370
+ // Preserve raw VML for complete fidelity
3371
+ let rawVml = "";
3372
+ for (const child of pictEl.children) {
3373
+ if (child.type === "element") {
3374
+ rawVml += serializeElement(child);
3375
+ }
3376
+ }
3377
+ if (rawVml) {
3378
+ pb.rawVmlXml = rawVml;
3379
+ }
3380
+ // Extract rId from v:imagedata
3381
+ const shapeEl = findChild(pictEl, "v:shape");
3382
+ if (shapeEl) {
3383
+ const imgDataEl = findChild(shapeEl, "v:imagedata");
3384
+ if (imgDataEl) {
3385
+ const rId = imgDataEl.attributes["r:id"] ?? imgDataEl.attributes["r:pict"];
3386
+ if (rId) {
3387
+ pb.rId = rId;
3388
+ }
3389
+ }
3390
+ // Extract width/height from style
3391
+ const style = shapeEl.attributes["style"];
3392
+ if (style) {
3393
+ const wMatch = /width:([\d.]+)pt/i.exec(style);
3394
+ const hMatch = /height:([\d.]+)pt/i.exec(style);
3395
+ if (wMatch) {
3396
+ pb.width = Math.round(parseFloat(wMatch[1]) * 12700);
3397
+ }
3398
+ if (hMatch) {
3399
+ pb.height = Math.round(parseFloat(hMatch[1]) * 12700);
3400
+ }
3401
+ }
3402
+ }
3403
+ }
3404
+ numPicBullets.push(pb);
3405
+ }
3406
+ for (const absEl of findChildrenNs(root, "abstractNum")) {
3407
+ const levels = [];
3408
+ for (const lvlEl of findChildrenNs(absEl, "lvl")) {
3409
+ levels.push(parseLevel(lvlEl));
3410
+ }
3411
+ const abs = {
3412
+ abstractNumId: attrInt(absEl, "abstractNumId") ?? 0,
3413
+ levels
3414
+ };
3415
+ const mltEl = findChildNs(absEl, "multiLevelType");
3416
+ if (mltEl) {
3417
+ abs.multiLevelType = attrVal(mltEl, "val");
3418
+ }
3419
+ const numStyleLinkEl = findChildNs(absEl, "numStyleLink");
3420
+ if (numStyleLinkEl) {
3421
+ abs.numStyleLink = attrVal(numStyleLinkEl, "val");
3422
+ }
3423
+ const styleLinkEl = findChildNs(absEl, "styleLink");
3424
+ if (styleLinkEl) {
3425
+ abs.styleLink = attrVal(styleLinkEl, "val");
3426
+ }
3427
+ abstractNums.push(abs);
3428
+ }
3429
+ for (const numEl of findChildrenNs(root, "num")) {
3430
+ const absIdEl = findChildNs(numEl, "abstractNumId");
3431
+ const overrides = [];
3432
+ for (const ovEl of findChildrenNs(numEl, "lvlOverride")) {
3433
+ const ov = { level: attrInt(ovEl, "ilvl") ?? 0 };
3434
+ const startOvEl = findChildNs(ovEl, "startOverride");
3435
+ if (startOvEl) {
3436
+ ov.startOverride = attrInt(startOvEl, "val");
3437
+ }
3438
+ // Level def override: parse full level definition
3439
+ const lvlEl = findChildNs(ovEl, "lvl");
3440
+ if (lvlEl) {
3441
+ ov.levelDef = parseLevel(lvlEl);
3442
+ // Inherit level index from parent if not specified
3443
+ if (ov.levelDef.level === undefined) {
3444
+ ov.levelDef.level = ov.level;
3445
+ }
3446
+ }
3447
+ overrides.push(ov);
3448
+ }
3449
+ instances.push({
3450
+ numId: attrInt(numEl, "numId") ?? 0,
3451
+ abstractNumId: absIdEl ? (attrInt(absIdEl, "val") ?? 0) : 0,
3452
+ overrides: overrides.length > 0 ? overrides : undefined
3453
+ });
3454
+ }
3455
+ return { abstractNums, instances, numPicBullets };
3456
+ }
3457
+ /** Parse a w:lvl element into a NumberingLevel (shared by abstractNum and lvlOverride). */
3458
+ function parseLevel(lvlEl) {
3459
+ const level = { level: attrInt(lvlEl, "ilvl") ?? 0 };
3460
+ const startEl = findChildNs(lvlEl, "start");
3461
+ if (startEl) {
3462
+ level.start = attrInt(startEl, "val");
3463
+ }
3464
+ const fmtEl = findChildNs(lvlEl, "numFmt");
3465
+ if (fmtEl) {
3466
+ level.format = attrVal(fmtEl, "val");
3467
+ }
3468
+ const textEl = findChildNs(lvlEl, "lvlText");
3469
+ if (textEl) {
3470
+ level.text = attrVal(textEl, "val") ?? "";
3471
+ }
3472
+ const pStyleEl = findChildNs(lvlEl, "pStyle");
3473
+ if (pStyleEl) {
3474
+ level.paragraphStyle = attrVal(pStyleEl, "val");
3475
+ }
3476
+ const jcEl = findChildNs(lvlEl, "lvlJc");
3477
+ if (jcEl) {
3478
+ level.justification = attrVal(jcEl, "val");
3479
+ }
3480
+ const pPrEl = findChildNs(lvlEl, "pPr");
3481
+ if (pPrEl) {
3482
+ level.paragraphProperties = parseParagraphProperties(pPrEl);
3483
+ }
3484
+ const rPrEl = findChildNs(lvlEl, "rPr");
3485
+ if (rPrEl) {
3486
+ level.runProperties = parseRunProperties(rPrEl);
3487
+ }
3488
+ const suffEl = findChildNs(lvlEl, "suff");
3489
+ if (suffEl) {
3490
+ level.suffix = attrVal(suffEl, "val");
3491
+ }
3492
+ if (findChildNs(lvlEl, "isLgl")) {
3493
+ level.isLegalNumberingStyle = true;
3494
+ }
3495
+ const lvlRestartEl = findChildNs(lvlEl, "lvlRestart");
3496
+ if (lvlRestartEl) {
3497
+ level.restartAfterLevel = attrInt(lvlRestartEl, "val");
3498
+ }
3499
+ const picBulletEl = findChildNs(lvlEl, "lvlPicBulletId");
3500
+ if (picBulletEl) {
3501
+ level.picBulletId = attrInt(picBulletEl, "val");
3502
+ }
3503
+ return level;
3504
+ }
3505
+ // =============================================================================
3506
+ // Footnotes/Endnotes Parser
3507
+ // =============================================================================
3508
+ function parseNotesXml(xmlStr, elementName) {
3509
+ const doc = parseXml(xmlStr);
3510
+ const root = doc.root;
3511
+ const notes = [];
3512
+ for (const noteEl of findChildrenNs(root, elementName)) {
3513
+ const id = attrInt(noteEl, "id");
3514
+ const type = attrVal(noteEl, "type");
3515
+ // Skip auto-generated separator entries (default IDs -1 and 0)
3516
+ // Real separators/continuationSeparators are regenerated by the writer.
3517
+ if (type === "separator" || type === "continuationSeparator") {
3518
+ continue;
3519
+ }
3520
+ if (id === undefined) {
3521
+ continue;
3522
+ }
3523
+ const content = [];
3524
+ for (const child of noteEl.children) {
3525
+ if (child.type === "element" && child.name.replace(/^w:/, "") === "p") {
3526
+ content.push(parseParagraph(child));
3527
+ }
3528
+ }
3529
+ const note = { id, content };
3530
+ if (type === "continuationNotice" || type === "normal") {
3531
+ note.type = type;
3532
+ }
3533
+ notes.push(note);
3534
+ }
3535
+ return notes;
3536
+ }
3537
+ // =============================================================================
3538
+ // Header/Footer Parser
3539
+ // =============================================================================
3540
+ function parseHeaderFooterXml(xmlStr) {
3541
+ return parseHeaderFooterRoot(parseXml(xmlStr).root);
3542
+ }
3543
+ function parseHeaderFooterRoot(root) {
3544
+ const children = [];
3545
+ for (const child of root.children) {
3546
+ if (child.type !== "element") {
3547
+ continue;
3548
+ }
3549
+ const name = child.name.replace(/^w:/, "");
3550
+ if (name === "p") {
3551
+ children.push(parseParagraph(child));
3552
+ }
3553
+ else if (name === "tbl") {
3554
+ children.push(parseTable(child));
3555
+ }
3556
+ }
3557
+ return { children };
3558
+ }
3559
+ /** Detect watermark from a header's parsed XML root element. */
3560
+ function detectWatermarkFromRoot(root) {
3561
+ // Look for VML shape with id containing "WaterMark"
3562
+ for (const pEl of root.children) {
3563
+ if (pEl.type !== "element") {
3564
+ continue;
3565
+ }
3566
+ for (const rEl of pEl.children) {
3567
+ if (rEl.type !== "element") {
3568
+ continue;
3569
+ }
3570
+ // Look for w:pict or w:r > w:pict
3571
+ const pictEls = [];
3572
+ const rName = rEl.name.replace(/^w:/, "");
3573
+ if (rName === "pict") {
3574
+ pictEls.push(rEl);
3575
+ }
3576
+ else if (rName === "r") {
3577
+ for (const rc of rEl.children) {
3578
+ if (rc.type === "element" && rc.name.replace(/^w:/, "") === "pict") {
3579
+ pictEls.push(rc);
3580
+ }
3581
+ }
3582
+ }
3583
+ for (const pictEl of pictEls) {
3584
+ for (const shapeEl of pictEl.children) {
3585
+ if (shapeEl.type !== "element") {
3586
+ continue;
3587
+ }
3588
+ const shapeId = shapeEl.attributes["id"] ?? "";
3589
+ if (!shapeId.toLowerCase().includes("watermark")) {
3590
+ continue;
3591
+ }
3592
+ // Found watermark shape
3593
+ const shapeType = shapeEl.attributes["type"] ?? "";
3594
+ if (shapeType.includes("136")) {
3595
+ // WordArt text watermark (shapetype 136)
3596
+ return parseTextWatermark(shapeEl);
3597
+ }
3598
+ // Check for image watermark (has v:imagedata)
3599
+ const imgData = findChild(shapeEl, "v:imagedata");
3600
+ if (imgData) {
3601
+ return parseImageWatermark(shapeEl, imgData);
3602
+ }
3603
+ }
3604
+ }
3605
+ }
3606
+ }
3607
+ return undefined;
3608
+ }
3609
+ function parseTextWatermark(shapeEl) {
3610
+ const fillColor = shapeEl.attributes["fillcolor"] ?? "#C0C0C0";
3611
+ const color = fillColor.replace(/^#/, "");
3612
+ // Parse rotation from style
3613
+ const style = shapeEl.attributes["style"] ?? "";
3614
+ let rotation = -45;
3615
+ const rotMatch = style.match(/rotation:\s*(-?\d+)/);
3616
+ if (rotMatch) {
3617
+ rotation = parseInt(rotMatch[1], 10);
3618
+ }
3619
+ // Get opacity from v:fill
3620
+ const fillEl = findChild(shapeEl, "v:fill");
3621
+ const opacity = fillEl?.attributes["opacity"] ?? ".5";
3622
+ const semiTransparent = opacity !== "1";
3623
+ // Get text and font from v:textpath
3624
+ const textpathEl = findChild(shapeEl, "v:textpath");
3625
+ const text = textpathEl?.attributes["string"] ?? "";
3626
+ const tpStyle = textpathEl?.attributes["style"] ?? "";
3627
+ let font;
3628
+ let fontSize;
3629
+ const fontMatch = tpStyle.match(/font-family:\s*"?([^";]+)"?/);
3630
+ if (fontMatch) {
3631
+ font = fontMatch[1].replace(/&quot;/g, "");
3632
+ }
3633
+ const sizeMatch = tpStyle.match(/font-size:\s*(\d+(?:\.\d+)?)\s*pt/);
3634
+ if (sizeMatch) {
3635
+ fontSize = Math.round(parseFloat(sizeMatch[1]) * 2); // convert pt to half-points
3636
+ }
3637
+ return {
3638
+ type: "text",
3639
+ text,
3640
+ font,
3641
+ fontSize,
3642
+ color,
3643
+ semiTransparent,
3644
+ rotation
3645
+ };
3646
+ }
3647
+ function parseImageWatermark(shapeEl, imgDataEl) {
3648
+ const rId = imgDataEl.attributes["r:id"] ?? "";
3649
+ const gain = imgDataEl.attributes["gain"] ?? "";
3650
+ const washout = gain.startsWith("19661") || gain === "";
3651
+ return {
3652
+ type: "image",
3653
+ rId,
3654
+ washout
3655
+ };
3656
+ }
3657
+ // =============================================================================
3658
+ // Comments Parser
3659
+ // =============================================================================
3660
+ function parseCommentsXml(xmlStr) {
3661
+ const doc = parseXml(xmlStr);
3662
+ const root = doc.root;
3663
+ const comments = [];
3664
+ for (const commentEl of findChildrenNs(root, "comment")) {
3665
+ const id = attrInt(commentEl, "id");
3666
+ const author = attrVal(commentEl, "author");
3667
+ if (id === undefined || !author) {
3668
+ continue;
3669
+ }
3670
+ const content = [];
3671
+ for (const child of commentEl.children) {
3672
+ if (child.type === "element" && child.name.replace(/^w:/, "") === "p") {
3673
+ content.push(parseParagraph(child));
3674
+ }
3675
+ }
3676
+ const comment = { id, author, content };
3677
+ const date = attrVal(commentEl, "date");
3678
+ if (date) {
3679
+ comment.date = date;
3680
+ }
3681
+ const initials = attrVal(commentEl, "initials");
3682
+ if (initials) {
3683
+ comment.initials = initials;
3684
+ }
3685
+ comments.push(comment);
3686
+ }
3687
+ return comments;
3688
+ }
3689
+ /** Parse word/commentsExtended.xml — map paraId → { done, parentId }. */
3690
+ function parseCommentsExtendedXml(xmlStr) {
3691
+ const map = new Map();
3692
+ const doc = parseXml(xmlStr);
3693
+ const root = doc.root;
3694
+ for (const child of root.children) {
3695
+ if (child.type !== "element") {
3696
+ continue;
3697
+ }
3698
+ // w15:commentEx
3699
+ const name = child.name;
3700
+ if (!name.endsWith("commentEx")) {
3701
+ continue;
3702
+ }
3703
+ const paraId = child.attributes["w15:paraId"] ?? child.attributes["paraId"];
3704
+ if (!paraId) {
3705
+ continue;
3706
+ }
3707
+ const entry = {};
3708
+ const done = child.attributes["w15:done"] ?? child.attributes["done"];
3709
+ if (done === "1" || done === "true") {
3710
+ entry.done = true;
3711
+ }
3712
+ else if (done === "0" || done === "false") {
3713
+ entry.done = false;
3714
+ }
3715
+ const pid = child.attributes["w15:paraIdParent"] ?? child.attributes["paraIdParent"];
3716
+ if (pid) {
3717
+ entry.parentId = pid;
3718
+ }
3719
+ map.set(paraId, entry);
3720
+ }
3721
+ return map;
3722
+ }
3723
+ // =============================================================================
3724
+ // Core Properties Parser
3725
+ // =============================================================================
3726
+ function parseCoreProps(xmlStr) {
3727
+ const doc = parseXml(xmlStr);
3728
+ const root = doc.root;
3729
+ const props = {};
3730
+ const fields = [
3731
+ ["dc:title", "title"],
3732
+ ["dc:subject", "subject"],
3733
+ ["dc:creator", "creator"],
3734
+ ["dc:description", "description"],
3735
+ ["cp:keywords", "keywords"],
3736
+ ["cp:lastModifiedBy", "lastModifiedBy"],
3737
+ ["cp:revision", "revision"],
3738
+ ["cp:category", "category"]
3739
+ ];
3740
+ for (const [tag, prop] of fields) {
3741
+ const el = findChild(root, tag);
3742
+ if (el) {
3743
+ const val = textContent(el);
3744
+ if (val) {
3745
+ props[prop] = val;
3746
+ }
3747
+ }
3748
+ }
3749
+ const createdEl = findChild(root, "dcterms:created");
3750
+ if (createdEl) {
3751
+ const val = textContent(createdEl);
3752
+ if (val) {
3753
+ props.created = new Date(val);
3754
+ }
3755
+ }
3756
+ const modifiedEl = findChild(root, "dcterms:modified");
3757
+ if (modifiedEl) {
3758
+ const val = textContent(modifiedEl);
3759
+ if (val) {
3760
+ props.modified = new Date(val);
3761
+ }
3762
+ }
3763
+ return props;
3764
+ }
3765
+ // =============================================================================
3766
+ // App Properties Parser
3767
+ // =============================================================================
3768
+ function parseAppProps(xmlStr) {
3769
+ const doc = parseXml(xmlStr);
3770
+ const root = doc.root;
3771
+ const props = {};
3772
+ const strFields = ["Application", "AppVersion", "Company", "Manager"];
3773
+ const intFields = ["Pages", "Words", "Characters", "Lines", "Paragraphs"];
3774
+ for (const field of strFields) {
3775
+ const el = findChild(root, field);
3776
+ if (el) {
3777
+ const val = textContent(el);
3778
+ if (val) {
3779
+ props[field.charAt(0).toLowerCase() + field.slice(1)] = val;
3780
+ }
3781
+ }
3782
+ }
3783
+ for (const field of intFields) {
3784
+ const el = findChild(root, field);
3785
+ if (el) {
3786
+ const val = textContent(el);
3787
+ if (val) {
3788
+ props[field.charAt(0).toLowerCase() + field.slice(1)] = parseInt(val, 10);
3789
+ }
3790
+ }
3791
+ }
3792
+ return props;
3793
+ }
3794
+ // =============================================================================
3795
+ // Theme Parser
3796
+ // =============================================================================
3797
+ const THEME_COLOR_NAMES = [
3798
+ "dk1",
3799
+ "lt1",
3800
+ "dk2",
3801
+ "lt2",
3802
+ "accent1",
3803
+ "accent2",
3804
+ "accent3",
3805
+ "accent4",
3806
+ "accent5",
3807
+ "accent6",
3808
+ "hlink",
3809
+ "folHlink"
3810
+ ];
3811
+ function parseThemeXml(xmlStr) {
3812
+ const doc = parseXml(xmlStr);
3813
+ const root = doc.root;
3814
+ // Find a:themeElements
3815
+ const themeElements = findChild(root, "a:themeElements") ?? findChildNs(root, "themeElements");
3816
+ const defaultScheme = {
3817
+ name: "Office",
3818
+ colors: {
3819
+ dk1: "000000",
3820
+ lt1: "FFFFFF",
3821
+ dk2: "44546A",
3822
+ lt2: "E7E6E6",
3823
+ accent1: "4472C4",
3824
+ accent2: "ED7D31",
3825
+ accent3: "A5A5A5",
3826
+ accent4: "FFC000",
3827
+ accent5: "5B9BD5",
3828
+ accent6: "70AD47",
3829
+ hlink: "0563C1",
3830
+ folHlink: "954F72"
3831
+ }
3832
+ };
3833
+ const defaultFontScheme = { name: "Office", majorFont: "Calibri Light", minorFont: "Calibri" };
3834
+ if (!themeElements) {
3835
+ return {
3836
+ name: root.attributes["name"],
3837
+ colorScheme: defaultScheme,
3838
+ fontScheme: defaultFontScheme
3839
+ };
3840
+ }
3841
+ // Parse color scheme
3842
+ const clrSchemeEl = findChild(themeElements, "a:clrScheme") ?? findChildNs(themeElements, "clrScheme");
3843
+ const colorScheme = { ...defaultScheme };
3844
+ if (clrSchemeEl) {
3845
+ colorScheme.name = clrSchemeEl.attributes["name"] ?? "Office";
3846
+ for (const colorName of THEME_COLOR_NAMES) {
3847
+ const colorEl = findChild(clrSchemeEl, `a:${colorName}`) ?? findChildNs(clrSchemeEl, colorName);
3848
+ if (colorEl) {
3849
+ // Color can be sysClr (with lastClr) or srgbClr (with val)
3850
+ const srgb = findChild(colorEl, "a:srgbClr") ?? findChildNs(colorEl, "srgbClr");
3851
+ if (srgb) {
3852
+ const val = srgb.attributes["val"];
3853
+ if (val) {
3854
+ colorScheme.colors[colorName] = val;
3855
+ }
3856
+ }
3857
+ else {
3858
+ const sys = findChild(colorEl, "a:sysClr") ?? findChildNs(colorEl, "sysClr");
3859
+ if (sys) {
3860
+ const lastClr = sys.attributes["lastClr"];
3861
+ if (lastClr) {
3862
+ colorScheme.colors[colorName] = lastClr;
3863
+ }
3864
+ }
3865
+ }
3866
+ }
3867
+ }
3868
+ }
3869
+ // Parse font scheme
3870
+ const fontSchemeEl = findChild(themeElements, "a:fontScheme") ?? findChildNs(themeElements, "fontScheme");
3871
+ const fontScheme = { ...defaultFontScheme };
3872
+ if (fontSchemeEl) {
3873
+ fontScheme.name = fontSchemeEl.attributes["name"] ?? "Office";
3874
+ const majorEl = findChild(fontSchemeEl, "a:majorFont") ?? findChildNs(fontSchemeEl, "majorFont");
3875
+ if (majorEl) {
3876
+ const major = parseThemeFont(majorEl);
3877
+ fontScheme.major = major;
3878
+ if (major.latin) {
3879
+ fontScheme.majorFont = major.latin;
3880
+ }
3881
+ }
3882
+ const minorEl = findChild(fontSchemeEl, "a:minorFont") ?? findChildNs(fontSchemeEl, "minorFont");
3883
+ if (minorEl) {
3884
+ const minor = parseThemeFont(minorEl);
3885
+ fontScheme.minor = minor;
3886
+ if (minor.latin) {
3887
+ fontScheme.minorFont = minor.latin;
3888
+ }
3889
+ }
3890
+ }
3891
+ // Parse format scheme (preserve raw XML of its children for round-trip)
3892
+ const fmtSchemeEl = findChild(themeElements, "a:fmtScheme") ?? findChildNs(themeElements, "fmtScheme");
3893
+ let formatScheme;
3894
+ if (fmtSchemeEl) {
3895
+ let rawXml = "";
3896
+ for (const child of fmtSchemeEl.children) {
3897
+ if (child.type === "element") {
3898
+ rawXml += serializeElement(child);
3899
+ }
3900
+ }
3901
+ formatScheme = {
3902
+ name: fmtSchemeEl.attributes["name"] ?? "Office",
3903
+ rawXml: rawXml || undefined
3904
+ };
3905
+ }
3906
+ // Preserve extLst (theme extensions) as raw XML
3907
+ let extLstXml;
3908
+ const extLstEl = findChild(root, "a:extLst") ?? findChildNs(root, "extLst");
3909
+ if (extLstEl) {
3910
+ extLstXml = serializeElement(extLstEl);
3911
+ }
3912
+ return {
3913
+ name: root.attributes["name"],
3914
+ colorScheme,
3915
+ fontScheme,
3916
+ formatScheme,
3917
+ extLstXml
3918
+ };
3919
+ }
3920
+ /** Parse a theme font (a:majorFont or a:minorFont). */
3921
+ function parseThemeFont(el) {
3922
+ const font = {};
3923
+ const latin = findChild(el, "a:latin") ?? findChildNs(el, "latin");
3924
+ if (latin?.attributes["typeface"]) {
3925
+ font.latin = latin.attributes["typeface"];
3926
+ }
3927
+ const ea = findChild(el, "a:ea") ?? findChildNs(el, "ea");
3928
+ if (ea?.attributes["typeface"]) {
3929
+ font.eastAsia = ea.attributes["typeface"];
3930
+ }
3931
+ const cs = findChild(el, "a:cs") ?? findChildNs(el, "cs");
3932
+ if (cs?.attributes["typeface"]) {
3933
+ font.complexScript = cs.attributes["typeface"];
3934
+ }
3935
+ // Supplemental fonts (a:font script="..." typeface="...")
3936
+ const supplementalFonts = {};
3937
+ for (const child of el.children) {
3938
+ if (child.type === "element" && (child.name === "a:font" || child.name === "font")) {
3939
+ const script = child.attributes["script"];
3940
+ const typeface = child.attributes["typeface"];
3941
+ if (script && typeface) {
3942
+ supplementalFonts[script] = typeface;
3943
+ }
3944
+ }
3945
+ }
3946
+ if (Object.keys(supplementalFonts).length > 0) {
3947
+ font.supplementalFonts = supplementalFonts;
3948
+ }
3949
+ return font;
3950
+ }
3951
+ // =============================================================================
3952
+ // Settings Parser
3953
+ // =============================================================================
3954
+ /** Parse word/webSettings.xml. */
3955
+ function parseWebSettings(xmlStr) {
3956
+ const doc = parseXml(xmlStr);
3957
+ const root = doc.root;
3958
+ const ws = {};
3959
+ const ofbEl = findChildNs(root, "optimizeForBrowser");
3960
+ if (ofbEl) {
3961
+ const ofb = {};
3962
+ const target = attrVal(ofbEl, "target");
3963
+ if (target) {
3964
+ ofb.target = target;
3965
+ }
3966
+ const mv = attrInt(ofbEl, "majorVersion");
3967
+ if (mv !== undefined) {
3968
+ ofb.majorVersion = mv;
3969
+ }
3970
+ ws.optimizeForBrowser = ofb;
3971
+ }
3972
+ if (findChildNs(root, "allowPNG")) {
3973
+ ws.allowPng = true;
3974
+ }
3975
+ if (findChildNs(root, "relyOnVML")) {
3976
+ ws.relyOnVml = true;
3977
+ }
3978
+ if (findChildNs(root, "doNotSaveAsSingleFile")) {
3979
+ ws.doNotSaveAsSingleFile = true;
3980
+ }
3981
+ if (findChildNs(root, "doNotOrganizeInFolder")) {
3982
+ ws.doNotOrganizeInFolder = true;
3983
+ }
3984
+ if (findChildNs(root, "useTargetMachineType")) {
3985
+ ws.useTargetMachineType = true;
3986
+ }
3987
+ return ws;
3988
+ }
3989
+ /** Parse word/people.xml. */
3990
+ function parsePeople(xmlStr) {
3991
+ const doc = parseXml(xmlStr);
3992
+ const root = doc.root;
3993
+ const people = [];
3994
+ for (const personEl of root.children) {
3995
+ if (personEl.type !== "element") {
3996
+ continue;
3997
+ }
3998
+ const author = personEl.attributes["w15:author"] ?? personEl.attributes["author"];
3999
+ if (!author) {
4000
+ continue;
4001
+ }
4002
+ const info = { author };
4003
+ // presenceInfo
4004
+ for (const child of personEl.children) {
4005
+ if (child.type === "element" && child.name.endsWith("presenceInfo")) {
4006
+ const pi = {};
4007
+ const providerId = child.attributes["w15:providerId"] ?? child.attributes["providerId"];
4008
+ if (providerId) {
4009
+ pi.providerId = providerId;
4010
+ }
4011
+ const userId = child.attributes["w15:userId"] ?? child.attributes["userId"];
4012
+ if (userId) {
4013
+ pi.userId = userId;
4014
+ }
4015
+ if (Object.keys(pi).length > 0) {
4016
+ info.presenceInfo = pi;
4017
+ }
4018
+ break;
4019
+ }
4020
+ }
4021
+ people.push(info);
4022
+ }
4023
+ return people;
4024
+ }
4025
+ function parseSettingsXml(xmlStr) {
4026
+ const doc = parseXml(xmlStr);
4027
+ const root = doc.root;
4028
+ const settings = {};
4029
+ const zoomEl = findChildNs(root, "zoom");
4030
+ if (zoomEl) {
4031
+ settings.zoom = attrInt(zoomEl, "percent");
4032
+ }
4033
+ const tabEl = findChildNs(root, "defaultTabStop");
4034
+ if (tabEl) {
4035
+ settings.defaultTabStop = attrInt(tabEl, "val");
4036
+ }
4037
+ const csControlEl = findChildNs(root, "characterSpacingControl");
4038
+ if (csControlEl) {
4039
+ const v = attrVal(csControlEl, "val");
4040
+ if (v === "doNotCompress" ||
4041
+ v === "compressPunctuation" ||
4042
+ v === "compressPunctuationAndJapaneseKana") {
4043
+ settings.characterSpacingControl = v;
4044
+ }
4045
+ }
4046
+ // Extended settings
4047
+ if (findChildNs(root, "doNotTrackMoves")) {
4048
+ settings.doNotTrackMoves = true;
4049
+ }
4050
+ if (findChildNs(root, "doNotTrackFormatting")) {
4051
+ settings.doNotTrackFormatting = true;
4052
+ }
4053
+ if (findChildNs(root, "doNotDemoteNonCombiningChars")) {
4054
+ settings.doNotDemoteAsianTextFirstLine = true;
4055
+ }
4056
+ const ssFontsEl = findChildNs(root, "saveSubsetFonts");
4057
+ if (ssFontsEl) {
4058
+ const v = attrVal(ssFontsEl, "val");
4059
+ settings.saveSubsetFonts = v !== "0" && v !== "false";
4060
+ }
4061
+ if (findChildNs(root, "noPunctuationKerning")) {
4062
+ settings.noPunctuationKerning = true;
4063
+ }
4064
+ if (findChildNs(root, "bordersDoNotSurroundHeader")) {
4065
+ settings.bordersDoNotSurroundHeader = true;
4066
+ }
4067
+ if (findChildNs(root, "bordersDoNotSurroundFooter")) {
4068
+ settings.bordersDoNotSurroundFooter = true;
4069
+ }
4070
+ const clickStyleEl = findChildNs(root, "clickAndTypeStyle");
4071
+ if (clickStyleEl) {
4072
+ settings.clickAndTypeStyle = attrVal(clickStyleEl, "val");
4073
+ }
4074
+ const spfEl = findChildNs(root, "stylePaneFormatFilter");
4075
+ if (spfEl) {
4076
+ settings.stylePaneFormatFilter = attrVal(spfEl, "val");
4077
+ }
4078
+ const spsEl = findChildNs(root, "stylePaneSortMethod");
4079
+ if (spsEl) {
4080
+ settings.stylePaneSortMethod = attrVal(spsEl, "val");
4081
+ }
4082
+ const tflEl = findChildNs(root, "themeFontLang");
4083
+ if (tflEl) {
4084
+ const tfl = {};
4085
+ const v = attrVal(tflEl, "val");
4086
+ if (v) {
4087
+ tfl.val = v;
4088
+ }
4089
+ const ea = attrVal(tflEl, "eastAsia");
4090
+ if (ea) {
4091
+ tfl.eastAsia = ea;
4092
+ }
4093
+ const bd = attrVal(tflEl, "bidi");
4094
+ if (bd) {
4095
+ tfl.bidi = bd;
4096
+ }
4097
+ if (Object.keys(tfl).length > 0) {
4098
+ settings.themeFontLang = tfl;
4099
+ }
4100
+ }
4101
+ const dsEl = findChildNs(root, "decimalSymbol");
4102
+ if (dsEl) {
4103
+ settings.decimalSymbol = attrVal(dsEl, "val");
4104
+ }
4105
+ const lsEl = findChildNs(root, "listSeparator");
4106
+ if (lsEl) {
4107
+ settings.listSeparator = attrVal(lsEl, "val");
4108
+ }
4109
+ // RSID list
4110
+ const rsidsEl = findChildNs(root, "rsids");
4111
+ if (rsidsEl) {
4112
+ const rsids = {};
4113
+ const rootEl = findChildNs(rsidsEl, "rsidRoot");
4114
+ if (rootEl) {
4115
+ rsids.rsidRoot = attrVal(rootEl, "val");
4116
+ }
4117
+ const rsidList = [];
4118
+ for (const rsidEl of findChildrenNs(rsidsEl, "rsid")) {
4119
+ const v = attrVal(rsidEl, "val");
4120
+ if (v) {
4121
+ rsidList.push(v);
4122
+ }
4123
+ }
4124
+ if (rsidList.length > 0) {
4125
+ rsids.rsid = rsidList;
4126
+ }
4127
+ if (Object.keys(rsids).length > 0) {
4128
+ settings.rsids = rsids;
4129
+ }
4130
+ }
4131
+ if (findChildNs(root, "evenAndOddHeaders")) {
4132
+ settings.evenAndOddHeaders = true;
4133
+ }
4134
+ if (findChildNs(root, "trackRevisions")) {
4135
+ settings.trackRevisions = true;
4136
+ }
4137
+ if (findChildNs(root, "mirrorMargins")) {
4138
+ settings.mirrorMargins = true;
4139
+ }
4140
+ if (findChildNs(root, "gutterAtTop")) {
4141
+ settings.gutterAtTop = true;
4142
+ }
4143
+ if (findChildNs(root, "displayBackgroundShape")) {
4144
+ settings.displayBackgroundShape = true;
4145
+ }
4146
+ if (findChildNs(root, "updateFields")) {
4147
+ settings.updateFieldsOnOpen = true;
4148
+ }
4149
+ // Hyphenation
4150
+ const autoHyphEl = findChildNs(root, "autoHyphenation");
4151
+ if (autoHyphEl) {
4152
+ settings.autoHyphenation = true;
4153
+ const hyph = { autoHyphenation: true };
4154
+ const hzEl = findChildNs(root, "hyphenationZone");
4155
+ if (hzEl) {
4156
+ hyph.hyphenationZone = attrInt(hzEl, "val");
4157
+ }
4158
+ const chlEl = findChildNs(root, "consecutiveHyphenLimit");
4159
+ if (chlEl) {
4160
+ hyph.consecutiveHyphenLimit = attrInt(chlEl, "val");
4161
+ }
4162
+ if (findChildNs(root, "doNotHyphenateCaps")) {
4163
+ hyph.doNotHyphenateCaps = true;
4164
+ }
4165
+ settings.hyphenation = hyph;
4166
+ }
4167
+ // Document protection
4168
+ const protEl = findChildNs(root, "documentProtection");
4169
+ if (protEl) {
4170
+ settings.documentProtection = {
4171
+ type: attrVal(protEl, "edit") ?? "none",
4172
+ enforcement: attrVal(protEl, "enforcement") === "1"
4173
+ };
4174
+ }
4175
+ const compatEl = findChildNs(root, "compat");
4176
+ if (compatEl) {
4177
+ const compatSettings = [];
4178
+ const compatFlags = [];
4179
+ for (const csEl of compatEl.children) {
4180
+ if (csEl.type !== "element") {
4181
+ continue;
4182
+ }
4183
+ const localName = csEl.name.replace(/^w:/, "");
4184
+ if (localName === "compatSetting") {
4185
+ const name = attrVal(csEl, "name");
4186
+ const uri = attrVal(csEl, "uri");
4187
+ const val = attrVal(csEl, "val");
4188
+ if (name === "compatibilityMode" && val !== undefined) {
4189
+ settings.compatibilityMode = parseInt(val, 10);
4190
+ }
4191
+ else if (name !== undefined && uri !== undefined && val !== undefined) {
4192
+ compatSettings.push({ name, uri, val });
4193
+ }
4194
+ }
4195
+ else {
4196
+ // Legacy compat flags (w:useFELayout, w:balanceSingleByteDoubleByteWidth, etc.)
4197
+ compatFlags.push({ name: localName, val: attrVal(csEl, "val") });
4198
+ }
4199
+ }
4200
+ if (compatSettings.length > 0) {
4201
+ settings.compatSettings = compatSettings;
4202
+ }
4203
+ if (compatFlags.length > 0) {
4204
+ settings.compatFlags = compatFlags;
4205
+ }
4206
+ }
4207
+ // Mail merge settings (preserve as raw XML)
4208
+ const mailMergeEl = findChildNs(root, "mailMerge");
4209
+ if (mailMergeEl) {
4210
+ settings.mailMergeRawXml = serializeElement(mailMergeEl);
4211
+ }
4212
+ // Write protection
4213
+ const writeProtectionEl = findChildNs(root, "writeProtection");
4214
+ if (writeProtectionEl) {
4215
+ const wp = {};
4216
+ const recommended = attrVal(writeProtectionEl, "recommended");
4217
+ if (recommended === "1" || recommended === "true") {
4218
+ wp.recommended = true;
4219
+ }
4220
+ const algName = attrVal(writeProtectionEl, "algorithmName");
4221
+ if (algName) {
4222
+ wp.algorithmName = algName;
4223
+ }
4224
+ const hashValue = attrVal(writeProtectionEl, "hashValue");
4225
+ if (hashValue) {
4226
+ wp.hashValue = hashValue;
4227
+ }
4228
+ const saltValue = attrVal(writeProtectionEl, "saltValue");
4229
+ if (saltValue) {
4230
+ wp.saltValue = saltValue;
4231
+ }
4232
+ const spinCount = attrInt(writeProtectionEl, "spinCount");
4233
+ if (spinCount !== undefined) {
4234
+ wp.spinCount = spinCount;
4235
+ }
4236
+ settings.writeProtection = wp;
4237
+ }
4238
+ // Document variables
4239
+ const docVarsEl = findChildNs(root, "docVars");
4240
+ if (docVarsEl) {
4241
+ const vars = new Map();
4242
+ for (const dvEl of findChildrenNs(docVarsEl, "docVar")) {
4243
+ const name = attrVal(dvEl, "name");
4244
+ const val = attrVal(dvEl, "val");
4245
+ if (name !== undefined && val !== undefined) {
4246
+ vars.set(name, val);
4247
+ }
4248
+ }
4249
+ if (vars.size > 0) {
4250
+ settings.docVars = vars;
4251
+ }
4252
+ }
4253
+ // Footnote/endnote properties at document level
4254
+ const fnPrEl = findChildNs(root, "footnotePr");
4255
+ if (fnPrEl) {
4256
+ const fnProps = parseNoteProperties(fnPrEl);
4257
+ if (fnProps) {
4258
+ settings.footnoteProperties = fnProps;
4259
+ }
4260
+ }
4261
+ const enPrEl = findChildNs(root, "endnotePr");
4262
+ if (enPrEl) {
4263
+ const enProps = parseNoteProperties(enPrEl);
4264
+ if (enProps) {
4265
+ settings.endnoteProperties = enProps;
4266
+ }
4267
+ }
4268
+ return settings;
4269
+ }
4270
+ // =============================================================================
4271
+ // Custom Properties Parser
4272
+ // =============================================================================
4273
+ function parseCustomPropsXml(xmlStr) {
4274
+ const doc = parseXml(xmlStr);
4275
+ const root = doc.root;
4276
+ const props = [];
4277
+ for (const propEl of root.children) {
4278
+ if (propEl.type !== "element" || propEl.name !== "property") {
4279
+ continue;
4280
+ }
4281
+ const name = propEl.attributes["name"];
4282
+ if (!name) {
4283
+ continue;
4284
+ }
4285
+ let value;
4286
+ for (const child of propEl.children) {
4287
+ if (child.type !== "element") {
4288
+ continue;
4289
+ }
4290
+ const tn = child.name;
4291
+ const tv = textContent(child);
4292
+ if (tn === "vt:lpwstr") {
4293
+ value = { type: "string", value: tv };
4294
+ }
4295
+ else if (tn === "vt:i4") {
4296
+ value = { type: "number", value: parseInt(tv, 10) };
4297
+ }
4298
+ else if (tn === "vt:r8") {
4299
+ value = { type: "number", value: parseFloat(tv) };
4300
+ }
4301
+ else if (tn === "vt:bool") {
4302
+ value = { type: "boolean", value: tv === "true" };
4303
+ }
4304
+ else if (tn === "vt:filetime") {
4305
+ value = { type: "date", value: new Date(tv) };
4306
+ }
4307
+ }
4308
+ if (value) {
4309
+ props.push({ name, value });
4310
+ }
4311
+ }
4312
+ return props;
4313
+ }
4314
+ // =============================================================================
4315
+ // Font Table Parser
4316
+ // =============================================================================
4317
+ function parseFontTableXml(xmlStr) {
4318
+ const doc = parseXml(xmlStr);
4319
+ const root = doc.root;
4320
+ const fonts = [];
4321
+ for (const fontEl of findChildrenNs(root, "font")) {
4322
+ const f = { name: attrVal(fontEl, "name") ?? "" };
4323
+ const p1 = findChildNs(fontEl, "panose1");
4324
+ if (p1) {
4325
+ f.panose1 = attrVal(p1, "val");
4326
+ }
4327
+ const cs = findChildNs(fontEl, "charset");
4328
+ if (cs) {
4329
+ f.charset = attrVal(cs, "val");
4330
+ }
4331
+ const fam = findChildNs(fontEl, "family");
4332
+ if (fam) {
4333
+ f.family = attrVal(fam, "val");
4334
+ }
4335
+ const pitch = findChildNs(fontEl, "pitch");
4336
+ if (pitch) {
4337
+ f.pitch = attrVal(pitch, "val");
4338
+ }
4339
+ // Signature
4340
+ const sigEl = findChildNs(fontEl, "sig");
4341
+ if (sigEl) {
4342
+ const sig = {};
4343
+ for (const key of ["usb0", "usb1", "usb2", "usb3", "csb0", "csb1"]) {
4344
+ const v = attrVal(sigEl, key);
4345
+ if (v !== undefined) {
4346
+ sig[key] = v;
4347
+ }
4348
+ }
4349
+ if (Object.keys(sig).length > 0) {
4350
+ f.sig = sig;
4351
+ }
4352
+ }
4353
+ // Embedded fonts
4354
+ for (const [tag, rIdKey, keyKey] of [
4355
+ ["embedRegular", "embedRegular", "embedRegularKey"],
4356
+ ["embedBold", "embedBold", "embedBoldKey"],
4357
+ ["embedItalic", "embedItalic", "embedItalicKey"],
4358
+ ["embedBoldItalic", "embedBoldItalic", "embedBoldItalicKey"]
4359
+ ]) {
4360
+ const el = findChildNs(fontEl, tag);
4361
+ if (el) {
4362
+ const rId = el.attributes["r:id"] ?? el.attributes["id"];
4363
+ if (rId) {
4364
+ f[rIdKey] = rId;
4365
+ const fontKey = attrVal(el, "fontKey");
4366
+ if (fontKey) {
4367
+ f[keyKey] = fontKey;
4368
+ }
4369
+ }
4370
+ }
4371
+ }
4372
+ fonts.push(f);
4373
+ }
4374
+ return fonts;
4375
+ }
4376
+ function parseRelationships(xmlStr) {
4377
+ const doc = parseXml(xmlStr);
4378
+ const rels = [];
4379
+ for (const child of doc.root.children) {
4380
+ if (child.type === "element" && child.name === "Relationship") {
4381
+ rels.push({
4382
+ id: child.attributes["Id"] ?? "",
4383
+ type: child.attributes["Type"] ?? "",
4384
+ target: child.attributes["Target"] ?? "",
4385
+ targetMode: child.attributes["TargetMode"]
4386
+ });
4387
+ }
4388
+ }
4389
+ return rels;
4390
+ }
4391
+ // =============================================================================
4392
+ // Main Document Parser
4393
+ // =============================================================================
4394
+ /** Recursively extract floating images, drawing shapes, and opaque drawings from an element tree. */
4395
+ function extractFloatingContent(el, images, shapes, opaqueDrawings) {
4396
+ for (const child of el.children) {
4397
+ if (child.type !== "element") {
4398
+ continue;
4399
+ }
4400
+ if (child.name === "wp:anchor") {
4401
+ // Check if this is a pic (image) or wsp (shape)
4402
+ const graphicEl = findChild(child, "a:graphic");
4403
+ const graphicDataEl = graphicEl ? findChild(graphicEl, "a:graphicData") : undefined;
4404
+ const wspEl = graphicDataEl
4405
+ ? (findChild(graphicDataEl, "wps:wsp") ?? findChildNs(graphicDataEl, "wsp"))
4406
+ : undefined;
4407
+ if (wspEl) {
4408
+ const shape = parseDrawingShape(child, wspEl);
4409
+ if (shape) {
4410
+ shapes.push(shape);
4411
+ }
4412
+ }
4413
+ else {
4414
+ const fi = parseFloatingImage(child);
4415
+ if (fi) {
4416
+ images.push(fi);
4417
+ }
4418
+ else {
4419
+ // Unknown anchor content (chart, diagram, etc.) — preserve as opaque
4420
+ const drawingEl = findDrawingParent(child);
4421
+ if (drawingEl) {
4422
+ const rids = new Set();
4423
+ collectRIds(drawingEl, rids);
4424
+ opaqueDrawings.push({
4425
+ type: "opaqueDrawing",
4426
+ rawXml: serializeElement(drawingEl),
4427
+ referencedRIds: [...rids]
4428
+ });
4429
+ }
4430
+ }
4431
+ }
4432
+ }
4433
+ else if (child.name === "wp:inline") {
4434
+ // Inline drawings that aren't images — check for chart etc.
4435
+ const graphicEl = findChild(child, "a:graphic");
4436
+ const graphicDataEl = graphicEl ? findChild(graphicEl, "a:graphicData") : undefined;
4437
+ if (graphicDataEl) {
4438
+ const picEl = findChild(graphicDataEl, "pic:pic") ?? findChildNs(graphicDataEl, "pic");
4439
+ if (!picEl) {
4440
+ // Not an image — opaque inline drawing
4441
+ // Find the w:drawing parent
4442
+ const rids = new Set();
4443
+ collectRIds(child, rids);
4444
+ // Serialize the wp:inline element wrapped in w:drawing
4445
+ const rawXml = `<w:drawing>${serializeElement(child)}</w:drawing>`;
4446
+ opaqueDrawings.push({
4447
+ type: "opaqueDrawing",
4448
+ rawXml,
4449
+ referencedRIds: [...rids]
4450
+ });
4451
+ }
4452
+ }
4453
+ }
4454
+ else {
4455
+ extractFloatingContent(child, images, shapes, opaqueDrawings);
4456
+ }
4457
+ }
4458
+ }
4459
+ /** Find the w:drawing ancestor element for serialization. */
4460
+ function findDrawingParent(anchorEl) {
4461
+ // We don't have parent refs, so we construct a synthetic w:drawing wrapper
4462
+ return {
4463
+ type: "element",
4464
+ name: "w:drawing",
4465
+ attributes: {},
4466
+ children: [anchorEl]
4467
+ };
4468
+ }
4469
+ function parseDocumentXml(xmlStr) {
4470
+ const doc = parseXml(xmlStr);
4471
+ const root = doc.root;
4472
+ // Parse background
4473
+ let background;
4474
+ const bgEl = findChildNs(root, "background");
4475
+ if (bgEl) {
4476
+ const bg = {};
4477
+ const color = attrVal(bgEl, "color");
4478
+ if (color) {
4479
+ bg.color = color;
4480
+ }
4481
+ const themeColor = attrVal(bgEl, "themeColor");
4482
+ if (themeColor) {
4483
+ bg.themeColor = themeColor;
4484
+ }
4485
+ const themeShade = attrVal(bgEl, "themeShade");
4486
+ if (themeShade) {
4487
+ bg.themeShade = themeShade;
4488
+ }
4489
+ const themeTint = attrVal(bgEl, "themeTint");
4490
+ if (themeTint) {
4491
+ bg.themeTint = themeTint;
4492
+ }
4493
+ background = bg;
4494
+ }
4495
+ const bodyEl = findChildNs(root, "body") ?? findChild(root, "w:body");
4496
+ if (!bodyEl) {
4497
+ throw new DocxParseError("Missing w:body element in document.xml");
4498
+ }
4499
+ const body = [];
4500
+ let sectionProperties;
4501
+ // Collect floating images, drawing shapes, and opaque drawings from the whole body
4502
+ const floatingImages = [];
4503
+ const drawingShapes = [];
4504
+ const opaqueDrawings = [];
4505
+ extractFloatingContent(bodyEl, floatingImages, drawingShapes, opaqueDrawings);
4506
+ for (const child of bodyEl.children) {
4507
+ if (child.type !== "element") {
4508
+ continue;
4509
+ }
4510
+ const name = child.name.replace(/^w:/, "");
4511
+ switch (name) {
4512
+ case "p":
4513
+ body.push(parseParagraph(child));
4514
+ break;
4515
+ case "tbl":
4516
+ body.push(parseTable(child));
4517
+ break;
4518
+ case "sectPr":
4519
+ // Final section properties at the body level
4520
+ sectionProperties = parseSectionProperties(child);
4521
+ break;
4522
+ case "sdt": {
4523
+ const sdtResult = parseSdt(child);
4524
+ if (sdtResult) {
4525
+ body.push(sdtResult);
4526
+ }
4527
+ break;
4528
+ }
4529
+ case "altChunk": {
4530
+ const rId = child.attributes["r:id"] ?? child.attributes["id"];
4531
+ if (rId) {
4532
+ body.push({ type: "altChunk", rId });
4533
+ }
4534
+ break;
4535
+ }
4536
+ default: {
4537
+ // Check for math namespace
4538
+ if (child.name === "m:oMathPara") {
4539
+ body.push(parseMathBlock(child));
4540
+ }
4541
+ else if (child.name === "m:oMath") {
4542
+ body.push({ type: "math", content: parseMathContent(child) });
4543
+ }
4544
+ // Check for VML pict (textbox)
4545
+ if (name === "pict" || child.name === "w:pict") {
4546
+ const tb = parseTextBox(child);
4547
+ if (tb) {
4548
+ body.push(tb);
4549
+ }
4550
+ }
4551
+ break;
4552
+ }
4553
+ }
4554
+ }
4555
+ // Append floating images as top-level body content
4556
+ for (const fi of floatingImages) {
4557
+ body.push(fi);
4558
+ }
4559
+ // Append drawing shapes as top-level body content
4560
+ for (const ds of drawingShapes) {
4561
+ body.push(ds);
4562
+ }
4563
+ // Append opaque drawings as top-level body content
4564
+ for (const od of opaqueDrawings) {
4565
+ body.push(od);
4566
+ }
4567
+ return { body, sectionProperties, background };
4568
+ }
4569
+ // =============================================================================
4570
+ // Public API - Read DOCX
4571
+ // =============================================================================
4572
+ /**
4573
+ * Read a DOCX file from a Uint8Array buffer and parse it into a DocxDocument model.
4574
+ */
4575
+ export async function readDocx(buffer) {
4576
+ try {
4577
+ return await _readDocxInner(buffer);
4578
+ }
4579
+ catch (e) {
4580
+ if (e instanceof DocxError) {
4581
+ throw e;
4582
+ }
4583
+ const msg = e instanceof Error ? e.message : String(e);
4584
+ throw new DocxParseError(`Failed to read DOCX: ${msg}`, { cause: e });
4585
+ }
4586
+ }
4587
+ async function _readDocxInner(buffer) {
4588
+ const reader = unzip(buffer);
4589
+ const entries = new Map();
4590
+ for await (const entry of reader.entries()) {
4591
+ const data = await entry.bytes();
4592
+ // Normalize path: remove leading slash, normalize separators
4593
+ const path = entry.path.replace(/^\//, "").replace(/\\/g, "/");
4594
+ entries.set(path, data);
4595
+ }
4596
+ const decoder = new TextDecoder("utf-8");
4597
+ const consumedPaths = new Set(["[Content_Types].xml"]);
4598
+ const getText = (path) => {
4599
+ const data = entries.get(path);
4600
+ if (data) {
4601
+ consumedPaths.add(path);
4602
+ }
4603
+ return data ? decoder.decode(data) : undefined;
4604
+ };
4605
+ // Parse document relationships (must be before parseDocumentXml for hyperlink resolution)
4606
+ const docRelsXml = getText("word/_rels/document.xml.rels");
4607
+ const docRels = docRelsXml ? parseRelationships(docRelsXml) : [];
4608
+ const _relMap = new Map(docRels.map(r => [r.id, r]));
4609
+ // Set module-level context for parseParagraph hyperlink resolution
4610
+ _parseRelMap = _relMap;
4611
+ // Parse document.xml (required)
4612
+ const documentXml = getText("word/document.xml");
4613
+ if (!documentXml) {
4614
+ throw new DocxMissingPartError("word/document.xml");
4615
+ }
4616
+ const { body, sectionProperties, background } = parseDocumentXml(documentXml);
4617
+ // Parse styles
4618
+ const stylesXml = getText("word/styles.xml");
4619
+ const stylesResult = stylesXml ? parseStyles(stylesXml) : undefined;
4620
+ // Parse numbering
4621
+ const numberingXml = getText("word/numbering.xml");
4622
+ const numberingResult = numberingXml ? parseNumberingXml(numberingXml) : undefined;
4623
+ // Parse footnotes/endnotes
4624
+ const footnotesXml = getText("word/footnotes.xml");
4625
+ const footnotes = footnotesXml ? parseNotesXml(footnotesXml, "footnote") : undefined;
4626
+ const endnotesXml = getText("word/endnotes.xml");
4627
+ const endnotes = endnotesXml ? parseNotesXml(endnotesXml, "endnote") : undefined;
4628
+ // Parse headers/footers + detect watermarks
4629
+ const headers = new Map();
4630
+ const footers = new Map();
4631
+ let watermark;
4632
+ for (const rel of docRels) {
4633
+ if (rel.type === RelType.Header) {
4634
+ const xml = getText(resolvePartPath("word/document.xml", rel.target));
4635
+ if (xml) {
4636
+ // Parse XML once, re-use for both header content and watermark detection
4637
+ const headerRoot = parseXml(xml).root;
4638
+ headers.set(rel.id, { content: parseHeaderFooterRoot(headerRoot), rId: rel.id });
4639
+ if (!watermark) {
4640
+ watermark = detectWatermarkFromRoot(headerRoot);
4641
+ }
4642
+ }
4643
+ }
4644
+ else if (rel.type === RelType.Footer) {
4645
+ const xml = getText(resolvePartPath("word/document.xml", rel.target));
4646
+ if (xml) {
4647
+ footers.set(rel.id, { content: parseHeaderFooterXml(xml), rId: rel.id });
4648
+ }
4649
+ }
4650
+ }
4651
+ // Parse settings
4652
+ const settingsXml = getText("word/settings.xml");
4653
+ const settings = settingsXml ? parseSettingsXml(settingsXml) : undefined;
4654
+ // Parse web settings
4655
+ const webSettingsXml = getText("word/webSettings.xml");
4656
+ const webSettings = webSettingsXml ? parseWebSettings(webSettingsXml) : undefined;
4657
+ // Parse people
4658
+ const peopleXml = getText("word/people.xml");
4659
+ const people = peopleXml ? parsePeople(peopleXml) : undefined;
4660
+ // Parse thumbnail (from package rels)
4661
+ let thumbnail;
4662
+ const packageRelsXml = getText("_rels/.rels");
4663
+ if (packageRelsXml) {
4664
+ const pkgRels = parseRelationships(packageRelsXml);
4665
+ for (const rel of pkgRels) {
4666
+ if (rel.type.endsWith("/thumbnail")) {
4667
+ // Target in package rels is relative to package root; may include or exclude leading slash
4668
+ let target = rel.target;
4669
+ if (target.startsWith("/")) {
4670
+ target = target.substring(1);
4671
+ }
4672
+ // If the target doesn't include docProps/ prefix, add it (some writers emit bare filenames)
4673
+ const normalized = target.includes("/") ? target : `docProps/${target}`;
4674
+ consumedPaths.add(normalized);
4675
+ const thumbData = entries.get(normalized);
4676
+ if (thumbData) {
4677
+ const ext = normalized.split(".").pop()?.toLowerCase();
4678
+ const ct = ext === "jpeg" || ext === "jpg"
4679
+ ? "image/jpeg"
4680
+ : ext === "png"
4681
+ ? "image/png"
4682
+ : "image/x-wmf";
4683
+ thumbnail = { contentType: ct, data: thumbData };
4684
+ }
4685
+ break;
4686
+ }
4687
+ }
4688
+ }
4689
+ // Parse font table
4690
+ const fontTableXml = getText("word/fontTable.xml");
4691
+ const fonts = fontTableXml ? parseFontTableXml(fontTableXml) : undefined;
4692
+ // Parse embedded fonts
4693
+ let embeddedFonts;
4694
+ const fontTableRelsXml = getText("word/_rels/fontTable.xml.rels");
4695
+ if (fontTableRelsXml && fonts) {
4696
+ const fontRels = parseRelationships(fontTableRelsXml);
4697
+ const efs = [];
4698
+ // Build rId → { key } map from font table
4699
+ const rIdToKey = new Map();
4700
+ for (const f of fonts) {
4701
+ if (f.embedRegular && f.embedRegularKey) {
4702
+ rIdToKey.set(f.embedRegular, f.embedRegularKey);
4703
+ }
4704
+ if (f.embedBold && f.embedBoldKey) {
4705
+ rIdToKey.set(f.embedBold, f.embedBoldKey);
4706
+ }
4707
+ if (f.embedItalic && f.embedItalicKey) {
4708
+ rIdToKey.set(f.embedItalic, f.embedItalicKey);
4709
+ }
4710
+ if (f.embedBoldItalic && f.embedBoldItalicKey) {
4711
+ rIdToKey.set(f.embedBoldItalic, f.embedBoldItalicKey);
4712
+ }
4713
+ }
4714
+ for (const rel of fontRels) {
4715
+ if (rel.type === RelType.Font) {
4716
+ const fontPath = resolvePartPath("word/fontTable.xml", rel.target);
4717
+ consumedPaths.add(fontPath);
4718
+ const data = entries.get(fontPath);
4719
+ if (data) {
4720
+ const fileName = rel.target.split("/").pop() ?? "";
4721
+ const fontKey = rIdToKey.get(rel.id);
4722
+ const ef = {
4723
+ rId: rel.id,
4724
+ data,
4725
+ fileName
4726
+ };
4727
+ if (fontKey) {
4728
+ ef.fontKey = fontKey;
4729
+ }
4730
+ efs.push(ef);
4731
+ }
4732
+ }
4733
+ }
4734
+ if (efs.length > 0) {
4735
+ embeddedFonts = efs;
4736
+ }
4737
+ }
4738
+ // Parse Custom XML parts (for SDT data binding)
4739
+ const customXmlParts = [];
4740
+ for (const rel of docRels) {
4741
+ if (rel.type === RelType.CustomXml) {
4742
+ const targetPath = resolvePartPath("word/document.xml", rel.target);
4743
+ consumedPaths.add(targetPath);
4744
+ const xmlContent = getText(targetPath);
4745
+ if (!xmlContent) {
4746
+ continue;
4747
+ }
4748
+ // Parse itemProps*.xml to get storeItemID
4749
+ const fileName = targetPath.split("/").pop() ?? "";
4750
+ // itemProps file is typically at the same directory
4751
+ const dir = targetPath.substring(0, targetPath.lastIndexOf("/"));
4752
+ // Extract item number from fileName (e.g. "item1.xml" → "1")
4753
+ const match = fileName.match(/item(\d+)\.xml$/);
4754
+ let itemId = "";
4755
+ let schemaReferences;
4756
+ if (match) {
4757
+ const num = match[1];
4758
+ const propsPath = `${dir}/itemProps${num}.xml`;
4759
+ consumedPaths.add(propsPath);
4760
+ const propsXml = getText(propsPath);
4761
+ if (propsXml) {
4762
+ const propsDoc = parseXml(propsXml);
4763
+ const dsItemEl = propsDoc.root;
4764
+ const id = dsItemEl.attributes["ds:itemID"];
4765
+ if (id) {
4766
+ itemId = id.replace(/[{}]/g, "");
4767
+ }
4768
+ // Schema references
4769
+ const refs = [];
4770
+ const schemaRefsEl = findChild(dsItemEl, "ds:schemaRefs") ?? findChild(dsItemEl, "schemaRefs");
4771
+ if (schemaRefsEl) {
4772
+ for (const srChild of schemaRefsEl.children) {
4773
+ if (srChild.type === "element") {
4774
+ const uri = srChild.attributes["ds:uri"] ?? srChild.attributes["uri"];
4775
+ if (uri) {
4776
+ refs.push(uri);
4777
+ }
4778
+ }
4779
+ }
4780
+ }
4781
+ if (refs.length > 0) {
4782
+ schemaReferences = refs;
4783
+ }
4784
+ }
4785
+ }
4786
+ customXmlParts.push({
4787
+ itemId,
4788
+ xmlContent,
4789
+ fileName,
4790
+ schemaReferences
4791
+ });
4792
+ }
4793
+ }
4794
+ // Parse core properties
4795
+ const corePropsXml = getText("docProps/core.xml");
4796
+ const coreProperties = corePropsXml ? parseCoreProps(corePropsXml) : undefined;
4797
+ // Parse app properties
4798
+ const appPropsXml = getText("docProps/app.xml");
4799
+ const appProperties = appPropsXml ? parseAppProps(appPropsXml) : undefined;
4800
+ // Parse comments
4801
+ const commentsXml = getText("word/comments.xml");
4802
+ let comments = commentsXml ? parseCommentsXml(commentsXml) : undefined;
4803
+ // Merge in commentsExtended.xml data if present
4804
+ const commentsExtXml = getText("word/commentsExtended.xml");
4805
+ if (commentsExtXml && comments) {
4806
+ const extMap = parseCommentsExtendedXml(commentsExtXml);
4807
+ comments = comments.map(c => {
4808
+ const firstPara = c.content[0];
4809
+ if (!firstPara?.paraId) {
4810
+ return c;
4811
+ }
4812
+ const ext = extMap.get(firstPara.paraId);
4813
+ if (!ext) {
4814
+ return c;
4815
+ }
4816
+ return {
4817
+ ...c,
4818
+ ...(ext.done !== undefined ? { done: ext.done } : {}),
4819
+ ...(ext.parentId !== undefined ? { parentId: ext.parentId } : {})
4820
+ };
4821
+ });
4822
+ }
4823
+ // Parse custom properties
4824
+ const customPropsXml = getText("docProps/custom.xml");
4825
+ const customProperties = customPropsXml ? parseCustomPropsXml(customPropsXml) : undefined;
4826
+ // Parse theme
4827
+ const themeXml = getText("word/theme/theme1.xml");
4828
+ const theme = themeXml ? parseThemeXml(themeXml) : undefined;
4829
+ // Collect images
4830
+ const images = [];
4831
+ for (const rel of docRels) {
4832
+ if (rel.type === RelType.Image) {
4833
+ const imgPath = resolvePartPath("word/document.xml", rel.target);
4834
+ consumedPaths.add(imgPath);
4835
+ const data = entries.get(imgPath);
4836
+ if (data) {
4837
+ const fileName = rel.target.split("/").pop() ?? "";
4838
+ const ext = fileName.split(".").pop()?.toLowerCase() ?? "png";
4839
+ images.push({
4840
+ data,
4841
+ mediaType: ext,
4842
+ fileName,
4843
+ rId: rel.id
4844
+ });
4845
+ }
4846
+ }
4847
+ }
4848
+ // Collect opaque (unrecognized) parts for round-trip preservation
4849
+ const opaqueParts = [];
4850
+ for (const [path, data] of entries) {
4851
+ // Skip consumed paths and all .rels files (structural)
4852
+ if (consumedPaths.has(path) || path.includes("_rels/")) {
4853
+ continue;
4854
+ }
4855
+ // Parse rels for this part if they exist
4856
+ const partRelsPath = getPartRelsPath(path);
4857
+ const partRelsData = entries.get(partRelsPath);
4858
+ let relationships;
4859
+ if (partRelsData) {
4860
+ const rels = parseRelationships(decoder.decode(partRelsData));
4861
+ relationships = rels.map(r => ({
4862
+ id: r.id,
4863
+ type: r.type,
4864
+ target: r.target,
4865
+ targetMode: r.targetMode === "External" ? "External" : undefined
4866
+ }));
4867
+ }
4868
+ opaqueParts.push({ path, data, relationships });
4869
+ }
4870
+ // Resolve altChunk data: body elements of type "altChunk" reference a rId.
4871
+ // The target file is stored in docRels + entries. Move target data from entries
4872
+ // (and remove from opaqueParts, since it's now part of the altChunk body element).
4873
+ for (const item of body) {
4874
+ if (item.type === "altChunk" && item.rId) {
4875
+ const rel = _relMap.get(item.rId);
4876
+ if (rel) {
4877
+ const target = resolvePartPath("word/document.xml", rel.target);
4878
+ const targetData = entries.get(target);
4879
+ if (targetData) {
4880
+ const fileName = target.split("/").pop();
4881
+ item.data = targetData;
4882
+ item.fileName = fileName;
4883
+ // Infer content type from extension
4884
+ const ext = fileName?.split(".").pop()?.toLowerCase();
4885
+ if (ext === "html" || ext === "htm") {
4886
+ item.contentType = "text/html";
4887
+ }
4888
+ else if (ext === "rtf") {
4889
+ item.contentType = "text/rtf";
4890
+ }
4891
+ else if (ext === "txt") {
4892
+ item.contentType = "text/plain";
4893
+ }
4894
+ }
4895
+ }
4896
+ }
4897
+ }
4898
+ return {
4899
+ body,
4900
+ sectionProperties,
4901
+ styles: stylesResult?.styles,
4902
+ docDefaults: stylesResult?.docDefaults,
4903
+ abstractNumberings: numberingResult?.abstractNums,
4904
+ numberingInstances: numberingResult?.instances,
4905
+ numPicBullets: numberingResult?.numPicBullets && numberingResult.numPicBullets.length > 0
4906
+ ? numberingResult.numPicBullets
4907
+ : undefined,
4908
+ headers: headers.size > 0 ? headers : undefined,
4909
+ footers: footers.size > 0 ? footers : undefined,
4910
+ footnotes: footnotes && footnotes.length > 0 ? footnotes : undefined,
4911
+ endnotes: endnotes && endnotes.length > 0 ? endnotes : undefined,
4912
+ images: images.length > 0 ? images : undefined,
4913
+ fonts: fonts && fonts.length > 0 ? fonts : undefined,
4914
+ embeddedFonts: embeddedFonts && embeddedFonts.length > 0 ? embeddedFonts : undefined,
4915
+ customXmlParts: customXmlParts.length > 0 ? customXmlParts : undefined,
4916
+ webSettings,
4917
+ thumbnail,
4918
+ people: people && people.length > 0 ? people : undefined,
4919
+ settings,
4920
+ coreProperties,
4921
+ appProperties,
4922
+ comments: comments && comments.length > 0 ? comments : undefined,
4923
+ background,
4924
+ customProperties: customProperties && customProperties.length > 0 ? customProperties : undefined,
4925
+ theme,
4926
+ watermark,
4927
+ opaqueParts: opaqueParts.length > 0 ? opaqueParts : undefined
4928
+ };
4929
+ }