docxmlater 10.0.2 → 10.0.4

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 (398) hide show
  1. package/README.md +3 -2
  2. package/dist/constants/legacyCompatFlags.d.ts.map +1 -1
  3. package/dist/constants/legacyCompatFlags.js.map +1 -1
  4. package/dist/constants/limits.d.ts +0 -27
  5. package/dist/constants/limits.d.ts.map +1 -1
  6. package/dist/constants/limits.js +13 -13
  7. package/dist/constants/limits.js.map +1 -1
  8. package/dist/core/Document.d.ts +23 -19
  9. package/dist/core/Document.d.ts.map +1 -1
  10. package/dist/core/Document.js +197 -63
  11. package/dist/core/Document.js.map +1 -1
  12. package/dist/core/DocumentContent.d.ts.map +1 -1
  13. package/dist/core/DocumentContent.js.map +1 -1
  14. package/dist/core/DocumentGenerator.d.ts.map +1 -1
  15. package/dist/core/DocumentGenerator.js +59 -24
  16. package/dist/core/DocumentGenerator.js.map +1 -1
  17. package/dist/core/DocumentIdManager.d.ts.map +1 -1
  18. package/dist/core/DocumentIdManager.js.map +1 -1
  19. package/dist/core/DocumentParser.d.ts +6 -6
  20. package/dist/core/DocumentParser.d.ts.map +1 -1
  21. package/dist/core/DocumentParser.js +86 -55
  22. package/dist/core/DocumentParser.js.map +1 -1
  23. package/dist/core/DocumentValidator.d.ts.map +1 -1
  24. package/dist/core/DocumentValidator.js.map +1 -1
  25. package/dist/core/Relationship.d.ts.map +1 -1
  26. package/dist/core/Relationship.js +1 -1
  27. package/dist/core/Relationship.js.map +1 -1
  28. package/dist/core/RelationshipManager.js +3 -3
  29. package/dist/core/RelationshipManager.js.map +1 -1
  30. package/dist/elements/AlternateContent.js.map +1 -1
  31. package/dist/elements/Bookmark.d.ts.map +1 -1
  32. package/dist/elements/Bookmark.js.map +1 -1
  33. package/dist/elements/BookmarkManager.d.ts.map +1 -1
  34. package/dist/elements/BookmarkManager.js.map +1 -1
  35. package/dist/elements/Comment.js +1 -1
  36. package/dist/elements/Comment.js.map +1 -1
  37. package/dist/elements/CommentManager.d.ts.map +1 -1
  38. package/dist/elements/CommentManager.js +8 -2
  39. package/dist/elements/CommentManager.js.map +1 -1
  40. package/dist/elements/CommonTypes.d.ts.map +1 -1
  41. package/dist/elements/CommonTypes.js +1 -2
  42. package/dist/elements/CommonTypes.js.map +1 -1
  43. package/dist/elements/CustomXml.js.map +1 -1
  44. package/dist/elements/Endnote.d.ts.map +1 -1
  45. package/dist/elements/Endnote.js.map +1 -1
  46. package/dist/elements/EndnoteManager.d.ts.map +1 -1
  47. package/dist/elements/EndnoteManager.js.map +1 -1
  48. package/dist/elements/Field.d.ts.map +1 -1
  49. package/dist/elements/Field.js +31 -28
  50. package/dist/elements/Field.js.map +1 -1
  51. package/dist/elements/FieldHelpers.d.ts.map +1 -1
  52. package/dist/elements/FieldHelpers.js +6 -6
  53. package/dist/elements/FieldHelpers.js.map +1 -1
  54. package/dist/elements/FontManager.d.ts.map +1 -1
  55. package/dist/elements/FontManager.js.map +1 -1
  56. package/dist/elements/Footer.js.map +1 -1
  57. package/dist/elements/Footnote.d.ts.map +1 -1
  58. package/dist/elements/Footnote.js.map +1 -1
  59. package/dist/elements/FootnoteManager.d.ts.map +1 -1
  60. package/dist/elements/FootnoteManager.js.map +1 -1
  61. package/dist/elements/Header.js.map +1 -1
  62. package/dist/elements/HeaderFooterManager.js.map +1 -1
  63. package/dist/elements/Hyperlink.d.ts.map +1 -1
  64. package/dist/elements/Hyperlink.js +5 -5
  65. package/dist/elements/Hyperlink.js.map +1 -1
  66. package/dist/elements/Image.d.ts +2 -2
  67. package/dist/elements/Image.d.ts.map +1 -1
  68. package/dist/elements/Image.js +21 -5
  69. package/dist/elements/Image.js.map +1 -1
  70. package/dist/elements/ImageManager.d.ts.map +1 -1
  71. package/dist/elements/ImageManager.js +2 -2
  72. package/dist/elements/ImageManager.js.map +1 -1
  73. package/dist/elements/ImageRun.js.map +1 -1
  74. package/dist/elements/MathElement.js.map +1 -1
  75. package/dist/elements/Paragraph.d.ts +8 -0
  76. package/dist/elements/Paragraph.d.ts.map +1 -1
  77. package/dist/elements/Paragraph.js +153 -118
  78. package/dist/elements/Paragraph.js.map +1 -1
  79. package/dist/elements/PreservedElement.js.map +1 -1
  80. package/dist/elements/PropertyChangeTypes.js.map +1 -1
  81. package/dist/elements/RangeMarker.js.map +1 -1
  82. package/dist/elements/Revision.d.ts +1 -0
  83. package/dist/elements/Revision.d.ts.map +1 -1
  84. package/dist/elements/Revision.js +44 -5
  85. package/dist/elements/Revision.js.map +1 -1
  86. package/dist/elements/RevisionContent.js.map +1 -1
  87. package/dist/elements/RevisionManager.d.ts.map +1 -1
  88. package/dist/elements/RevisionManager.js.map +1 -1
  89. package/dist/elements/Run.d.ts.map +1 -1
  90. package/dist/elements/Run.js +1 -3
  91. package/dist/elements/Run.js.map +1 -1
  92. package/dist/elements/Section.d.ts.map +1 -1
  93. package/dist/elements/Section.js +127 -118
  94. package/dist/elements/Section.js.map +1 -1
  95. package/dist/elements/Shape.d.ts.map +1 -1
  96. package/dist/elements/Shape.js +21 -0
  97. package/dist/elements/Shape.js.map +1 -1
  98. package/dist/elements/StructuredDocumentTag.d.ts.map +1 -1
  99. package/dist/elements/StructuredDocumentTag.js +20 -8
  100. package/dist/elements/StructuredDocumentTag.js.map +1 -1
  101. package/dist/elements/Table.d.ts +2 -2
  102. package/dist/elements/Table.d.ts.map +1 -1
  103. package/dist/elements/Table.js +29 -35
  104. package/dist/elements/Table.js.map +1 -1
  105. package/dist/elements/TableCell.d.ts +2 -2
  106. package/dist/elements/TableCell.d.ts.map +1 -1
  107. package/dist/elements/TableCell.js +63 -67
  108. package/dist/elements/TableCell.js.map +1 -1
  109. package/dist/elements/TableGridChange.js.map +1 -1
  110. package/dist/elements/TableOfContents.d.ts +6 -6
  111. package/dist/elements/TableOfContents.d.ts.map +1 -1
  112. package/dist/elements/TableOfContents.js.map +1 -1
  113. package/dist/elements/TableOfContentsElement.js.map +1 -1
  114. package/dist/elements/TableRow.d.ts.map +1 -1
  115. package/dist/elements/TableRow.js +65 -47
  116. package/dist/elements/TableRow.js.map +1 -1
  117. package/dist/elements/TextBox.d.ts.map +1 -1
  118. package/dist/elements/TextBox.js +1 -1
  119. package/dist/elements/TextBox.js.map +1 -1
  120. package/dist/formatting/AbstractNumbering.d.ts +1 -1
  121. package/dist/formatting/AbstractNumbering.d.ts.map +1 -1
  122. package/dist/formatting/AbstractNumbering.js +11 -11
  123. package/dist/formatting/AbstractNumbering.js.map +1 -1
  124. package/dist/formatting/NumberingInstance.d.ts.map +1 -1
  125. package/dist/formatting/NumberingInstance.js +4 -4
  126. package/dist/formatting/NumberingInstance.js.map +1 -1
  127. package/dist/formatting/NumberingLevel.d.ts.map +1 -1
  128. package/dist/formatting/NumberingLevel.js +26 -26
  129. package/dist/formatting/NumberingLevel.js.map +1 -1
  130. package/dist/formatting/NumberingManager.d.ts +1 -1
  131. package/dist/formatting/NumberingManager.d.ts.map +1 -1
  132. package/dist/formatting/NumberingManager.js.map +1 -1
  133. package/dist/formatting/Style.d.ts.map +1 -1
  134. package/dist/formatting/Style.js +87 -95
  135. package/dist/formatting/Style.js.map +1 -1
  136. package/dist/formatting/StylesManager.d.ts +3 -3
  137. package/dist/formatting/StylesManager.d.ts.map +1 -1
  138. package/dist/formatting/StylesManager.js.map +1 -1
  139. package/dist/helpers/CleanupHelper.js.map +1 -1
  140. package/dist/images/ImageOptimizer.js.map +1 -1
  141. package/dist/index.js.map +1 -1
  142. package/dist/managers/DrawingManager.d.ts.map +1 -1
  143. package/dist/managers/DrawingManager.js.map +1 -1
  144. package/dist/tracking/DocumentTrackingContext.js.map +1 -1
  145. package/dist/tracking/TrackingContext.js.map +1 -1
  146. package/dist/types/compatibility-types.js.map +1 -1
  147. package/dist/types/formatting.js.map +1 -1
  148. package/dist/types/list-types.d.ts +4 -4
  149. package/dist/types/list-types.d.ts.map +1 -1
  150. package/dist/types/list-types.js.map +1 -1
  151. package/dist/types/settings-types.js.map +1 -1
  152. package/dist/types/styleConfig.js.map +1 -1
  153. package/dist/utils/ChangelogGenerator.d.ts.map +1 -1
  154. package/dist/utils/ChangelogGenerator.js.map +1 -1
  155. package/dist/utils/CompatibilityUpgrader.d.ts.map +1 -1
  156. package/dist/utils/CompatibilityUpgrader.js +7 -7
  157. package/dist/utils/CompatibilityUpgrader.js.map +1 -1
  158. package/dist/utils/InMemoryRevisionAcceptor.d.ts.map +1 -1
  159. package/dist/utils/InMemoryRevisionAcceptor.js +23 -2
  160. package/dist/utils/InMemoryRevisionAcceptor.js.map +1 -1
  161. package/dist/utils/MoveOperationHelper.js.map +1 -1
  162. package/dist/utils/RevisionAwareProcessor.js.map +1 -1
  163. package/dist/utils/RevisionWalker.js.map +1 -1
  164. package/dist/utils/SelectiveRevisionAcceptor.d.ts +1 -0
  165. package/dist/utils/SelectiveRevisionAcceptor.d.ts.map +1 -1
  166. package/dist/utils/SelectiveRevisionAcceptor.js +46 -0
  167. package/dist/utils/SelectiveRevisionAcceptor.js.map +1 -1
  168. package/dist/utils/ShadingResolver.js +1 -1
  169. package/dist/utils/ShadingResolver.js.map +1 -1
  170. package/dist/utils/acceptRevisions.d.ts +0 -28
  171. package/dist/utils/acceptRevisions.d.ts.map +1 -1
  172. package/dist/utils/acceptRevisions.js +5 -7
  173. package/dist/utils/acceptRevisions.js.map +1 -1
  174. package/dist/utils/cnfStyleDecoder.js +1 -1
  175. package/dist/utils/cnfStyleDecoder.js.map +1 -1
  176. package/dist/utils/corruptionDetection.js.map +1 -1
  177. package/dist/utils/dateFormatting.js.map +1 -1
  178. package/dist/utils/deepClone.d.ts +0 -1
  179. package/dist/utils/deepClone.d.ts.map +1 -1
  180. package/dist/utils/deepClone.js +0 -7
  181. package/dist/utils/deepClone.js.map +1 -1
  182. package/dist/utils/diagnostics.d.ts +2 -2
  183. package/dist/utils/diagnostics.d.ts.map +1 -1
  184. package/dist/utils/diagnostics.js.map +1 -1
  185. package/dist/utils/errorHandling.js.map +1 -1
  186. package/dist/utils/formatting.js.map +1 -1
  187. package/dist/utils/list-detection.d.ts +2 -2
  188. package/dist/utils/list-detection.d.ts.map +1 -1
  189. package/dist/utils/list-detection.js +3 -3
  190. package/dist/utils/list-detection.js.map +1 -1
  191. package/dist/utils/logger.d.ts +2 -4
  192. package/dist/utils/logger.d.ts.map +1 -1
  193. package/dist/utils/logger.js +0 -2
  194. package/dist/utils/logger.js.map +1 -1
  195. package/dist/utils/parsingHelpers.js.map +1 -1
  196. package/dist/utils/stripTrackedChanges.d.ts +0 -19
  197. package/dist/utils/stripTrackedChanges.d.ts.map +1 -1
  198. package/dist/utils/stripTrackedChanges.js +0 -2
  199. package/dist/utils/stripTrackedChanges.js.map +1 -1
  200. package/dist/utils/textDiff.js.map +1 -1
  201. package/dist/utils/units.js.map +1 -1
  202. package/dist/utils/validation.d.ts.map +1 -1
  203. package/dist/utils/validation.js.map +1 -1
  204. package/dist/utils/xmlSanitization.js.map +1 -1
  205. package/dist/validation/RevisionAutoFixer.js.map +1 -1
  206. package/dist/validation/RevisionValidator.js.map +1 -1
  207. package/dist/validation/ValidationRules.js.map +1 -1
  208. package/dist/validation/index.js.map +1 -1
  209. package/dist/xml/XMLBuilder.d.ts.map +1 -1
  210. package/dist/xml/XMLBuilder.js +10 -0
  211. package/dist/xml/XMLBuilder.js.map +1 -1
  212. package/dist/xml/XMLParser.d.ts.map +1 -1
  213. package/dist/xml/XMLParser.js +4 -5
  214. package/dist/xml/XMLParser.js.map +1 -1
  215. package/dist/zip/ZipHandler.js.map +1 -1
  216. package/dist/zip/ZipReader.js.map +1 -1
  217. package/dist/zip/ZipWriter.js.map +1 -1
  218. package/dist/zip/errors.js.map +1 -1
  219. package/dist/zip/types.js.map +1 -1
  220. package/package.json +34 -4
  221. package/src/__tests__/helper-methods.test.ts +512 -0
  222. package/src/constants/legacyCompatFlags.ts +138 -0
  223. package/src/constants/limits.ts +50 -0
  224. package/src/core/CLAUDE.md +109 -0
  225. package/src/core/Document.ts +15569 -0
  226. package/src/core/DocumentContent.ts +467 -0
  227. package/src/core/DocumentGenerator.ts +1104 -0
  228. package/src/core/DocumentIdManager.ts +158 -0
  229. package/src/core/DocumentParser.ts +10140 -0
  230. package/src/core/DocumentValidator.ts +372 -0
  231. package/src/core/Relationship.ts +367 -0
  232. package/src/core/RelationshipManager.ts +428 -0
  233. package/src/elements/AlternateContent.ts +42 -0
  234. package/src/elements/Bookmark.ts +210 -0
  235. package/src/elements/BookmarkManager.ts +250 -0
  236. package/src/elements/CLAUDE.md +126 -0
  237. package/src/elements/Comment.ts +359 -0
  238. package/src/elements/CommentManager.ts +502 -0
  239. package/src/elements/CommonTypes.ts +549 -0
  240. package/src/elements/CustomXml.ts +36 -0
  241. package/src/elements/Endnote.ts +217 -0
  242. package/src/elements/EndnoteManager.ts +249 -0
  243. package/src/elements/Field.ts +1233 -0
  244. package/src/elements/FieldHelpers.ts +333 -0
  245. package/src/elements/FontManager.ts +339 -0
  246. package/src/elements/Footer.ts +269 -0
  247. package/src/elements/Footnote.ts +217 -0
  248. package/src/elements/FootnoteManager.ts +249 -0
  249. package/src/elements/Header.ts +269 -0
  250. package/src/elements/HeaderFooterManager.ts +219 -0
  251. package/src/elements/Hyperlink.ts +1146 -0
  252. package/src/elements/Image.ts +1756 -0
  253. package/src/elements/ImageManager.ts +432 -0
  254. package/src/elements/ImageRun.ts +59 -0
  255. package/src/elements/MathElement.ts +65 -0
  256. package/src/elements/Paragraph.ts +4287 -0
  257. package/src/elements/PreservedElement.ts +53 -0
  258. package/src/elements/PropertyChangeTypes.ts +442 -0
  259. package/src/elements/RangeMarker.ts +400 -0
  260. package/src/elements/Revision.ts +1217 -0
  261. package/src/elements/RevisionContent.ts +73 -0
  262. package/src/elements/RevisionManager.ts +1070 -0
  263. package/src/elements/Run.ts +3068 -0
  264. package/src/elements/Section.ts +1421 -0
  265. package/src/elements/Shape.ts +873 -0
  266. package/src/elements/StructuredDocumentTag.ts +978 -0
  267. package/src/elements/Table.ts +2524 -0
  268. package/src/elements/TableCell.ts +1586 -0
  269. package/src/elements/TableGridChange.ts +151 -0
  270. package/src/elements/TableOfContents.ts +691 -0
  271. package/src/elements/TableOfContentsElement.ts +89 -0
  272. package/src/elements/TableRow.ts +906 -0
  273. package/src/elements/TextBox.ts +768 -0
  274. package/src/formatting/AbstractNumbering.ts +548 -0
  275. package/src/formatting/CLAUDE.md +74 -0
  276. package/src/formatting/NumberingInstance.ts +212 -0
  277. package/src/formatting/NumberingLevel.ts +1006 -0
  278. package/src/formatting/NumberingManager.ts +827 -0
  279. package/src/formatting/Style.ts +1833 -0
  280. package/src/formatting/StylesManager.ts +1005 -0
  281. package/src/helpers/CleanupHelper.ts +524 -0
  282. package/src/images/ImageOptimizer.ts +274 -0
  283. package/src/index.ts +554 -0
  284. package/src/managers/CLAUDE.md +47 -0
  285. package/src/managers/DrawingManager.ts +319 -0
  286. package/src/tracking/DocumentTrackingContext.ts +643 -0
  287. package/src/tracking/TrackingContext.ts +173 -0
  288. package/src/types/compatibility-types.ts +49 -0
  289. package/src/types/formatting.ts +210 -0
  290. package/src/types/list-types.ts +152 -0
  291. package/src/types/settings-types.ts +59 -0
  292. package/src/types/styleConfig.ts +189 -0
  293. package/src/utils/CLAUDE.md +153 -0
  294. package/src/utils/ChangelogGenerator.ts +1581 -0
  295. package/src/utils/CompatibilityUpgrader.ts +237 -0
  296. package/src/utils/InMemoryRevisionAcceptor.ts +696 -0
  297. package/src/utils/MoveOperationHelper.ts +238 -0
  298. package/src/utils/RevisionAwareProcessor.ts +526 -0
  299. package/src/utils/RevisionWalker.ts +457 -0
  300. package/src/utils/SelectiveRevisionAcceptor.ts +683 -0
  301. package/src/utils/ShadingResolver.ts +107 -0
  302. package/src/utils/acceptRevisions.ts +714 -0
  303. package/src/utils/cnfStyleDecoder.ts +217 -0
  304. package/src/utils/corruptionDetection.ts +345 -0
  305. package/src/utils/dateFormatting.ts +20 -0
  306. package/src/utils/deepClone.ts +78 -0
  307. package/src/utils/diagnostics.ts +129 -0
  308. package/src/utils/errorHandling.ts +80 -0
  309. package/src/utils/formatting.ts +213 -0
  310. package/src/utils/list-detection.ts +274 -0
  311. package/src/utils/logger.ts +404 -0
  312. package/src/utils/parsingHelpers.ts +190 -0
  313. package/src/utils/stripTrackedChanges.ts +353 -0
  314. package/src/utils/textDiff.ts +100 -0
  315. package/src/utils/units.ts +421 -0
  316. package/src/utils/validation.ts +542 -0
  317. package/src/utils/xmlSanitization.ts +182 -0
  318. package/src/validation/RevisionAutoFixer.ts +542 -0
  319. package/src/validation/RevisionValidator.ts +460 -0
  320. package/src/validation/ValidationRules.ts +338 -0
  321. package/src/validation/index.ts +30 -0
  322. package/src/xml/CLAUDE.md +65 -0
  323. package/src/xml/XMLBuilder.ts +871 -0
  324. package/src/xml/XMLParser.ts +919 -0
  325. package/src/zip/CLAUDE.md +55 -0
  326. package/src/zip/ZipHandler.ts +637 -0
  327. package/src/zip/ZipReader.ts +299 -0
  328. package/src/zip/ZipWriter.ts +390 -0
  329. package/src/zip/errors.ts +69 -0
  330. package/src/zip/types.ts +116 -0
  331. package/dist/core/ListNormalizer.d.ts +0 -23
  332. package/dist/core/ListNormalizer.d.ts.map +0 -1
  333. package/dist/core/ListNormalizer.js +0 -624
  334. package/dist/core/ListNormalizer.js.map +0 -1
  335. package/dist/images/index.d.ts +0 -2
  336. package/dist/images/index.d.ts.map +0 -1
  337. package/dist/images/index.js +0 -8
  338. package/dist/images/index.js.map +0 -1
  339. package/dist/ms-doc/cfb/CFBReader.d.ts +0 -35
  340. package/dist/ms-doc/cfb/CFBReader.d.ts.map +0 -1
  341. package/dist/ms-doc/cfb/CFBReader.js +0 -360
  342. package/dist/ms-doc/cfb/CFBReader.js.map +0 -1
  343. package/dist/ms-doc/converter/DocToDocxConverter.d.ts +0 -55
  344. package/dist/ms-doc/converter/DocToDocxConverter.d.ts.map +0 -1
  345. package/dist/ms-doc/converter/DocToDocxConverter.js +0 -324
  346. package/dist/ms-doc/converter/DocToDocxConverter.js.map +0 -1
  347. package/dist/ms-doc/fib/FIB.d.ts +0 -18
  348. package/dist/ms-doc/fib/FIB.d.ts.map +0 -1
  349. package/dist/ms-doc/fib/FIB.js +0 -342
  350. package/dist/ms-doc/fib/FIB.js.map +0 -1
  351. package/dist/ms-doc/fields/FieldParser.d.ts +0 -31
  352. package/dist/ms-doc/fields/FieldParser.d.ts.map +0 -1
  353. package/dist/ms-doc/fields/FieldParser.js +0 -266
  354. package/dist/ms-doc/fields/FieldParser.js.map +0 -1
  355. package/dist/ms-doc/images/PictureExtractor.d.ts +0 -22
  356. package/dist/ms-doc/images/PictureExtractor.d.ts.map +0 -1
  357. package/dist/ms-doc/images/PictureExtractor.js +0 -233
  358. package/dist/ms-doc/images/PictureExtractor.js.map +0 -1
  359. package/dist/ms-doc/index.d.ts +0 -20
  360. package/dist/ms-doc/index.d.ts.map +0 -1
  361. package/dist/ms-doc/index.js +0 -59
  362. package/dist/ms-doc/index.js.map +0 -1
  363. package/dist/ms-doc/properties/SPRM.d.ts +0 -210
  364. package/dist/ms-doc/properties/SPRM.d.ts.map +0 -1
  365. package/dist/ms-doc/properties/SPRM.js +0 -633
  366. package/dist/ms-doc/properties/SPRM.js.map +0 -1
  367. package/dist/ms-doc/sections/SectionParser.d.ts +0 -25
  368. package/dist/ms-doc/sections/SectionParser.d.ts.map +0 -1
  369. package/dist/ms-doc/sections/SectionParser.js +0 -214
  370. package/dist/ms-doc/sections/SectionParser.js.map +0 -1
  371. package/dist/ms-doc/styles/StyleSheet.d.ts +0 -23
  372. package/dist/ms-doc/styles/StyleSheet.d.ts.map +0 -1
  373. package/dist/ms-doc/styles/StyleSheet.js +0 -268
  374. package/dist/ms-doc/styles/StyleSheet.js.map +0 -1
  375. package/dist/ms-doc/subdocuments/SubdocumentParser.d.ts +0 -61
  376. package/dist/ms-doc/subdocuments/SubdocumentParser.d.ts.map +0 -1
  377. package/dist/ms-doc/subdocuments/SubdocumentParser.js +0 -208
  378. package/dist/ms-doc/subdocuments/SubdocumentParser.js.map +0 -1
  379. package/dist/ms-doc/tables/TableParser.d.ts +0 -29
  380. package/dist/ms-doc/tables/TableParser.d.ts.map +0 -1
  381. package/dist/ms-doc/tables/TableParser.js +0 -176
  382. package/dist/ms-doc/tables/TableParser.js.map +0 -1
  383. package/dist/ms-doc/text/PieceTable.d.ts +0 -21
  384. package/dist/ms-doc/text/PieceTable.d.ts.map +0 -1
  385. package/dist/ms-doc/text/PieceTable.js +0 -171
  386. package/dist/ms-doc/text/PieceTable.js.map +0 -1
  387. package/dist/ms-doc/types/Constants.d.ts +0 -99
  388. package/dist/ms-doc/types/Constants.d.ts.map +0 -1
  389. package/dist/ms-doc/types/Constants.js +0 -102
  390. package/dist/ms-doc/types/Constants.js.map +0 -1
  391. package/dist/ms-doc/types/DocTypes.d.ts +0 -368
  392. package/dist/ms-doc/types/DocTypes.d.ts.map +0 -1
  393. package/dist/ms-doc/types/DocTypes.js +0 -3
  394. package/dist/ms-doc/types/DocTypes.js.map +0 -1
  395. package/dist/tracking/index.d.ts +0 -3
  396. package/dist/tracking/index.d.ts.map +0 -1
  397. package/dist/tracking/index.js +0 -6
  398. package/dist/tracking/index.js.map +0 -1
@@ -0,0 +1,2524 @@
1
+ /**
2
+ * Table - Represents a table in a document
3
+ */
4
+
5
+ import { Paragraph } from "./Paragraph";
6
+ import { TableRow, RowFormatting } from "./TableRow";
7
+ import { TableCell, CellFormatting } from "./TableCell";
8
+ import { Run } from "./Run";
9
+ import { Revision } from "./Revision";
10
+ import { XMLBuilder, XMLElement } from "../xml/XMLBuilder";
11
+ import { deepClone } from "../utils/deepClone";
12
+ import {
13
+ TableAlignment as CommonTableAlignment,
14
+ BorderStyle,
15
+ BorderDefinition,
16
+ TableBorderDefinitions,
17
+ HorizontalAnchor,
18
+ VerticalAnchor,
19
+ HorizontalAlignment,
20
+ VerticalAlignment,
21
+ WidthType,
22
+ ShadingPattern,
23
+ ShadingConfig,
24
+ buildShadingAttributes,
25
+ } from "./CommonTypes";
26
+
27
+ // ============================================================================
28
+ // RE-EXPORTED TYPES (for backward compatibility)
29
+ // ============================================================================
30
+
31
+ /**
32
+ * Table alignment
33
+ * @see CommonTypes.TableAlignment
34
+ */
35
+ export type TableAlignment = CommonTableAlignment;
36
+
37
+ /**
38
+ * Table layout type
39
+ */
40
+ export type TableLayout = "auto" | "autofit" | "fixed";
41
+
42
+ /**
43
+ * Table border definition (same as cell borders)
44
+ * @see CommonTypes.BorderDefinition
45
+ */
46
+ export interface TableBorder {
47
+ style?: BorderStyle;
48
+ size?: number;
49
+ space?: number; // Border spacing (padding) in points
50
+ color?: string;
51
+ }
52
+
53
+ /**
54
+ * Table borders
55
+ * @see CommonTypes.TableBorderDefinitions
56
+ */
57
+ export interface TableBorders {
58
+ top?: TableBorder;
59
+ bottom?: TableBorder;
60
+ left?: TableBorder;
61
+ right?: TableBorder;
62
+ insideH?: TableBorder; // Inside horizontal borders
63
+ insideV?: TableBorder; // Inside vertical borders
64
+ }
65
+
66
+ /**
67
+ * Horizontal anchor for table positioning
68
+ * @see CommonTypes.HorizontalAnchor
69
+ */
70
+ export type TableHorizontalAnchor = HorizontalAnchor;
71
+
72
+ /**
73
+ * Vertical anchor for table positioning
74
+ * @see CommonTypes.VerticalAnchor
75
+ */
76
+ export type TableVerticalAnchor = VerticalAnchor;
77
+
78
+ /**
79
+ * Horizontal alignment for relative table positioning
80
+ * @see CommonTypes.HorizontalAlignment
81
+ */
82
+ export type TableHorizontalAlignment = HorizontalAlignment;
83
+
84
+ /**
85
+ * Vertical alignment for relative table positioning
86
+ * @see CommonTypes.VerticalAlignment
87
+ */
88
+ export type TableVerticalAlignment = VerticalAlignment;
89
+
90
+ /**
91
+ * Table positioning properties (for floating tables)
92
+ * Per ECMA-376 Part 1 §17.4.57 (tblpPr)
93
+ */
94
+ export interface TablePositionProperties {
95
+ /** Horizontal position in twips (absolute positioning) */
96
+ x?: number;
97
+ /** Vertical position in twips (absolute positioning) */
98
+ y?: number;
99
+ /** Horizontal anchor/positioning base */
100
+ horizontalAnchor?: TableHorizontalAnchor;
101
+ /** Vertical anchor/positioning base */
102
+ verticalAnchor?: TableVerticalAnchor;
103
+ /** Horizontal alignment (relative positioning) */
104
+ horizontalAlignment?: TableHorizontalAlignment;
105
+ /** Vertical alignment (relative positioning) */
106
+ verticalAlignment?: TableVerticalAlignment;
107
+ /** Left padding from anchor in twips */
108
+ leftFromText?: number;
109
+ /** Right padding from anchor in twips */
110
+ rightFromText?: number;
111
+ /** Top padding from anchor in twips */
112
+ topFromText?: number;
113
+ /** Bottom padding from anchor in twips */
114
+ bottomFromText?: number;
115
+ }
116
+
117
+ /**
118
+ * Table width type
119
+ */
120
+ export type TableWidthType = "auto" | "dxa" | "pct";
121
+
122
+ /**
123
+ * Table shading/background
124
+ * @see ShadingConfig in CommonTypes.ts for the canonical definition
125
+ */
126
+ export type TableShading = ShadingConfig;
127
+
128
+ /**
129
+ * Table formatting options
130
+ */
131
+ /**
132
+ * Table cell margins (padding inside cells)
133
+ * Per ECMA-376 Part 1 §17.4.42 (tblCellMar)
134
+ */
135
+ export interface TableCellMargins {
136
+ /** Top margin in twips */
137
+ top?: number;
138
+ /** Bottom margin in twips */
139
+ bottom?: number;
140
+ /** Left margin in twips */
141
+ left?: number;
142
+ /** Right margin in twips */
143
+ right?: number;
144
+ }
145
+
146
+ /**
147
+ * Table formatting options
148
+ */
149
+ export interface TableFormatting {
150
+ style?: string; // Table style ID (e.g., 'Table1', 'TableGrid')
151
+ width?: number; // Table width in twips
152
+ widthType?: TableWidthType; // Width type (auto, dxa=twips, pct=percentage)
153
+ alignment?: TableAlignment;
154
+ layout?: TableLayout;
155
+ borders?: TableBorders;
156
+ cellSpacing?: number; // Cell spacing in twips
157
+ cellSpacingType?: TableWidthType; // Cell spacing type
158
+ cellMargins?: TableCellMargins; // Default cell margins (padding) for all cells
159
+ indent?: number; // Left indent in twips
160
+ tblLook?: string; // Table look flags (appearance settings)
161
+ shading?: TableShading; // Table background shading
162
+ // Batch 1 properties
163
+ position?: TablePositionProperties; // Floating table positioning
164
+ overlap?: boolean; // Allow table overlap with other floating tables
165
+ bidiVisual?: boolean; // Right-to-left table layout
166
+ tableGrid?: number[]; // Column widths in twips
167
+ caption?: string; // Table caption for accessibility
168
+ description?: string; // Table description for accessibility
169
+ tblStyleRowBandSize?: number; // Number of rows in each row band for style alternation
170
+ tblStyleColBandSize?: number; // Number of columns in each column band for style alternation
171
+ }
172
+
173
+ /**
174
+ * First row formatting options for table headers
175
+ */
176
+ export interface FirstRowFormattingOptions {
177
+ /** Text alignment in cells */
178
+ alignment?: "left" | "center" | "right";
179
+ /** Bold text */
180
+ bold?: boolean;
181
+ /** Italic text */
182
+ italic?: boolean;
183
+ /** Underline text */
184
+ underline?: boolean | "single" | "double" | "thick" | "dotted" | "dash";
185
+ /** Spacing before paragraph (in twips) */
186
+ spacingBefore?: number;
187
+ /** Spacing after paragraph (in twips) */
188
+ spacingAfter?: number;
189
+ /** Background color (hex without #) */
190
+ shading?: string;
191
+ }
192
+
193
+ /**
194
+ * Represents a table
195
+ */
196
+ /**
197
+ * Table property change tracking (w:tblPrChange)
198
+ * Per ECMA-376 Part 1 §17.13.5.36
199
+ */
200
+ export interface TblPrChange {
201
+ author: string;
202
+ date: string;
203
+ id: string;
204
+ previousProperties: Record<string, any>;
205
+ }
206
+
207
+ export class Table {
208
+ private rows: TableRow[] = [];
209
+ private formatting: TableFormatting;
210
+ /** StylesManager reference for conditional formatting resolution */
211
+ private _stylesManager?: import("../formatting/StylesManager").StylesManager;
212
+ /** Tracking context for automatic change tracking */
213
+ private trackingContext?: import('../tracking/TrackingContext').TrackingContext;
214
+ /** Table property change tracking (w:tblPrChange) */
215
+ private tblPrChange?: TblPrChange;
216
+
217
+ /**
218
+ * Creates a new Table
219
+ * @param rows - Number of rows to create (optional)
220
+ * @param columns - Number of columns per row (optional)
221
+ * @param formatting - Table formatting options
222
+ */
223
+ constructor(
224
+ rows?: number,
225
+ columns?: number,
226
+ formatting: TableFormatting = {}
227
+ ) {
228
+ // Set default width if not specified
229
+ // Per ECMA-376, tables require <w:tblW> element for Word compatibility
230
+ // Default: Letter page width (12240 twips) minus standard margins (2*1440 twips) = 9360 twips
231
+ if (formatting.width === undefined) {
232
+ formatting.width = 9360; // ~6.5 inches
233
+ }
234
+
235
+ this.formatting = formatting;
236
+
237
+ if (
238
+ rows !== undefined &&
239
+ rows > 0 &&
240
+ columns !== undefined &&
241
+ columns > 0
242
+ ) {
243
+ for (let i = 0; i < rows; i++) {
244
+ const row = new TableRow(columns);
245
+ row._setParentTable(this);
246
+ this.rows.push(row);
247
+ }
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Sets the tracking context for automatic change tracking.
253
+ * Called by Document when track changes is enabled.
254
+ * @internal
255
+ */
256
+ _setTrackingContext(context: import('../tracking/TrackingContext').TrackingContext): void {
257
+ this.trackingContext = context;
258
+ }
259
+
260
+ /**
261
+ * Gets the table property change tracking info
262
+ */
263
+ getTblPrChange(): TblPrChange | undefined {
264
+ return this.tblPrChange;
265
+ }
266
+
267
+ /**
268
+ * Sets the table property change tracking info
269
+ */
270
+ setTblPrChange(change: TblPrChange | undefined): void {
271
+ this.tblPrChange = change;
272
+ }
273
+
274
+ /**
275
+ * Clears the table property change tracking
276
+ */
277
+ clearTblPrChange(): void {
278
+ this.tblPrChange = undefined;
279
+ }
280
+
281
+ /**
282
+ * Adds a row to the table
283
+ *
284
+ * Appends a TableRow instance to the end of the table.
285
+ *
286
+ * @param row - The TableRow instance to add
287
+ * @returns This table instance for method chaining
288
+ *
289
+ * @example
290
+ * ```typescript
291
+ * const table = new Table();
292
+ * const row = new TableRow(3);
293
+ * table.addRow(row);
294
+ * ```
295
+ */
296
+ addRow(row: TableRow): this {
297
+ this.rows.push(row);
298
+ row._setParentTable(this);
299
+ return this;
300
+ }
301
+
302
+ /**
303
+ * Creates a new row and adds it to the table
304
+ *
305
+ * Convenience method that creates a TableRow and appends it in one operation.
306
+ *
307
+ * @param cellCount - Number of cells to create in the row
308
+ * @param formatting - Optional row formatting properties
309
+ * @returns The created TableRow instance for further customization
310
+ *
311
+ * @example
312
+ * ```typescript
313
+ * const table = new Table();
314
+ * const row = table.createRow(4);
315
+ * row.getCell(0)?.addParagraph(new Paragraph().addText('Cell 1'));
316
+ * ```
317
+ */
318
+ createRow(cellCount?: number, formatting?: RowFormatting): TableRow {
319
+ const row = new TableRow(cellCount, formatting);
320
+ this.rows.push(row);
321
+ row._setParentTable(this);
322
+ return row;
323
+ }
324
+
325
+ /**
326
+ * Gets a row by its index
327
+ *
328
+ * @param index - The row index (0-based, where 0 is the first row)
329
+ * @returns The TableRow at the specified index, or undefined if index is out of bounds
330
+ *
331
+ * @example
332
+ * ```typescript
333
+ * const firstRow = table.getRow(0);
334
+ * const secondRow = table.getRow(1);
335
+ * if (firstRow) {
336
+ * console.log(`First row has ${firstRow.getCellCount()} cells`);
337
+ * }
338
+ * ```
339
+ */
340
+ getRow(index: number): TableRow | undefined {
341
+ return this.rows[index];
342
+ }
343
+
344
+ /**
345
+ * Gets all rows in the table
346
+ *
347
+ * Returns a copy of the rows array to prevent external modification.
348
+ *
349
+ * @returns Array of all TableRow instances
350
+ *
351
+ * @example
352
+ * ```typescript
353
+ * const rows = table.getRows();
354
+ * console.log(`Table has ${rows.length} rows`);
355
+ * for (const row of rows) {
356
+ * console.log(`Row has ${row.getCellCount()} cells`);
357
+ * }
358
+ * ```
359
+ */
360
+ getRows(): TableRow[] {
361
+ return [...this.rows];
362
+ }
363
+
364
+ /**
365
+ * Gets the first paragraph in the table (first cell of first row)
366
+ * @returns The first paragraph, or null if the table is empty
367
+ */
368
+ getFirstParagraph(): Paragraph | null {
369
+ if (this.rows.length === 0) return null;
370
+ const firstRow = this.rows[0];
371
+ if (!firstRow) return null;
372
+ const cells = firstRow.getCells();
373
+ if (cells.length === 0) return null;
374
+ const paras = cells[0]?.getParagraphs();
375
+ return paras && paras.length > 0 ? paras[0] ?? null : null;
376
+ }
377
+
378
+ /**
379
+ * Gets the last paragraph in the table (last cell of last row)
380
+ * @returns The last paragraph, or null if the table is empty
381
+ */
382
+ getLastParagraph(): Paragraph | null {
383
+ for (let r = this.rows.length - 1; r >= 0; r--) {
384
+ const row = this.rows[r];
385
+ if (!row) continue;
386
+ const cells = row.getCells();
387
+ for (let c = cells.length - 1; c >= 0; c--) {
388
+ const cell = cells[c];
389
+ if (!cell) continue;
390
+ const paras = cell.getParagraphs();
391
+ if (paras.length > 0) {
392
+ return paras[paras.length - 1] ?? null;
393
+ }
394
+ }
395
+ }
396
+ return null;
397
+ }
398
+
399
+ /**
400
+ * Gets the total number of rows in the table
401
+ *
402
+ * @returns Number of rows
403
+ *
404
+ * @example
405
+ * ```typescript
406
+ * console.log(`Table has ${table.getRowCount()} rows`);
407
+ * ```
408
+ */
409
+ getRowCount(): number {
410
+ return this.rows.length;
411
+ }
412
+
413
+ /**
414
+ * Gets a specific cell by row and column indices
415
+ *
416
+ * @param rowIndex - The row index (0-based)
417
+ * @param columnIndex - The column index (0-based)
418
+ * @returns The TableCell at the specified position, or undefined if indices are out of bounds
419
+ *
420
+ * @example
421
+ * ```typescript
422
+ * const cell = table.getCell(0, 0); // Top-left cell
423
+ * if (cell) {
424
+ * cell.addParagraph(new Paragraph().addText('A1'));
425
+ * }
426
+ *
427
+ * // Access cell in third row, second column
428
+ * const cell2 = table.getCell(2, 1);
429
+ * ```
430
+ */
431
+ getCell(rowIndex: number, columnIndex: number): TableCell | undefined {
432
+ const row = this.getRow(rowIndex);
433
+ return row ? row.getCell(columnIndex) : undefined;
434
+ }
435
+
436
+ /**
437
+ * Sets the table width
438
+ *
439
+ * Defines the total width of the table. Use with {@link setWidthType}
440
+ * to specify if width is in twips, percentage, or auto.
441
+ *
442
+ * @param twips - Width value (interpretation depends on widthType)
443
+ * - For 'dxa' (default): Width in twips (1/20th of a point)
444
+ * - For 'pct': Percentage * 50 (e.g., 5000 = 100%)
445
+ * - For 'auto': Value is ignored
446
+ * @returns This table instance for method chaining
447
+ *
448
+ * @example
449
+ * ```typescript
450
+ * table.setWidth(9360); // 6.5 inches in twips
451
+ * table.setWidth(5000).setWidthType('pct'); // 100% width
452
+ * ```
453
+ */
454
+ setWidth(twips: number): this {
455
+ const prev = this.formatting.width;
456
+ this.formatting.width = twips;
457
+ if (this.trackingContext?.isEnabled() && prev !== twips) {
458
+ this.trackingContext.trackTableChange(this, 'width', prev, twips);
459
+ }
460
+ return this;
461
+ }
462
+
463
+ /**
464
+ * Sets table horizontal alignment
465
+ *
466
+ * Controls where the table is positioned horizontally on the page.
467
+ *
468
+ * @param alignment - Alignment value ('left' |'center' | 'right')
469
+ * @returns This table instance for method chaining
470
+ *
471
+ * @example
472
+ * ```typescript
473
+ * table.setAlignment('center'); // Center the table on page
474
+ * table.setAlignment('right'); // Align table to right margin
475
+ * ```
476
+ */
477
+ setAlignment(alignment: TableAlignment): this {
478
+ const prev = this.formatting.alignment;
479
+ this.formatting.alignment = alignment;
480
+ if (this.trackingContext?.isEnabled() && prev !== alignment) {
481
+ this.trackingContext.trackTableChange(this, 'alignment', prev, alignment);
482
+ }
483
+ return this;
484
+ }
485
+
486
+ /**
487
+ * Sets table layout algorithm
488
+ *
489
+ * Controls how table column widths are calculated.
490
+ *
491
+ * @param layout - Layout type
492
+ * - 'auto': Columns auto-fit to content and window width
493
+ * - 'fixed': Columns use fixed widths (faster rendering)
494
+ * @returns This table instance for method chaining
495
+ *
496
+ * @example
497
+ * ```typescript
498
+ * table.setLayout('auto'); // Auto-fit to window
499
+ * table.setLayout('fixed'); // Use fixed column widths
500
+ * ```
501
+ */
502
+ setLayout(layout: TableLayout): this {
503
+ const prev = this.formatting.layout;
504
+ // Normalize "auto" to "autofit" per ECMA-376 ST_TblLayoutType (§17.18.87)
505
+ this.formatting.layout = layout === "auto" ? "autofit" : layout;
506
+ if (this.trackingContext?.isEnabled() && prev !== this.formatting.layout) {
507
+ this.trackingContext.trackTableChange(this, 'layout', prev, this.formatting.layout);
508
+ }
509
+ return this;
510
+ }
511
+
512
+ /**
513
+ * Sets table borders
514
+ *
515
+ * Defines borders for all sides of the table and interior borders.
516
+ * By default, also applies borders to all cells within the table for consistency.
517
+ *
518
+ * @param borders - Border definitions for each edge
519
+ * @param borders.top - Top border of table
520
+ * @param borders.bottom - Bottom border of table
521
+ * @param borders.left - Left border of table
522
+ * @param borders.right - Right border of table
523
+ * @param borders.insideH - Horizontal borders between rows
524
+ * @param borders.insideV - Vertical borders between columns
525
+ * @param options - Additional options
526
+ * @param options.applyToCells - Whether to also set borders on all cells (default: true)
527
+ * @returns This table instance for method chaining
528
+ *
529
+ * @example
530
+ * ```typescript
531
+ * // Set borders on table AND all cells (default behavior)
532
+ * table.setBorders({
533
+ * top: { style: 'single', size: 4, color: '000000' },
534
+ * bottom: { style: 'single', size: 4, color: '000000' },
535
+ * insideH: { style: 'single', size: 2, color: 'CCCCCC' },
536
+ * insideV: { style: 'single', size: 2, color: 'CCCCCC' }
537
+ * });
538
+ *
539
+ * // Set only table-level borders (no cell borders)
540
+ * table.setBorders({ top: { style: 'single', size: 4 } }, { applyToCells: false });
541
+ * ```
542
+ */
543
+ setBorders(borders: TableBorders, options?: { applyToCells?: boolean }): this {
544
+ const prev = this.formatting.borders;
545
+ // Set table-level borders (w:tblBorders)
546
+ this.formatting.borders = borders;
547
+ if (this.trackingContext?.isEnabled() && prev !== borders) {
548
+ this.trackingContext.trackTableChange(this, 'borders', prev, borders);
549
+ }
550
+
551
+ // Also apply to all cells for consistency (default: true)
552
+ if (options?.applyToCells !== false) {
553
+ const cellBorders = {
554
+ top: borders.top,
555
+ bottom: borders.bottom,
556
+ left: borders.left,
557
+ right: borders.right,
558
+ };
559
+
560
+ for (const row of this.rows) {
561
+ // Update row-level table property exceptions if they have borders
562
+ // Per ECMA-376, w:tblPrEx can contain border overrides that must also be updated
563
+ const exceptions = row.getTablePropertyExceptions();
564
+ if (exceptions?.borders) {
565
+ row.setTablePropertyExceptions({
566
+ ...exceptions,
567
+ borders: borders, // Use full table borders for row-level exceptions
568
+ });
569
+ }
570
+
571
+ // Update cell borders
572
+ for (const cell of row.getCells()) {
573
+ cell.setBorders(cellBorders);
574
+ }
575
+ }
576
+ }
577
+
578
+ return this;
579
+ }
580
+
581
+ /**
582
+ * Sets all borders to the same style
583
+ *
584
+ * Convenience method that applies identical borders to all edges
585
+ * (top, bottom, left, right, insideH, insideV) and all cells.
586
+ *
587
+ * @param border - Border definition to apply uniformly
588
+ * @param options - Additional options
589
+ * @param options.applyToCells - Whether to also set borders on all cells (default: true)
590
+ * @returns This table instance for method chaining
591
+ *
592
+ * @example
593
+ * ```typescript
594
+ * // Apply single black border to all edges and cells
595
+ * table.setAllBorders({
596
+ * style: 'single',
597
+ * size: 4,
598
+ * color: '000000'
599
+ * });
600
+ *
601
+ * // Apply only to table, not individual cells
602
+ * table.setAllBorders({ style: 'single', size: 4 }, { applyToCells: false });
603
+ * ```
604
+ */
605
+ setAllBorders(border: TableBorder, options?: { applyToCells?: boolean }): this {
606
+ return this.setBorders({
607
+ top: border,
608
+ bottom: border,
609
+ left: border,
610
+ right: border,
611
+ insideH: border,
612
+ insideV: border,
613
+ }, options);
614
+ }
615
+
616
+ /**
617
+ * Sets shading (background color) for the first row of the table
618
+ * Useful for header rows in data tables
619
+ * @param color - Hex color without # (e.g., 'DFDFDF' for light gray)
620
+ * @returns This table for chaining
621
+ * @example
622
+ * ```typescript
623
+ * table.setFirstRowShading('DFDFDF'); // Light gray header
624
+ * ```
625
+ */
626
+ setFirstRowShading(color: string): this {
627
+ if (this.rows.length > 0) {
628
+ const firstRow = this.rows[0];
629
+ if (firstRow) {
630
+ for (const cell of firstRow.getCells()) {
631
+ cell.setShading({ fill: color });
632
+ }
633
+ }
634
+ }
635
+ return this;
636
+ }
637
+
638
+ /**
639
+ * Sets comprehensive formatting for the first row of the table
640
+ *
641
+ * This is ideal for formatting table headers with alignment, text formatting,
642
+ * spacing, and background color in a single call.
643
+ *
644
+ * @param options - Formatting options for the first row
645
+ * @returns This table for chaining
646
+ * @example
647
+ * ```typescript
648
+ * // Create a formatted table header
649
+ * table.setFirstRowFormatting({
650
+ * alignment: 'center',
651
+ * bold: true,
652
+ * spacingBefore: 120,
653
+ * spacingAfter: 120,
654
+ * shading: 'DFDFDF'
655
+ * });
656
+ *
657
+ * // Format header with underline and custom spacing
658
+ * table.setFirstRowFormatting({
659
+ * alignment: 'left',
660
+ * bold: true,
661
+ * underline: 'single',
662
+ * spacingAfter: 80
663
+ * });
664
+ * ```
665
+ */
666
+ setFirstRowFormatting(options: FirstRowFormattingOptions): this {
667
+ if (this.rows.length === 0) {
668
+ return this; // No rows to format
669
+ }
670
+
671
+ const firstRow = this.rows[0];
672
+ if (!firstRow) {
673
+ return this;
674
+ }
675
+
676
+ // Apply formatting to each cell in the first row
677
+ for (const cell of firstRow.getCells()) {
678
+ const paragraphs = cell.getParagraphs();
679
+
680
+ // Apply shading to the cell if specified
681
+ if (options.shading) {
682
+ cell.setShading({ fill: options.shading });
683
+ }
684
+
685
+ // Apply formatting to each paragraph in the cell
686
+ for (const para of paragraphs) {
687
+ // Apply paragraph alignment
688
+ if (options.alignment) {
689
+ para.setAlignment(options.alignment as any);
690
+ }
691
+
692
+ // Apply spacing
693
+ if (options.spacingBefore !== undefined) {
694
+ para.setSpaceBefore(options.spacingBefore);
695
+ }
696
+ if (options.spacingAfter !== undefined) {
697
+ para.setSpaceAfter(options.spacingAfter);
698
+ }
699
+
700
+ // Apply run formatting to all runs in the paragraph
701
+ const runs = para.getRuns();
702
+ for (const run of runs) {
703
+ // Apply formatting using run's setter methods
704
+ if (options.bold !== undefined) {
705
+ run.setBold(options.bold);
706
+ }
707
+ if (options.italic !== undefined) {
708
+ run.setItalic(options.italic);
709
+ }
710
+ if (options.underline !== undefined) {
711
+ run.setUnderline(options.underline);
712
+ }
713
+ }
714
+ }
715
+ }
716
+
717
+ return this;
718
+ }
719
+
720
+ /**
721
+ * Sets cell spacing
722
+ * @param twips - Cell spacing in twips
723
+ * @returns This table for chaining
724
+ */
725
+ setCellSpacing(twips: number): this {
726
+ const prev = this.formatting.cellSpacing;
727
+ this.formatting.cellSpacing = twips;
728
+ if (this.trackingContext?.isEnabled() && prev !== twips) {
729
+ this.trackingContext.trackTableChange(this, 'cellSpacing', prev, twips);
730
+ }
731
+ return this;
732
+ }
733
+
734
+ /**
735
+ * Sets left indent
736
+ * @param twips - Indent in twips
737
+ * @returns This table for chaining
738
+ */
739
+ setIndent(twips: number): this {
740
+ const prev = this.formatting.indent;
741
+ this.formatting.indent = twips;
742
+ if (this.trackingContext?.isEnabled() && prev !== twips) {
743
+ this.trackingContext.trackTableChange(this, 'indent', prev, twips);
744
+ }
745
+ return this;
746
+ }
747
+
748
+ /**
749
+ * Sets table style reference
750
+ * @param style - Table style ID (e.g., 'Table1', 'TableGrid')
751
+ * @returns This table for chaining
752
+ */
753
+ setStyle(style: string): this {
754
+ const prev = this.formatting.style;
755
+ this.formatting.style = style;
756
+ if (this.trackingContext?.isEnabled() && prev !== style) {
757
+ this.trackingContext.trackTableChange(this, 'style', prev, style);
758
+ }
759
+ return this;
760
+ }
761
+
762
+ /**
763
+ * Sets table look flags (appearance settings)
764
+ * @param tblLook - Table look value (e.g., '0000', '04A0')
765
+ * @returns This table for chaining
766
+ */
767
+ setTblLook(tblLook: string): this {
768
+ const prev = this.formatting.tblLook;
769
+ this.formatting.tblLook = tblLook;
770
+ if (this.trackingContext?.isEnabled() && prev !== tblLook) {
771
+ this.trackingContext.trackTableChange(this, 'tblLook', prev, tblLook);
772
+ }
773
+ return this;
774
+ }
775
+
776
+ /**
777
+ * Sets the number of rows in each row band for table style alternation
778
+ * Per ECMA-376 Part 1 §17.4.52
779
+ * @param size - Number of rows per band (default: 1)
780
+ * @returns This table for chaining
781
+ */
782
+ setStyleRowBandSize(size: number): this {
783
+ const prev = this.formatting.tblStyleRowBandSize;
784
+ this.formatting.tblStyleRowBandSize = size;
785
+ if (this.trackingContext?.isEnabled() && prev !== size) {
786
+ this.trackingContext.trackTableChange(this, 'tblStyleRowBandSize', prev, size);
787
+ }
788
+ return this;
789
+ }
790
+
791
+ /**
792
+ * Sets the number of columns in each column band for table style alternation
793
+ * Per ECMA-376 Part 1 §17.4.51
794
+ * @param size - Number of columns per band (default: 1)
795
+ * @returns This table for chaining
796
+ */
797
+ setStyleColBandSize(size: number): this {
798
+ const prev = this.formatting.tblStyleColBandSize;
799
+ this.formatting.tblStyleColBandSize = size;
800
+ if (this.trackingContext?.isEnabled() && prev !== size) {
801
+ this.trackingContext.trackTableChange(this, 'tblStyleColBandSize', prev, size);
802
+ }
803
+ return this;
804
+ }
805
+
806
+ /**
807
+ * Decodes tblLook hex string into boolean flags
808
+ * Per ECMA-376 Part 1 Section 17.4.56
809
+ * @returns Object with boolean flags for each tblLook property
810
+ */
811
+ getTblLookFlags(): {
812
+ firstRow: boolean;
813
+ lastRow: boolean;
814
+ firstColumn: boolean;
815
+ lastColumn: boolean;
816
+ noHBand: boolean;
817
+ noVBand: boolean;
818
+ } {
819
+ const hex = this.formatting.tblLook || "0000";
820
+ const value = parseInt(hex, 16) || 0;
821
+
822
+ return {
823
+ firstRow: (value & 0x0020) !== 0,
824
+ lastRow: (value & 0x0040) !== 0,
825
+ firstColumn: (value & 0x0080) !== 0,
826
+ lastColumn: (value & 0x0100) !== 0,
827
+ noHBand: (value & 0x0200) !== 0,
828
+ noVBand: (value & 0x0400) !== 0,
829
+ };
830
+ }
831
+
832
+ /**
833
+ * Applies conditional formatting to table cells based on rules
834
+ *
835
+ * Enables advanced table formatting including:
836
+ * - Automatic header row detection and styling
837
+ * - Alternating row colors (zebra striping)
838
+ * - Content-based formatting rules
839
+ *
840
+ * @param rules - Conditional formatting rules
841
+ * @returns This table for chaining
842
+ *
843
+ * @example
844
+ * ```typescript
845
+ * // Apply header formatting and alternating rows
846
+ * table.applyConditionalFormatting({
847
+ * headerRow: true,
848
+ * alternatingRows: {
849
+ * even: { shading: { fill: 'F0F0F0' } },
850
+ * odd: { shading: { fill: 'FFFFFF' } }
851
+ * }
852
+ * });
853
+ *
854
+ * // Custom header formatting
855
+ * table.applyConditionalFormatting({
856
+ * headerRow: {
857
+ * shading: { fill: '4472C4' },
858
+ * textColor: 'FFFFFF'
859
+ * }
860
+ * });
861
+ *
862
+ * // Content-based formatting
863
+ * table.applyConditionalFormatting({
864
+ * contentRules: [
865
+ * {
866
+ * condition: (text, row, col) => parseFloat(text) > 1000,
867
+ * formatting: { shading: { fill: 'FFD700' } } // Gold for large numbers
868
+ * },
869
+ * {
870
+ * condition: (text) => text.toLowerCase().includes('error'),
871
+ * formatting: { shading: { fill: 'FF0000' } } // Red for errors
872
+ * }
873
+ * ]
874
+ * });
875
+ *
876
+ * // Combined rules
877
+ * table.applyConditionalFormatting({
878
+ * headerRow: { shading: { fill: '2F5496' } },
879
+ * alternatingRows: {
880
+ * even: { shading: { fill: 'D9E1F2' } }
881
+ * },
882
+ * contentRules: [
883
+ * {
884
+ * condition: (text, row, col) => col === 0 && row > 0,
885
+ * formatting: { textColor: '000000' }
886
+ * }
887
+ * ]
888
+ * });
889
+ * ```
890
+ */
891
+ applyConditionalFormatting(rules: {
892
+ headerRow?: boolean | Partial<CellFormatting>;
893
+ alternatingRows?: {
894
+ even?: Partial<CellFormatting>;
895
+ odd?: Partial<CellFormatting>;
896
+ };
897
+ contentRules?: {
898
+ condition: (
899
+ cellText: string,
900
+ rowIndex: number,
901
+ colIndex: number
902
+ ) => boolean;
903
+ formatting: Partial<CellFormatting>;
904
+ }[];
905
+ }): this {
906
+ const rows = this.getRows();
907
+
908
+ // Apply header row formatting
909
+ if (rules.headerRow && rows.length > 0) {
910
+ const headerFormatting: Partial<CellFormatting> =
911
+ rules.headerRow === true
912
+ ? { shading: { fill: "4472C4" } } // Default blue header
913
+ : rules.headerRow;
914
+
915
+ const headerRow = rows[0];
916
+ if (headerRow) {
917
+ for (const cell of headerRow.getCells()) {
918
+ this.applyCellFormatting(cell, headerFormatting);
919
+ }
920
+ }
921
+ }
922
+
923
+ // Apply alternating rows
924
+ if (rules.alternatingRows) {
925
+ rows.forEach((row, index) => {
926
+ const isEven = index % 2 === 0;
927
+ const formatting = isEven
928
+ ? rules.alternatingRows?.even
929
+ : rules.alternatingRows?.odd;
930
+
931
+ if (formatting) {
932
+ for (const cell of row.getCells()) {
933
+ this.applyCellFormatting(cell, formatting);
934
+ }
935
+ }
936
+ });
937
+ }
938
+
939
+ // Apply content-based rules
940
+ if (rules.contentRules && rules.contentRules.length > 0) {
941
+ rows.forEach((row, rowIndex) => {
942
+ row.getCells().forEach((cell, colIndex) => {
943
+ const cellText = cell
944
+ .getParagraphs()
945
+ .map((p) => p.getText())
946
+ .join("");
947
+
948
+ for (const rule of rules.contentRules!) {
949
+ if (rule.condition(cellText, rowIndex, colIndex)) {
950
+ this.applyCellFormatting(cell, rule.formatting);
951
+ }
952
+ }
953
+ });
954
+ });
955
+ }
956
+
957
+ return this;
958
+ }
959
+
960
+ /**
961
+ * Helper method to apply formatting to a cell
962
+ * @private
963
+ */
964
+ private applyCellFormatting(
965
+ cell: TableCell,
966
+ formatting: Partial<CellFormatting>
967
+ ): void {
968
+ // Apply shading
969
+ if (formatting.shading) {
970
+ cell.setShading(formatting.shading);
971
+ }
972
+
973
+ // Apply borders
974
+ if (formatting.borders) {
975
+ cell.setBorders(formatting.borders);
976
+ }
977
+
978
+ // Apply text direction
979
+ if (formatting.textDirection) {
980
+ cell.setTextDirection(formatting.textDirection);
981
+ }
982
+
983
+ // Apply vertical alignment
984
+ if (formatting.verticalAlignment) {
985
+ cell.setVerticalAlignment(formatting.verticalAlignment);
986
+ }
987
+
988
+ // Apply width
989
+ if (formatting.width !== undefined) {
990
+ cell.setWidth(formatting.width);
991
+ }
992
+
993
+ // Apply margins
994
+ if (formatting.margins) {
995
+ cell.setMargins(formatting.margins);
996
+ }
997
+ }
998
+
999
+ /**
1000
+ * Sets table positioning properties for floating tables
1001
+ * Per ECMA-376 Part 1 §17.4.57
1002
+ * @param position - Table position properties
1003
+ * @returns This table for chaining
1004
+ * @example
1005
+ * ```typescript
1006
+ * // Position table at absolute coordinates
1007
+ * table.setPosition({
1008
+ * x: 1440, // 1 inch from left
1009
+ * y: 1440, // 1 inch from top
1010
+ * horizontalAnchor: 'page',
1011
+ * verticalAnchor: 'page'
1012
+ * });
1013
+ *
1014
+ * // Position table with relative alignment
1015
+ * table.setPosition({
1016
+ * horizontalAlignment: 'center',
1017
+ * verticalAlignment: 'top',
1018
+ * horizontalAnchor: 'margin',
1019
+ * verticalAnchor: 'page'
1020
+ * });
1021
+ * ```
1022
+ */
1023
+ setPosition(position: TablePositionProperties): this {
1024
+ const prev = this.formatting.position;
1025
+ this.formatting.position = position;
1026
+ if (this.trackingContext?.isEnabled() && prev !== position) {
1027
+ this.trackingContext.trackTableChange(this, 'position', prev, position);
1028
+ }
1029
+ return this;
1030
+ }
1031
+
1032
+ /**
1033
+ * Sets whether table can overlap with other floating tables
1034
+ * Per ECMA-376 Part 1 §17.4.30
1035
+ * @param overlap - True to allow overlap, false to prevent
1036
+ * @returns This table for chaining
1037
+ */
1038
+ setOverlap(overlap: boolean): this {
1039
+ const prev = this.formatting.overlap;
1040
+ this.formatting.overlap = overlap;
1041
+ if (this.trackingContext?.isEnabled() && prev !== overlap) {
1042
+ this.trackingContext.trackTableChange(this, 'overlap', prev, overlap);
1043
+ }
1044
+ return this;
1045
+ }
1046
+
1047
+ /**
1048
+ * Sets bidirectional (right-to-left) visual layout
1049
+ * Per ECMA-376 Part 1 §17.4.1
1050
+ * @param bidi - True for RTL layout, false for LTR
1051
+ * @returns This table for chaining
1052
+ */
1053
+ setBidiVisual(bidi: boolean): this {
1054
+ const prev = this.formatting.bidiVisual;
1055
+ this.formatting.bidiVisual = bidi;
1056
+ if (this.trackingContext?.isEnabled() && prev !== bidi) {
1057
+ this.trackingContext.trackTableChange(this, 'bidiVisual', prev, bidi);
1058
+ }
1059
+ return this;
1060
+ }
1061
+
1062
+ /**
1063
+ * Sets table grid column widths
1064
+ * Per ECMA-376 Part 1 §17.4.49
1065
+ * @param widths - Array of column widths in twips
1066
+ * @returns This table for chaining
1067
+ * @example
1068
+ * ```typescript
1069
+ * // 3 columns: 2 inches, 3 inches, 2 inches
1070
+ * table.setTableGrid([2880, 4320, 2880]);
1071
+ * ```
1072
+ */
1073
+ setTableGrid(widths: number[]): this {
1074
+ const prev = this.formatting.tableGrid;
1075
+ this.formatting.tableGrid = widths;
1076
+ if (this.trackingContext?.isEnabled()) {
1077
+ this.trackingContext.trackTableChange(this, 'tableGrid', prev, widths);
1078
+ }
1079
+ return this;
1080
+ }
1081
+
1082
+ /**
1083
+ * Sets table caption for accessibility
1084
+ * Per ECMA-376 Part 1 §17.4.58
1085
+ * @param caption - Table caption text
1086
+ * @returns This table for chaining
1087
+ */
1088
+ setCaption(caption: string): this {
1089
+ const prev = this.formatting.caption;
1090
+ this.formatting.caption = caption;
1091
+ if (this.trackingContext?.isEnabled() && prev !== caption) {
1092
+ this.trackingContext.trackTableChange(this, 'caption', prev, caption);
1093
+ }
1094
+ return this;
1095
+ }
1096
+
1097
+ /**
1098
+ * Sets table description for accessibility
1099
+ * Per ECMA-376 Part 1 §17.4.63
1100
+ * @param description - Table description text
1101
+ * @returns This table for chaining
1102
+ */
1103
+ setDescription(description: string): this {
1104
+ const prev = this.formatting.description;
1105
+ this.formatting.description = description;
1106
+ if (this.trackingContext?.isEnabled() && prev !== description) {
1107
+ this.trackingContext.trackTableChange(this, 'description', prev, description);
1108
+ }
1109
+ return this;
1110
+ }
1111
+
1112
+ /**
1113
+ * Sets table-level shading (background)
1114
+ *
1115
+ * Per ECMA-376 Part 1 §17.4.56 (w:shd), this sets the default
1116
+ * background shading for the entire table. Individual cells can
1117
+ * override this with their own shading.
1118
+ *
1119
+ * @param shading - Table shading configuration
1120
+ * @returns This table instance for method chaining
1121
+ *
1122
+ * @example
1123
+ * ```typescript
1124
+ * table.setShading({ fill: 'F0F0F0' }); // Light gray background
1125
+ * table.setShading({ fill: 'FFFFFF', pattern: 'pct12', color: '000000' });
1126
+ * ```
1127
+ */
1128
+ setShading(shading: TableShading): this {
1129
+ const prev = this.formatting.shading;
1130
+ this.formatting.shading = shading;
1131
+ if (this.trackingContext?.isEnabled() && prev !== shading) {
1132
+ this.trackingContext.trackTableChange(this, 'shading', prev, shading);
1133
+ }
1134
+ return this;
1135
+ }
1136
+
1137
+ /**
1138
+ * Gets table-level shading configuration
1139
+ * @returns Table shading or undefined if not set
1140
+ */
1141
+ getShading(): TableShading | undefined {
1142
+ return this.formatting.shading;
1143
+ }
1144
+
1145
+ /**
1146
+ * Sets table width type
1147
+ *
1148
+ * Defines how the table width value should be interpreted.
1149
+ * Per ECMA-376 Part 1 §17.4.64
1150
+ *
1151
+ * @param type - Width interpretation type
1152
+ * - 'auto': Automatic width (ignores width value)
1153
+ * - 'dxa': Width in twips (1/20th of a point)
1154
+ * - 'pct': Width as percentage (value * 50 = percentage, e.g., 5000 = 100%)
1155
+ * @returns This table instance for method chaining
1156
+ *
1157
+ * @example
1158
+ * ```typescript
1159
+ * table.setWidth(5000).setWidthType('pct'); // 100% page width
1160
+ * table.setWidth(9360).setWidthType('dxa'); // 6.5 inches (absolute)
1161
+ * table.setWidthType('auto'); // Auto-fit content
1162
+ * ```
1163
+ */
1164
+ setWidthType(type: TableWidthType): this {
1165
+ const prev = this.formatting.widthType;
1166
+ this.formatting.widthType = type;
1167
+ if (this.trackingContext?.isEnabled() && prev !== type) {
1168
+ this.trackingContext.trackTableChange(this, 'widthType', prev, type);
1169
+ }
1170
+ return this;
1171
+ }
1172
+
1173
+ /**
1174
+ * Sets cell spacing type
1175
+ * @param type - Cell spacing type
1176
+ * @returns This table for chaining
1177
+ */
1178
+ setCellSpacingType(type: TableWidthType): this {
1179
+ const prev = this.formatting.cellSpacingType;
1180
+ this.formatting.cellSpacingType = type;
1181
+ if (this.trackingContext?.isEnabled() && prev !== type) {
1182
+ this.trackingContext.trackTableChange(this, 'cellSpacingType', prev, type);
1183
+ }
1184
+ return this;
1185
+ }
1186
+
1187
+ /**
1188
+ * Gets a copy of the table formatting
1189
+ *
1190
+ * Returns a copy of all formatting properties including width, alignment,
1191
+ * layout, borders, and other table-level settings.
1192
+ *
1193
+ * @returns Copy of the table formatting object
1194
+ *
1195
+ * @example
1196
+ * ```typescript
1197
+ * const formatting = table.getFormatting();
1198
+ * console.log(`Width: ${formatting.width} twips`);
1199
+ * console.log(`Layout: ${formatting.layout}`);
1200
+ * ```
1201
+ */
1202
+ getFormatting(): TableFormatting {
1203
+ return { ...this.formatting };
1204
+ }
1205
+
1206
+ // ============================================================================
1207
+ // Individual Formatting Getters
1208
+ // ============================================================================
1209
+
1210
+ /**
1211
+ * Gets the table width in twips
1212
+ * @returns Width in twips or undefined if not set
1213
+ */
1214
+ getWidth(): number | undefined {
1215
+ return this.formatting.width;
1216
+ }
1217
+
1218
+ /**
1219
+ * Gets the table width type
1220
+ * @returns Width type ('auto', 'dxa', 'pct', 'nil') or undefined
1221
+ */
1222
+ getWidthType(): string | undefined {
1223
+ return this.formatting.widthType;
1224
+ }
1225
+
1226
+ /**
1227
+ * Gets the table horizontal alignment
1228
+ * @returns Alignment ('left', 'center', 'right') or undefined
1229
+ */
1230
+ getAlignment(): string | undefined {
1231
+ return this.formatting.alignment;
1232
+ }
1233
+
1234
+ /**
1235
+ * Gets the table layout type
1236
+ * @returns Layout ('autofit', 'fixed') or undefined
1237
+ */
1238
+ getLayout(): string | undefined {
1239
+ return this.formatting.layout;
1240
+ }
1241
+
1242
+ /**
1243
+ * Gets the table left indentation in twips
1244
+ * @returns Indent in twips or undefined if not set
1245
+ */
1246
+ getIndent(): number | undefined {
1247
+ return this.formatting.indent;
1248
+ }
1249
+
1250
+ /**
1251
+ * Gets the table borders
1252
+ * @returns Borders object or undefined
1253
+ */
1254
+ getBorders(): TableFormatting['borders'] | undefined {
1255
+ return this.formatting.borders;
1256
+ }
1257
+
1258
+ /**
1259
+ * Gets the column widths (table grid)
1260
+ * @returns Array of column widths in twips
1261
+ */
1262
+ getColumnWidths(): number[] {
1263
+ return [...(this.formatting.tableGrid || [])];
1264
+ }
1265
+
1266
+ /**
1267
+ * Gets the cell spacing value
1268
+ * @returns Cell spacing in twips or undefined
1269
+ */
1270
+ getCellSpacing(): number | undefined {
1271
+ return this.formatting.cellSpacing;
1272
+ }
1273
+
1274
+ /**
1275
+ * Gets the default cell margins (padding) for all cells
1276
+ * Per ECMA-376 Part 1 §17.4.42 (tblCellMar)
1277
+ * @returns Cell margins object or undefined if not set
1278
+ */
1279
+ getCellMargins(): TableCellMargins | undefined {
1280
+ return this.formatting.cellMargins;
1281
+ }
1282
+
1283
+ /**
1284
+ * Sets the default cell margins (padding) for all cells
1285
+ * Per ECMA-376 Part 1 §17.4.42 (tblCellMar)
1286
+ * @param margins - Cell margins in twips
1287
+ * @returns This table for chaining
1288
+ * @example
1289
+ * ```typescript
1290
+ * table.setCellMargins({ top: 43, left: 115, bottom: 43, right: 115 });
1291
+ * ```
1292
+ */
1293
+ setCellMargins(margins: TableCellMargins): this {
1294
+ const prev = this.formatting.cellMargins;
1295
+ this.formatting.cellMargins = margins;
1296
+ if (this.trackingContext?.isEnabled() && prev !== margins) {
1297
+ this.trackingContext.trackTableChange(this, 'cellMargins', prev, margins);
1298
+ }
1299
+ return this;
1300
+ }
1301
+
1302
+ /**
1303
+ * Gets the table style ID
1304
+ * @returns Style ID or undefined if not set
1305
+ */
1306
+ getStyle(): string | undefined {
1307
+ return this.formatting.style;
1308
+ }
1309
+
1310
+ /**
1311
+ * Gets the raw table look (tblLook) value
1312
+ * @returns Table look hex string or undefined if not set
1313
+ */
1314
+ getTblLook(): string | undefined {
1315
+ return this.formatting.tblLook;
1316
+ }
1317
+
1318
+ /**
1319
+ * Gets the table position properties for floating tables
1320
+ * @returns Table position properties or undefined if not set
1321
+ */
1322
+ getPosition(): TablePositionProperties | undefined {
1323
+ return this.formatting.position ? { ...this.formatting.position } : undefined;
1324
+ }
1325
+
1326
+ /**
1327
+ * Gets whether table can overlap with other floating tables
1328
+ * @returns True if overlap is allowed, false if not, undefined if not set
1329
+ */
1330
+ getOverlap(): boolean | undefined {
1331
+ return this.formatting.overlap;
1332
+ }
1333
+
1334
+ /**
1335
+ * Gets whether bidirectional (RTL) visual layout is enabled
1336
+ * @returns True if RTL layout, false if LTR, undefined if not set
1337
+ */
1338
+ getBidiVisual(): boolean | undefined {
1339
+ return this.formatting.bidiVisual;
1340
+ }
1341
+
1342
+ /**
1343
+ * Gets the table grid column widths
1344
+ * @returns Array of column widths in twips, or undefined if not set
1345
+ */
1346
+ getTableGrid(): number[] | undefined {
1347
+ return this.formatting.tableGrid ? [...this.formatting.tableGrid] : undefined;
1348
+ }
1349
+
1350
+ /**
1351
+ * Gets the table caption for accessibility
1352
+ * @returns Caption text or undefined if not set
1353
+ */
1354
+ getCaption(): string | undefined {
1355
+ return this.formatting.caption;
1356
+ }
1357
+
1358
+ /**
1359
+ * Gets the table description for accessibility
1360
+ * @returns Description text or undefined if not set
1361
+ */
1362
+ getDescription(): string | undefined {
1363
+ return this.formatting.description;
1364
+ }
1365
+
1366
+ /**
1367
+ * Gets the cell spacing type
1368
+ * @returns Cell spacing type or undefined if not set
1369
+ */
1370
+ getCellSpacingType(): TableWidthType | undefined {
1371
+ return this.formatting.cellSpacingType;
1372
+ }
1373
+
1374
+ // ============================================================================
1375
+ // Checker Methods
1376
+ // ============================================================================
1377
+
1378
+ /**
1379
+ * Checks if the table has any rows
1380
+ * @returns True if table has rows
1381
+ */
1382
+ hasRows(): boolean {
1383
+ return this.rows.length > 0;
1384
+ }
1385
+
1386
+ /**
1387
+ * Checks if the table is a floating table (has positioning)
1388
+ * @returns True if table has positioning properties
1389
+ */
1390
+ isFloating(): boolean {
1391
+ return this.formatting.position !== undefined;
1392
+ }
1393
+
1394
+ /**
1395
+ * Checks if the table has a style applied
1396
+ * @returns True if table has a style
1397
+ */
1398
+ hasStyle(): boolean {
1399
+ return this.formatting.style !== undefined && this.formatting.style !== '';
1400
+ }
1401
+
1402
+ /**
1403
+ * Sets the StylesManager reference for conditional formatting resolution.
1404
+ * Propagates to all paragraphs in all cells.
1405
+ * @internal
1406
+ */
1407
+ _setStylesManager(
1408
+ manager: import("../formatting/StylesManager").StylesManager
1409
+ ): void {
1410
+ this._stylesManager = manager;
1411
+ // Propagate to all paragraphs in all cells
1412
+ for (const row of this.rows) {
1413
+ for (const cell of row.getCells()) {
1414
+ for (const para of cell.getParagraphs()) {
1415
+ para._setStylesManager(manager);
1416
+ }
1417
+ }
1418
+ }
1419
+ }
1420
+
1421
+ /**
1422
+ * Gets the StylesManager reference for conditional formatting resolution.
1423
+ * @internal
1424
+ */
1425
+ _getStylesManager():
1426
+ | import("../formatting/StylesManager").StylesManager
1427
+ | undefined {
1428
+ return this._stylesManager;
1429
+ }
1430
+
1431
+ /**
1432
+ * Converts the table to WordprocessingML XML element
1433
+ * @returns XMLElement representing the table
1434
+ */
1435
+ toXML(): XMLElement {
1436
+ const tblPrChildren: XMLElement[] = [];
1437
+
1438
+ // CT_TblPr element order per ECMA-376:
1439
+ // tblStyle, tblpPr, tblOverlap, bidiVisual, tblStyleRowBandSize, tblStyleColBandSize,
1440
+ // tblW, jc, tblCellSpacing, tblInd, tblBorders, shd, tblLayout, tblCellMar, tblLook,
1441
+ // tblCaption, tblDescription, tblPrChange
1442
+
1443
+ // 1. tblStyle
1444
+ if (this.formatting.style) {
1445
+ tblPrChildren.push(
1446
+ XMLBuilder.wSelf("tblStyle", { "w:val": this.formatting.style })
1447
+ );
1448
+ }
1449
+
1450
+ // 2. tblpPr - table positioning properties (floating tables)
1451
+ if (this.formatting.position) {
1452
+ const pos = this.formatting.position;
1453
+ const posAttrs: Record<string, string | number> = {};
1454
+
1455
+ if (pos.x !== undefined) posAttrs["w:tblpX"] = pos.x;
1456
+ if (pos.y !== undefined) posAttrs["w:tblpY"] = pos.y;
1457
+ if (pos.horizontalAnchor) posAttrs["w:horzAnchor"] = pos.horizontalAnchor;
1458
+ if (pos.verticalAnchor) posAttrs["w:vertAnchor"] = pos.verticalAnchor;
1459
+ if (pos.horizontalAlignment)
1460
+ posAttrs["w:tblpXSpec"] = pos.horizontalAlignment;
1461
+ if (pos.verticalAlignment)
1462
+ posAttrs["w:tblpYSpec"] = pos.verticalAlignment;
1463
+ if (pos.leftFromText !== undefined)
1464
+ posAttrs["w:leftFromText"] = pos.leftFromText;
1465
+ if (pos.rightFromText !== undefined)
1466
+ posAttrs["w:rightFromText"] = pos.rightFromText;
1467
+ if (pos.topFromText !== undefined)
1468
+ posAttrs["w:topFromText"] = pos.topFromText;
1469
+ if (pos.bottomFromText !== undefined)
1470
+ posAttrs["w:bottomFromText"] = pos.bottomFromText;
1471
+
1472
+ if (Object.keys(posAttrs).length > 0) {
1473
+ tblPrChildren.push(XMLBuilder.wSelf("tblpPr", posAttrs));
1474
+ }
1475
+ }
1476
+
1477
+ // 3. tblOverlap
1478
+ if (this.formatting.overlap !== undefined) {
1479
+ tblPrChildren.push(
1480
+ XMLBuilder.wSelf("tblOverlap", {
1481
+ "w:val": this.formatting.overlap ? "overlap" : "never",
1482
+ })
1483
+ );
1484
+ }
1485
+
1486
+ // 4. bidiVisual
1487
+ if (this.formatting.bidiVisual) {
1488
+ tblPrChildren.push(XMLBuilder.wSelf("bidiVisual"));
1489
+ }
1490
+
1491
+ // 5-6. tblStyleRowBandSize / tblStyleColBandSize
1492
+ // Only valid within table style definitions (CT_TblPrBase in w:style),
1493
+ // not in direct tblPr. Style.ts handles serialization in style context.
1494
+ // Values are preserved in formatting for round-trip and style use.
1495
+
1496
+ // 7. tblW
1497
+ if (this.formatting.width !== undefined) {
1498
+ const widthType = this.formatting.widthType || "dxa";
1499
+ tblPrChildren.push(
1500
+ XMLBuilder.wSelf("tblW", {
1501
+ "w:w": this.formatting.width,
1502
+ "w:type": widthType,
1503
+ })
1504
+ );
1505
+ }
1506
+
1507
+ // 8. jc (alignment)
1508
+ if (this.formatting.alignment) {
1509
+ tblPrChildren.push(
1510
+ XMLBuilder.wSelf("jc", { "w:val": this.formatting.alignment })
1511
+ );
1512
+ }
1513
+
1514
+ // 9. tblCellSpacing
1515
+ if (this.formatting.cellSpacing !== undefined) {
1516
+ const cellSpacingType = this.formatting.cellSpacingType || "dxa";
1517
+ tblPrChildren.push(
1518
+ XMLBuilder.wSelf("tblCellSpacing", {
1519
+ "w:w": this.formatting.cellSpacing,
1520
+ "w:type": cellSpacingType,
1521
+ })
1522
+ );
1523
+ }
1524
+
1525
+ // 10. tblInd
1526
+ if (this.formatting.indent !== undefined) {
1527
+ tblPrChildren.push(
1528
+ XMLBuilder.wSelf("tblInd", {
1529
+ "w:w": this.formatting.indent,
1530
+ "w:type": "dxa",
1531
+ })
1532
+ );
1533
+ }
1534
+
1535
+ // 11. tblBorders
1536
+ if (this.formatting.borders) {
1537
+ const borderElements: XMLElement[] = [];
1538
+ const borders = this.formatting.borders;
1539
+
1540
+ if (borders.top) {
1541
+ borderElements.push(XMLBuilder.createBorder("top", borders.top));
1542
+ }
1543
+ if (borders.left) {
1544
+ borderElements.push(XMLBuilder.createBorder("left", borders.left));
1545
+ }
1546
+ if (borders.bottom) {
1547
+ borderElements.push(XMLBuilder.createBorder("bottom", borders.bottom));
1548
+ }
1549
+ if (borders.right) {
1550
+ borderElements.push(XMLBuilder.createBorder("right", borders.right));
1551
+ }
1552
+ if (borders.insideH) {
1553
+ borderElements.push(
1554
+ XMLBuilder.createBorder("insideH", borders.insideH)
1555
+ );
1556
+ }
1557
+ if (borders.insideV) {
1558
+ borderElements.push(
1559
+ XMLBuilder.createBorder("insideV", borders.insideV)
1560
+ );
1561
+ }
1562
+
1563
+ if (borderElements.length > 0) {
1564
+ tblPrChildren.push(
1565
+ XMLBuilder.w("tblBorders", undefined, borderElements)
1566
+ );
1567
+ }
1568
+ }
1569
+
1570
+ // 12. shd (table shading/background)
1571
+ if (this.formatting.shading) {
1572
+ const shdAttrs = buildShadingAttributes(this.formatting.shading);
1573
+ if (Object.keys(shdAttrs).length > 0) {
1574
+ tblPrChildren.push(XMLBuilder.wSelf("shd", shdAttrs));
1575
+ }
1576
+ }
1577
+
1578
+ // 13. tblLayout
1579
+ if (this.formatting.layout) {
1580
+ tblPrChildren.push(
1581
+ XMLBuilder.wSelf("tblLayout", { "w:type": this.formatting.layout })
1582
+ );
1583
+ }
1584
+
1585
+ // 14. tblCellMar
1586
+ if (this.formatting.cellMargins) {
1587
+ const marginElements: XMLElement[] = [];
1588
+ if (this.formatting.cellMargins.top !== undefined) {
1589
+ marginElements.push(
1590
+ XMLBuilder.wSelf("top", {
1591
+ "w:w": this.formatting.cellMargins.top,
1592
+ "w:type": "dxa",
1593
+ })
1594
+ );
1595
+ }
1596
+ if (this.formatting.cellMargins.left !== undefined) {
1597
+ marginElements.push(
1598
+ XMLBuilder.wSelf("left", {
1599
+ "w:w": this.formatting.cellMargins.left,
1600
+ "w:type": "dxa",
1601
+ })
1602
+ );
1603
+ }
1604
+ if (this.formatting.cellMargins.bottom !== undefined) {
1605
+ marginElements.push(
1606
+ XMLBuilder.wSelf("bottom", {
1607
+ "w:w": this.formatting.cellMargins.bottom,
1608
+ "w:type": "dxa",
1609
+ })
1610
+ );
1611
+ }
1612
+ if (this.formatting.cellMargins.right !== undefined) {
1613
+ marginElements.push(
1614
+ XMLBuilder.wSelf("right", {
1615
+ "w:w": this.formatting.cellMargins.right,
1616
+ "w:type": "dxa",
1617
+ })
1618
+ );
1619
+ }
1620
+ if (marginElements.length > 0) {
1621
+ tblPrChildren.push(
1622
+ XMLBuilder.w("tblCellMar", undefined, marginElements)
1623
+ );
1624
+ }
1625
+ }
1626
+
1627
+ // 15. tblLook
1628
+ if (this.formatting.tblLook) {
1629
+ tblPrChildren.push(
1630
+ XMLBuilder.wSelf("tblLook", { "w:val": this.formatting.tblLook })
1631
+ );
1632
+ }
1633
+
1634
+ // Add table caption (accessibility)
1635
+ if (this.formatting.caption) {
1636
+ tblPrChildren.push(
1637
+ XMLBuilder.wSelf("tblCaption", { "w:val": this.formatting.caption })
1638
+ );
1639
+ }
1640
+
1641
+ // Add table description (accessibility)
1642
+ if (this.formatting.description) {
1643
+ tblPrChildren.push(
1644
+ XMLBuilder.wSelf("tblDescription", {
1645
+ "w:val": this.formatting.description,
1646
+ })
1647
+ );
1648
+ }
1649
+
1650
+ // Add table property change (w:tblPrChange) per ECMA-376 Part 1 §17.13.5.36
1651
+ // Must be last child of w:tblPr
1652
+ if (this.tblPrChange) {
1653
+ const changeAttrs: Record<string, string | number> = {
1654
+ "w:id": this.tblPrChange.id,
1655
+ "w:author": this.tblPrChange.author,
1656
+ "w:date": this.tblPrChange.date,
1657
+ };
1658
+ const prevTblPrChildren: XMLElement[] = [];
1659
+ const prev = this.tblPrChange.previousProperties;
1660
+ if (prev) {
1661
+ if (prev.style) {
1662
+ prevTblPrChildren.push(XMLBuilder.wSelf("tblStyle", { "w:val": prev.style }));
1663
+ }
1664
+ if (prev.width !== undefined) {
1665
+ prevTblPrChildren.push(XMLBuilder.wSelf("tblW", {
1666
+ "w:w": prev.width,
1667
+ "w:type": prev.widthType || "dxa",
1668
+ }));
1669
+ }
1670
+ if (prev.alignment) {
1671
+ prevTblPrChildren.push(XMLBuilder.wSelf("jc", { "w:val": prev.alignment }));
1672
+ }
1673
+ if (prev.indent !== undefined) {
1674
+ prevTblPrChildren.push(XMLBuilder.wSelf("tblInd", { "w:w": prev.indent, "w:type": "dxa" }));
1675
+ }
1676
+ if (prev.borders) {
1677
+ const borderChildren: XMLElement[] = [];
1678
+ const bNames = ['top', 'left', 'bottom', 'right', 'insideH', 'insideV'] as const;
1679
+ for (const name of bNames) {
1680
+ const b = prev.borders[name];
1681
+ if (b) {
1682
+ const bAttrs: Record<string, string | number> = {};
1683
+ if (b.style) bAttrs['w:val'] = b.style;
1684
+ if (b.size !== undefined) bAttrs['w:sz'] = b.size;
1685
+ if (b.color) bAttrs['w:color'] = b.color;
1686
+ borderChildren.push(XMLBuilder.wSelf(name, bAttrs));
1687
+ }
1688
+ }
1689
+ if (borderChildren.length > 0) {
1690
+ prevTblPrChildren.push(XMLBuilder.w("tblBorders", undefined, borderChildren));
1691
+ }
1692
+ }
1693
+ if (prev.shading) {
1694
+ const shadingAttrs = buildShadingAttributes(prev.shading);
1695
+ if (Object.keys(shadingAttrs).length > 0) {
1696
+ prevTblPrChildren.push(XMLBuilder.wSelf("shd", shadingAttrs));
1697
+ }
1698
+ }
1699
+ if (prev.layout) {
1700
+ prevTblPrChildren.push(XMLBuilder.wSelf("tblLayout", { "w:type": prev.layout }));
1701
+ }
1702
+ if (prev.cellSpacing !== undefined) {
1703
+ const csAttrs: Record<string, string | number> = { "w:w": prev.cellSpacing, "w:type": prev.cellSpacingType || "dxa" };
1704
+ prevTblPrChildren.push(XMLBuilder.wSelf("tblCellSpacing", csAttrs));
1705
+ }
1706
+ if (prev.bidiVisual) {
1707
+ prevTblPrChildren.push(XMLBuilder.wSelf("bidiVisual"));
1708
+ }
1709
+ if (prev.cellMargins) {
1710
+ const cmChildren: XMLElement[] = [];
1711
+ for (const side of ['top', 'start', 'bottom', 'end'] as const) {
1712
+ const val = (prev.cellMargins as Record<string, number | undefined>)[side];
1713
+ if (val !== undefined) {
1714
+ cmChildren.push(XMLBuilder.wSelf(side, { "w:w": val, "w:type": "dxa" }));
1715
+ }
1716
+ }
1717
+ if (cmChildren.length > 0) {
1718
+ prevTblPrChildren.push(XMLBuilder.w("tblCellMar", undefined, cmChildren));
1719
+ }
1720
+ }
1721
+ if (prev.tblLook) {
1722
+ prevTblPrChildren.push(XMLBuilder.wSelf("tblLook", { "w:val": prev.tblLook }));
1723
+ }
1724
+ if (prev.tblStyleRowBandSize !== undefined) {
1725
+ prevTblPrChildren.push(XMLBuilder.wSelf("tblStyleRowBandSize", { "w:val": prev.tblStyleRowBandSize }));
1726
+ }
1727
+ if (prev.tblStyleColBandSize !== undefined) {
1728
+ prevTblPrChildren.push(XMLBuilder.wSelf("tblStyleColBandSize", { "w:val": prev.tblStyleColBandSize }));
1729
+ }
1730
+ if (prev.overlap) {
1731
+ prevTblPrChildren.push(XMLBuilder.wSelf("tblOverlap", { "w:val": prev.overlap }));
1732
+ }
1733
+ if (prev.caption) {
1734
+ prevTblPrChildren.push(XMLBuilder.wSelf("tblCaption", { "w:val": prev.caption }));
1735
+ }
1736
+ if (prev.description) {
1737
+ prevTblPrChildren.push(XMLBuilder.wSelf("tblDescription", { "w:val": prev.description }));
1738
+ }
1739
+ }
1740
+ const prevTblPr = XMLBuilder.w("tblPr", undefined, prevTblPrChildren);
1741
+ tblPrChildren.push(XMLBuilder.w("tblPrChange", changeAttrs, [prevTblPr]));
1742
+ }
1743
+
1744
+ // Build table element
1745
+ const tableChildren: XMLElement[] = [];
1746
+
1747
+ // Add table properties
1748
+ tableChildren.push(XMLBuilder.w("tblPr", undefined, tblPrChildren));
1749
+
1750
+ // Add table grid (column definitions)
1751
+ // Per ECMA-376 Part 1 §17.4.49, w:tblGrid MUST be present in w:tbl
1752
+ // Use custom tableGrid if specified, otherwise auto-generate
1753
+ // CRITICAL: Use getTotalGridSpan() instead of getCellCount() to account for
1754
+ // cells with gridSpan (column span). A row with 2 cells where one spans 4 columns
1755
+ // should generate 5 grid columns, not 2.
1756
+ const gridWidths = this.formatting.tableGrid;
1757
+ const maxColumns = gridWidths
1758
+ ? gridWidths.length
1759
+ : Math.max(...this.rows.map((row) => row.getTotalGridSpan()), 0);
1760
+
1761
+ // Always generate tblGrid - use at least 1 column for empty tables
1762
+ const gridColumnCount = maxColumns > 0 ? maxColumns : 1;
1763
+ const tblGridChildren: XMLElement[] = [];
1764
+
1765
+ for (let i = 0; i < gridColumnCount; i++) {
1766
+ if (gridWidths?.[i] !== undefined) {
1767
+ // Use specified grid width
1768
+ tblGridChildren.push(
1769
+ XMLBuilder.wSelf("gridCol", { "w:w": gridWidths[i] })
1770
+ );
1771
+ } else {
1772
+ // Auto width (default to 2880 twips = 2 inches)
1773
+ tblGridChildren.push(XMLBuilder.wSelf("gridCol", { "w:w": 2880 }));
1774
+ }
1775
+ }
1776
+ tableChildren.push(XMLBuilder.w("tblGrid", undefined, tblGridChildren));
1777
+
1778
+ // Add rows
1779
+ for (const row of this.rows) {
1780
+ tableChildren.push(row.toXML());
1781
+ }
1782
+
1783
+ return XMLBuilder.w("tbl", undefined, tableChildren);
1784
+ }
1785
+
1786
+ /**
1787
+ * Removes a row from the table
1788
+ *
1789
+ * Deletes the row at the specified index and shifts subsequent rows up.
1790
+ *
1791
+ * @param index - The row index to remove (0-based)
1792
+ * @returns True if the row was removed, false if index was invalid
1793
+ *
1794
+ * @example
1795
+ * ```typescript
1796
+ * table.removeRow(0); // Remove first row
1797
+ * table.removeRow(2); // Remove third row
1798
+ * ```
1799
+ */
1800
+ removeRow(index: number): boolean {
1801
+ if (index >= 0 && index < this.rows.length) {
1802
+ // When tracking enabled, mark cells with cellDel and wrap content in w:del
1803
+ if (this.trackingContext?.isEnabled()) {
1804
+ const author = this.trackingContext.getAuthor();
1805
+ const row = this.rows[index]!;
1806
+ for (const cell of row.getCells()) {
1807
+ const cellDelRevision = Revision.createTableCellDelete(author, []);
1808
+ cell.setCellRevision(cellDelRevision);
1809
+ // Wrap paragraph runs in w:del so content appears as deleted
1810
+ for (const para of cell.getParagraphs()) {
1811
+ const runs = para.getRuns();
1812
+ if (runs.length > 0) {
1813
+ const deletion = Revision.createDeletion(author, runs);
1814
+ para.addRevision(deletion);
1815
+ }
1816
+ }
1817
+ }
1818
+ return true;
1819
+ }
1820
+ this.rows.splice(index, 1);
1821
+ return true;
1822
+ }
1823
+ return false;
1824
+ }
1825
+
1826
+ /**
1827
+ * Inserts a row at a specific position
1828
+ *
1829
+ * Inserts a new row at the specified index. If no row is provided,
1830
+ * creates an empty row with columns matching the table's column count.
1831
+ *
1832
+ * @param index - Position to insert at (0-based, clamped to valid range)
1833
+ * @param row - Optional TableRow to insert (creates new row if not provided)
1834
+ * @returns The inserted TableRow instance
1835
+ *
1836
+ * @example
1837
+ * ```typescript
1838
+ * // Insert empty row at beginning
1839
+ * const row = table.insertRow(0);
1840
+ *
1841
+ * // Insert custom row in the middle
1842
+ * const customRow = new TableRow(3);
1843
+ * table.insertRow(2, customRow);
1844
+ * ```
1845
+ */
1846
+ insertRow(index: number, row?: TableRow): TableRow {
1847
+ // Clamp index to valid range
1848
+ if (index < 0) index = 0;
1849
+ if (index > this.rows.length) index = this.rows.length;
1850
+
1851
+ // Create new row if not provided, matching the column count
1852
+ if (!row) {
1853
+ const columnCount = this.getColumnCount();
1854
+ row = new TableRow(columnCount);
1855
+ }
1856
+
1857
+ // Insert the row
1858
+ this.rows.splice(index, 0, row);
1859
+
1860
+ // When tracking enabled, mark every cell in the new row with cellIns
1861
+ if (this.trackingContext?.isEnabled()) {
1862
+ const author = this.trackingContext.getAuthor();
1863
+ for (const cell of row.getCells()) {
1864
+ const revision = Revision.createTableCellInsert(author, []);
1865
+ cell.setCellRevision(revision);
1866
+ }
1867
+ }
1868
+
1869
+ return row;
1870
+ }
1871
+
1872
+ /**
1873
+ * Adds a column to all rows in the table
1874
+ *
1875
+ * Inserts a new cell in each row at the specified position.
1876
+ * If no index is provided, adds the column at the end.
1877
+ *
1878
+ * @param index - Optional position to insert the column (0-based, defaults to end)
1879
+ * @returns This table instance for method chaining
1880
+ *
1881
+ * @example
1882
+ * ```typescript
1883
+ * table.addColumn(); // Add column at end
1884
+ * table.addColumn(0); // Insert column at beginning
1885
+ * table.addColumn(2); // Insert column at position 2
1886
+ * ```
1887
+ */
1888
+ addColumn(index?: number): this {
1889
+ const isTracking = this.trackingContext?.isEnabled();
1890
+ const author = isTracking ? this.trackingContext!.getAuthor() : '';
1891
+
1892
+ for (const row of this.rows) {
1893
+ const cell = new TableCell();
1894
+
1895
+ // Mark cell as inserted when tracking
1896
+ if (isTracking) {
1897
+ const revision = Revision.createTableCellInsert(author, []);
1898
+ cell.setCellRevision(revision);
1899
+ }
1900
+
1901
+ const cells = row.getCells();
1902
+
1903
+ if (index === undefined || index >= cells.length) {
1904
+ // Add to end
1905
+ row.addCell(cell);
1906
+ } else {
1907
+ // Insert at specific position
1908
+ const idx = Math.max(0, index);
1909
+ row.insertCellAt(idx, cell);
1910
+ }
1911
+ }
1912
+ return this;
1913
+ }
1914
+
1915
+ /**
1916
+ * Removes a column from all rows in the table
1917
+ *
1918
+ * Deletes the cell at the specified column index in every row.
1919
+ *
1920
+ * @param index - The column index to remove (0-based)
1921
+ * @returns True if the column was removed from at least one row, false if index was invalid
1922
+ *
1923
+ * @example
1924
+ * ```typescript
1925
+ * table.removeColumn(0); // Remove first column
1926
+ * table.removeColumn(2); // Remove third column
1927
+ * ```
1928
+ */
1929
+ removeColumn(index: number): boolean {
1930
+ if (index < 0 || this.rows.length === 0) {
1931
+ return false;
1932
+ }
1933
+
1934
+ // When tracking enabled, mark cells with cellDel instead of removing
1935
+ if (this.trackingContext?.isEnabled()) {
1936
+ const author = this.trackingContext.getAuthor();
1937
+ let marked = false;
1938
+ for (const row of this.rows) {
1939
+ const cells = row.getCells();
1940
+ if (index < cells.length) {
1941
+ const revision = Revision.createTableCellDelete(author, []);
1942
+ cells[index]!.setCellRevision(revision);
1943
+ marked = true;
1944
+ }
1945
+ }
1946
+ return marked;
1947
+ }
1948
+
1949
+ let removed = false;
1950
+ for (const row of this.rows) {
1951
+ const cells = row.getCells();
1952
+ if (index < cells.length) {
1953
+ row.removeCellAt(index);
1954
+ removed = true;
1955
+ }
1956
+ }
1957
+
1958
+ return removed;
1959
+ }
1960
+
1961
+ /**
1962
+ * Gets the maximum column count across all rows
1963
+ *
1964
+ * Returns the highest number of cells in any row. Useful since
1965
+ * rows may have different numbers of cells.
1966
+ *
1967
+ * @returns Maximum number of columns in the table
1968
+ *
1969
+ * @example
1970
+ * ```typescript
1971
+ * console.log(`Table has up to ${table.getColumnCount()} columns`);
1972
+ * ```
1973
+ */
1974
+ getColumnCount(): number {
1975
+ if (this.rows.length === 0) {
1976
+ return 0;
1977
+ }
1978
+ return Math.max(...this.rows.map((row) => row.getCellCount()));
1979
+ }
1980
+
1981
+ /**
1982
+ * Sets specific widths for table columns
1983
+ *
1984
+ * Defines the width of each column. Use null for auto-width columns.
1985
+ *
1986
+ * @param widths - Array of column widths in twips (null = auto width)
1987
+ * @returns This table instance for method chaining
1988
+ *
1989
+ * @example
1990
+ * ```typescript
1991
+ * // First column 2", second column 3", third auto
1992
+ * table.setColumnWidths([2880, 4320, null]);
1993
+ * ```
1994
+ */
1995
+ setColumnWidths(widths: (number | null)[]): this {
1996
+ // Use tableGrid (the property that toXML() actually reads)
1997
+ // Convert null values to undefined for auto-width handling in toXML()
1998
+ this.formatting.tableGrid = widths.map(w => w === null ? undefined : w) as number[];
1999
+ return this;
2000
+ }
2001
+
2002
+ /**
2003
+ * Creates a new Table instance
2004
+ *
2005
+ * Factory method for creating a table with specified dimensions and formatting.
2006
+ *
2007
+ * @param rows - Number of rows to create
2008
+ * @param columns - Number of columns per row
2009
+ * @param formatting - Optional table formatting properties
2010
+ * @returns New Table instance
2011
+ *
2012
+ * @example
2013
+ * ```typescript
2014
+ * const table = Table.create(3, 4); // 3 rows × 4 columns
2015
+ * const styledTable = Table.create(2, 3, {
2016
+ * alignment: 'center',
2017
+ * layout: 'auto'
2018
+ * });
2019
+ * ```
2020
+ */
2021
+ static create(
2022
+ rows?: number,
2023
+ columns?: number,
2024
+ formatting?: TableFormatting
2025
+ ): Table {
2026
+ return new Table(rows, columns, formatting);
2027
+ }
2028
+
2029
+ /**
2030
+ * Merges cells into a single cell (uses columnSpan and rowSpan)
2031
+ * @param startRow - Starting row index (0-based)
2032
+ * @param startCol - Starting column index (0-based)
2033
+ * @param endRow - Ending row index (0-based, inclusive)
2034
+ * @param endCol - Ending column index (0-based, inclusive)
2035
+ * @returns This table for chaining
2036
+ * @example
2037
+ * ```typescript
2038
+ * table.mergeCells(0, 0, 0, 2); // Merge cells across columns in first row
2039
+ * table.mergeCells(0, 0, 2, 0); // Merge cells down rows in first column
2040
+ * ```
2041
+ */
2042
+ mergeCells(
2043
+ startRow: number,
2044
+ startCol: number,
2045
+ endRow: number,
2046
+ endCol: number
2047
+ ): this {
2048
+ // Validate bounds
2049
+ if (
2050
+ startRow < 0 ||
2051
+ endRow >= this.rows.length ||
2052
+ startCol < 0 ||
2053
+ endCol < 0
2054
+ ) {
2055
+ return this;
2056
+ }
2057
+
2058
+ // Validate that end position is >= start position
2059
+ if (endRow < startRow || endCol < startCol) {
2060
+ return this;
2061
+ }
2062
+
2063
+ const cell = this.getCell(startRow, startCol);
2064
+ if (!cell) {
2065
+ return this;
2066
+ }
2067
+
2068
+ // Check for merge conflicts - cells already part of another merge region
2069
+ // This prevents undefined behavior when merging overlapping regions
2070
+ for (let row = startRow; row <= endRow; row++) {
2071
+ for (let col = startCol; col <= endCol; col++) {
2072
+ const checkCell = this.getCell(row, col);
2073
+ if (!checkCell) continue;
2074
+
2075
+ // Check if cell is already part of a vertical merge
2076
+ const vMerge = checkCell.getVerticalMerge();
2077
+ if (vMerge) {
2078
+ // Allow if this is the start cell and it's a 'restart' (we'll overwrite it)
2079
+ if (row === startRow && col === startCol && vMerge === "restart") {
2080
+ continue;
2081
+ }
2082
+ // Cell is part of an existing vertical merge - conflict
2083
+ throw new Error(
2084
+ `Cannot merge cells: Cell at row ${row}, column ${col} is already part of a vertical merge region. ` +
2085
+ `Use splitCell() or clear existing merges before creating new merge regions.`
2086
+ );
2087
+ }
2088
+
2089
+ // Check if cell has a column span > 1 (already part of horizontal merge)
2090
+ const colSpan = checkCell.getColumnSpan();
2091
+ if (colSpan > 1) {
2092
+ // Allow if this is the start cell (we'll overwrite its span)
2093
+ if (row === startRow && col === startCol) {
2094
+ continue;
2095
+ }
2096
+ // Cell has existing column span - conflict
2097
+ throw new Error(
2098
+ `Cannot merge cells: Cell at row ${row}, column ${col} already has a column span of ${colSpan}. ` +
2099
+ `Use splitCell() or clear existing merges before creating new merge regions.`
2100
+ );
2101
+ }
2102
+ }
2103
+ }
2104
+
2105
+ // Set column span if merging horizontally
2106
+ if (endCol > startCol) {
2107
+ cell.setColumnSpan(endCol - startCol + 1);
2108
+ }
2109
+
2110
+ // Set vertical merge if merging vertically
2111
+ if (endRow > startRow) {
2112
+ // First cell starts the merge region
2113
+ cell.setVerticalMerge("restart");
2114
+
2115
+ // Subsequent cells continue the merge
2116
+ for (let row = startRow + 1; row <= endRow; row++) {
2117
+ const mergeCell = this.getCell(row, startCol);
2118
+ if (mergeCell) {
2119
+ mergeCell.setVerticalMerge("continue");
2120
+ // If also merging horizontally, set column span on all merged cells
2121
+ if (endCol > startCol) {
2122
+ mergeCell.setColumnSpan(endCol - startCol + 1);
2123
+ }
2124
+ }
2125
+ }
2126
+ }
2127
+
2128
+ // When tracking, mark absorbed cells with cellMerge
2129
+ if (this.trackingContext?.isEnabled()) {
2130
+ const author = this.trackingContext.getAuthor();
2131
+ for (let row = startRow; row <= endRow; row++) {
2132
+ for (let col = startCol; col <= endCol; col++) {
2133
+ // Skip the start cell
2134
+ if (row === startRow && col === startCol) continue;
2135
+ const absorbedCell = this.getCell(row, col);
2136
+ if (absorbedCell) {
2137
+ const revision = Revision.createTableCellMerge(author, []);
2138
+ absorbedCell.setCellRevision(revision);
2139
+ }
2140
+ }
2141
+ }
2142
+ }
2143
+
2144
+ return this;
2145
+ }
2146
+
2147
+ /**
2148
+ * Splits a cell (removes column/row span)
2149
+ * @param row - Row index (0-based)
2150
+ * @param col - Column index (0-based)
2151
+ * @returns This table for chaining
2152
+ * @example
2153
+ * ```typescript
2154
+ * table.splitCell(0, 0); // Remove any spanning from cell
2155
+ * ```
2156
+ */
2157
+ splitCell(row: number, col: number): this {
2158
+ const cell = this.getCell(row, col);
2159
+ if (cell) {
2160
+ cell.setColumnSpan(1); // Reset to single cell
2161
+ cell.setVerticalMerge(undefined); // Clear vertical merge
2162
+ }
2163
+ return this;
2164
+ }
2165
+
2166
+ /**
2167
+ * Checks if a merge region would conflict with existing merges
2168
+ * @param startRow - Starting row index (0-based)
2169
+ * @param startCol - Starting column index (0-based)
2170
+ * @param endRow - Ending row index (0-based, inclusive)
2171
+ * @param endCol - Ending column index (0-based, inclusive)
2172
+ * @returns Object with valid flag and list of conflicts found
2173
+ * @example
2174
+ * ```typescript
2175
+ * const result = table.canMergeCells(0, 0, 2, 2);
2176
+ * if (!result.valid) {
2177
+ * console.log('Conflicts:', result.conflicts);
2178
+ * }
2179
+ * ```
2180
+ */
2181
+ canMergeCells(
2182
+ startRow: number,
2183
+ startCol: number,
2184
+ endRow: number,
2185
+ endCol: number
2186
+ ): { valid: boolean; conflicts: string[] } {
2187
+ const conflicts: string[] = [];
2188
+
2189
+ // Validate bounds
2190
+ if (
2191
+ startRow < 0 ||
2192
+ endRow >= this.rows.length ||
2193
+ startCol < 0 ||
2194
+ endCol < 0 ||
2195
+ endRow < startRow ||
2196
+ endCol < startCol
2197
+ ) {
2198
+ conflicts.push("Invalid cell range specified");
2199
+ return { valid: false, conflicts };
2200
+ }
2201
+
2202
+ // Check each cell in the proposed merge region
2203
+ for (let row = startRow; row <= endRow; row++) {
2204
+ for (let col = startCol; col <= endCol; col++) {
2205
+ const checkCell = this.getCell(row, col);
2206
+ if (!checkCell) {
2207
+ conflicts.push(`Cell at row ${row}, column ${col} does not exist`);
2208
+ continue;
2209
+ }
2210
+
2211
+ // Check if cell is already part of a vertical merge
2212
+ const vMerge = checkCell.getVerticalMerge();
2213
+ if (vMerge) {
2214
+ // Allow if this is the start cell and it's a 'restart'
2215
+ if (!(row === startRow && col === startCol && vMerge === "restart")) {
2216
+ conflicts.push(
2217
+ `Cell at row ${row}, column ${col} is part of a vertical merge (${vMerge})`
2218
+ );
2219
+ }
2220
+ }
2221
+
2222
+ // Check if cell has a column span > 1
2223
+ const colSpan = checkCell.getColumnSpan();
2224
+ if (colSpan > 1) {
2225
+ // Allow if this is the start cell
2226
+ if (!(row === startRow && col === startCol)) {
2227
+ conflicts.push(
2228
+ `Cell at row ${row}, column ${col} has column span of ${colSpan}`
2229
+ );
2230
+ }
2231
+ }
2232
+ }
2233
+ }
2234
+
2235
+ return { valid: conflicts.length === 0, conflicts };
2236
+ }
2237
+
2238
+ /**
2239
+ * Moves cell contents from one position to another
2240
+ * @param fromRow - Source row index
2241
+ * @param fromCol - Source column index
2242
+ * @param toRow - Target row index
2243
+ * @param toCol - Target column index
2244
+ * @returns This table for chaining
2245
+ * @example
2246
+ * ```typescript
2247
+ * table.moveCell(0, 0, 1, 1); // Move cell from [0,0] to [1,1]
2248
+ * ```
2249
+ */
2250
+ moveCell(
2251
+ fromRow: number,
2252
+ fromCol: number,
2253
+ toRow: number,
2254
+ toCol: number
2255
+ ): this {
2256
+ const fromCell = this.getCell(fromRow, fromCol);
2257
+ const toCell = this.getCell(toRow, toCol);
2258
+
2259
+ if (!fromCell || !toCell) {
2260
+ return this;
2261
+ }
2262
+
2263
+ // Copy all paragraphs from source to target
2264
+ const paragraphs = fromCell.getParagraphs();
2265
+ for (const para of paragraphs) {
2266
+ toCell.addParagraph(para);
2267
+ }
2268
+
2269
+ // Copy formatting
2270
+ const formatting = fromCell.getFormatting();
2271
+ if (formatting.shading) toCell.setShading(formatting.shading);
2272
+ if (formatting.borders) toCell.setBorders(formatting.borders);
2273
+ if (formatting.width) toCell.setWidth(formatting.width);
2274
+ if (formatting.verticalAlignment)
2275
+ toCell.setVerticalAlignment(formatting.verticalAlignment);
2276
+
2277
+ // Clear source cell (replace with empty paragraph)
2278
+ const row = this.getRow(fromRow);
2279
+ if (row) {
2280
+ const cells = row.getCells();
2281
+ cells[fromCol] = new TableCell();
2282
+ }
2283
+
2284
+ return this;
2285
+ }
2286
+
2287
+ /**
2288
+ * Swaps contents of two cells
2289
+ * @param row1 - First cell row index
2290
+ * @param col1 - First cell column index
2291
+ * @param row2 - Second cell row index
2292
+ * @param col2 - Second cell column index
2293
+ * @returns This table for chaining
2294
+ * @example
2295
+ * ```typescript
2296
+ * table.swapCells(0, 0, 1, 1); // Swap cells at [0,0] and [1,1]
2297
+ * ```
2298
+ */
2299
+ swapCells(row1: number, col1: number, row2: number, col2: number): this {
2300
+ const row1Obj = this.getRow(row1);
2301
+ const row2Obj = this.getRow(row2);
2302
+
2303
+ if (!row1Obj || !row2Obj) {
2304
+ return this;
2305
+ }
2306
+
2307
+ const cells1 = row1Obj.getCells();
2308
+ const cells2 = row2Obj.getCells();
2309
+
2310
+ if (col1 >= cells1.length || col2 >= cells2.length) {
2311
+ return this;
2312
+ }
2313
+
2314
+ // Swap cells
2315
+ const temp = cells1[col1];
2316
+ cells1[col1] = cells2[col2]!;
2317
+ cells2[col2] = temp!;
2318
+
2319
+ return this;
2320
+ }
2321
+
2322
+ /**
2323
+ * Sets the width of a specific column
2324
+ *
2325
+ * Defines the width for a single column without affecting others.
2326
+ *
2327
+ * @param columnIndex - The column index (0-based)
2328
+ * @param width - Width in twips (1/20th of a point)
2329
+ * @returns This table instance for method chaining
2330
+ *
2331
+ * @example
2332
+ * ```typescript
2333
+ * table.setColumnWidth(0, 2880); // First column = 2 inches
2334
+ * table.setColumnWidth(1, 1440); // Second column = 1 inch
2335
+ * ```
2336
+ */
2337
+ setColumnWidth(columnIndex: number, width: number): this {
2338
+ // Use tableGrid (the property that toXML() actually reads)
2339
+ const tableGrid = this.formatting.tableGrid || [];
2340
+ tableGrid[columnIndex] = width;
2341
+ this.formatting.tableGrid = tableGrid;
2342
+ return this;
2343
+ }
2344
+
2345
+ /**
2346
+ * Inserts multiple rows at once
2347
+ * @param startIndex - Position to insert at (0-based)
2348
+ * @param count - Number of rows to insert
2349
+ * @returns Array of inserted rows
2350
+ * @example
2351
+ * ```typescript
2352
+ * const rows = table.insertRows(2, 3); // Insert 3 rows at position 2
2353
+ * ```
2354
+ */
2355
+ insertRows(startIndex: number, count: number): TableRow[] {
2356
+ const insertedRows: TableRow[] = [];
2357
+ const columnCount = this.getColumnCount();
2358
+
2359
+ for (let i = 0; i < count; i++) {
2360
+ const row = new TableRow(columnCount);
2361
+ this.insertRow(startIndex + i, row);
2362
+ insertedRows.push(row);
2363
+ }
2364
+
2365
+ return insertedRows;
2366
+ }
2367
+
2368
+ /**
2369
+ * Removes multiple rows at once
2370
+ * @param startIndex - Starting position (0-based)
2371
+ * @param count - Number of rows to remove
2372
+ * @returns True if rows were removed
2373
+ * @example
2374
+ * ```typescript
2375
+ * table.removeRows(2, 3); // Remove 3 rows starting at position 2
2376
+ * ```
2377
+ */
2378
+ removeRows(startIndex: number, count: number): boolean {
2379
+ if (startIndex < 0 || startIndex >= this.rows.length || count <= 0) {
2380
+ return false;
2381
+ }
2382
+
2383
+ const actualCount = Math.min(count, this.rows.length - startIndex);
2384
+
2385
+ // When tracking enabled, mark each row's cells with cellDel + w:del
2386
+ if (this.trackingContext?.isEnabled()) {
2387
+ const author = this.trackingContext.getAuthor();
2388
+ for (let i = startIndex; i < startIndex + actualCount; i++) {
2389
+ const row = this.rows[i]!;
2390
+ for (const cell of row.getCells()) {
2391
+ const cellDelRevision = Revision.createTableCellDelete(author, []);
2392
+ cell.setCellRevision(cellDelRevision);
2393
+ for (const para of cell.getParagraphs()) {
2394
+ const runs = para.getRuns();
2395
+ if (runs.length > 0) {
2396
+ const deletion = Revision.createDeletion(author, runs);
2397
+ para.addRevision(deletion);
2398
+ }
2399
+ }
2400
+ }
2401
+ }
2402
+ return true;
2403
+ }
2404
+
2405
+ this.rows.splice(startIndex, actualCount);
2406
+ return actualCount > 0;
2407
+ }
2408
+
2409
+ /**
2410
+ * Sorts table rows by the content of a specified column
2411
+ *
2412
+ * Sorts all rows based on the text content of cells in the given column.
2413
+ * By default, excludes the first row (header row) from sorting.
2414
+ *
2415
+ * @param columnIndex - Column to sort by (0-based)
2416
+ * @param options - Sort options
2417
+ * @param options.ascending - Sort ascending (default: true)
2418
+ * @param options.numeric - Treat values as numbers (default: false, string sort)
2419
+ * @param options.skipHeaderRow - Skip first row from sorting (default: true)
2420
+ * @returns This table for chaining
2421
+ *
2422
+ * @example
2423
+ * ```typescript
2424
+ * // Sort by first column alphabetically
2425
+ * table.sortRows(0);
2426
+ *
2427
+ * // Sort by third column numerically, descending
2428
+ * table.sortRows(2, { numeric: true, ascending: false });
2429
+ *
2430
+ * // Sort all rows including header
2431
+ * table.sortRows(0, { skipHeaderRow: false });
2432
+ * ```
2433
+ */
2434
+ sortRows(
2435
+ columnIndex: number,
2436
+ options?: { ascending?: boolean; numeric?: boolean; skipHeaderRow?: boolean }
2437
+ ): this {
2438
+ const ascending = options?.ascending ?? true;
2439
+ const numeric = options?.numeric ?? false;
2440
+ const skipHeaderRow = options?.skipHeaderRow ?? true;
2441
+
2442
+ if (this.rows.length <= 1) {
2443
+ return this; // Nothing to sort
2444
+ }
2445
+
2446
+ // Determine which rows to sort
2447
+ const headerRow = skipHeaderRow ? this.rows.shift() : null;
2448
+ const rowsToSort = [...this.rows];
2449
+
2450
+ // Sort the rows
2451
+ rowsToSort.sort((a, b) => {
2452
+ const cellA = a.getCell(columnIndex);
2453
+ const cellB = b.getCell(columnIndex);
2454
+
2455
+ const textA = cellA?.getText().trim() || "";
2456
+ const textB = cellB?.getText().trim() || "";
2457
+
2458
+ let comparison: number;
2459
+ if (numeric) {
2460
+ const numA = parseFloat(textA) || 0;
2461
+ const numB = parseFloat(textB) || 0;
2462
+ comparison = numA - numB;
2463
+ } else {
2464
+ comparison = textA.localeCompare(textB);
2465
+ }
2466
+
2467
+ return ascending ? comparison : -comparison;
2468
+ });
2469
+
2470
+ // Reconstruct rows array
2471
+ this.rows = headerRow ? [headerRow, ...rowsToSort] : rowsToSort;
2472
+
2473
+ return this;
2474
+ }
2475
+
2476
+ /**
2477
+ * Creates a deep clone of this table
2478
+ *
2479
+ * Creates a new Table with copies of all rows, cells, content, and formatting.
2480
+ * The clone is completely independent of the original.
2481
+ *
2482
+ * @returns New Table instance with the same structure and content
2483
+ *
2484
+ * @example
2485
+ * ```typescript
2486
+ * const original = new Table(2, 3);
2487
+ * original.getCell(0, 0)?.addParagraph(new Paragraph().addText('Data'));
2488
+ *
2489
+ * const copy = original.clone();
2490
+ * copy.getCell(0, 0)?.addParagraph(new Paragraph().addText(' - Modified'));
2491
+ * // Original table unchanged
2492
+ * ```
2493
+ */
2494
+ clone(): Table {
2495
+ // Clone formatting
2496
+ const clonedFormatting: TableFormatting = deepClone(this.formatting);
2497
+
2498
+ // Create new table with same structure
2499
+ const clonedTable = new Table(0, 0, clonedFormatting);
2500
+
2501
+ // Clone all rows
2502
+ for (const row of this.rows) {
2503
+ // Clone row by creating new cells with same content
2504
+ const cells = row.getCells();
2505
+ const clonedRow = new TableRow(0);
2506
+
2507
+ for (const cell of cells) {
2508
+ const cellFormatting = cell.getFormatting();
2509
+ const clonedCell = new TableCell(deepClone(cellFormatting));
2510
+
2511
+ // Clone paragraphs in cell
2512
+ for (const para of cell.getParagraphs()) {
2513
+ clonedCell.addParagraph(para.clone());
2514
+ }
2515
+
2516
+ clonedRow.addCell(clonedCell);
2517
+ }
2518
+
2519
+ clonedTable.addRow(clonedRow);
2520
+ }
2521
+
2522
+ return clonedTable;
2523
+ }
2524
+ }