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,1586 @@
1
+ /**
2
+ * TableCell - Represents a cell in a table
3
+ */
4
+
5
+ import { formatDateForXml } from "../utils/dateFormatting";
6
+ import { XMLBuilder, XMLElement } from "../xml/XMLBuilder";
7
+ import { Paragraph, TextDirection } from "./Paragraph";
8
+ import { Revision } from "./Revision";
9
+ import {
10
+ BorderStyle as CommonBorderStyle,
11
+ BorderDefinition,
12
+ FourSidedBorders,
13
+ CellVerticalAlignment as CommonCellVerticalAlignment,
14
+ ShadingConfig,
15
+ ShadingPattern,
16
+ buildShadingAttributes,
17
+ WidthType,
18
+ } from "./CommonTypes";
19
+
20
+ // ============================================================================
21
+ // RE-EXPORTED TYPES (for backward compatibility)
22
+ // ============================================================================
23
+
24
+ /**
25
+ * Cell border style
26
+ * @see CommonTypes.BorderStyle
27
+ */
28
+ export type BorderStyle = CommonBorderStyle;
29
+
30
+ /**
31
+ * Cell border definition
32
+ * @see CommonTypes.BorderDefinition
33
+ */
34
+ export interface CellBorder {
35
+ style?: BorderStyle;
36
+ size?: number; // Size in eighths of a point
37
+ color?: string; // Hex color without #
38
+ }
39
+
40
+ /**
41
+ * Cell borders
42
+ * @see CommonTypes.FourSidedBorders
43
+ */
44
+ export interface CellBorders {
45
+ top?: CellBorder;
46
+ bottom?: CellBorder;
47
+ left?: CellBorder;
48
+ right?: CellBorder;
49
+ }
50
+
51
+ /**
52
+ * Cell shading/background
53
+ * @see ShadingConfig in CommonTypes.ts for the canonical definition
54
+ */
55
+ export type CellShading = ShadingConfig;
56
+
57
+ /**
58
+ * Vertical alignment in cell
59
+ * @see CommonTypes.CellVerticalAlignment
60
+ */
61
+ export type CellVerticalAlignment = CommonCellVerticalAlignment;
62
+
63
+ /**
64
+ * Cell margins (spacing inside cell borders)
65
+ * Per ECMA-376 Part 1 §17.4.43
66
+ */
67
+ export interface CellMargins {
68
+ top?: number; // Margin in twips
69
+ bottom?: number; // Margin in twips
70
+ left?: number; // Margin in twips
71
+ right?: number; // Margin in twips
72
+ }
73
+
74
+ /**
75
+ * Cell width type
76
+ * Per ECMA-376 Part 1 §17.18.87
77
+ */
78
+ export type CellWidthType = "auto" | "dxa" | "pct";
79
+
80
+ /**
81
+ * Vertical merge type for cells
82
+ * Per ECMA-376 Part 1 §17.4.85
83
+ */
84
+ export type VerticalMerge = "restart" | "continue";
85
+
86
+ /**
87
+ * Cell formatting options
88
+ */
89
+ export interface CellFormatting {
90
+ width?: number; // Width in twips
91
+ widthType?: CellWidthType; // Width type (auto, dxa, pct)
92
+ borders?: CellBorders;
93
+ shading?: CellShading;
94
+ verticalAlignment?: CellVerticalAlignment;
95
+ columnSpan?: number; // Number of columns to span (gridSpan)
96
+ rowSpan?: number; // Number of rows to span (gridSpan)
97
+ margins?: CellMargins; // Cell margins (spacing inside borders)
98
+ textDirection?: TextDirection; // Text flow direction
99
+ fitText?: boolean; // Fit text to cell width
100
+ noWrap?: boolean; // Prevent text wrapping
101
+ hideMark?: boolean; // Hide end-of-cell mark
102
+ cnfStyle?: string; // Conditional formatting style (14-char binary string)
103
+ vMerge?: VerticalMerge; // Vertical cell merge
104
+ hMerge?: 'restart' | 'continue'; // Legacy horizontal merge (w:hMerge) per ECMA-376 Part 1 §17.4.22
105
+ }
106
+
107
+ /**
108
+ * Raw nested content stored as XML to preserve nested tables/SDTs
109
+ * Position indicates after which paragraph index the content appears
110
+ */
111
+ export interface RawNestedContent {
112
+ position: number; // Content appears after this paragraph index
113
+ xml: string; // Raw XML content
114
+ type: "table" | "sdt"; // Type of nested content
115
+ }
116
+
117
+ /**
118
+ * Table cell property change tracking (w:tcPrChange)
119
+ * Per ECMA-376 Part 1 §17.13.5.37
120
+ */
121
+ export interface TcPrChange {
122
+ author: string;
123
+ date: string;
124
+ id: string;
125
+ previousProperties: Record<string, any>;
126
+ }
127
+
128
+ /**
129
+ * Represents a table cell
130
+ */
131
+ export class TableCell {
132
+ private paragraphs: Paragraph[] = [];
133
+ private formatting: CellFormatting;
134
+ /** Raw nested content (tables, SDTs) stored as XML for passthrough */
135
+ private rawNestedContent: RawNestedContent[] = [];
136
+ /** Parent row reference (if cell is inside a table row) */
137
+ private _parentRow?: import('./TableRow').TableRow;
138
+ /** Table cell revision (w:cellIns, w:cellDel, w:cellMerge) per ECMA-376 Part 1 §17.13.5.4-5.6 */
139
+ private cellRevision?: Revision;
140
+ /** Tracking context for automatic change tracking */
141
+ private trackingContext?: import('../tracking/TrackingContext').TrackingContext;
142
+ /** Table cell property change tracking (w:tcPrChange) */
143
+ private tcPrChange?: TcPrChange;
144
+
145
+ /**
146
+ * Creates a new TableCell
147
+ * @param formatting - Cell formatting options
148
+ */
149
+ constructor(formatting: CellFormatting = {}) {
150
+ this.formatting = formatting;
151
+ }
152
+
153
+ /**
154
+ * Sets the tracking context for automatic change tracking.
155
+ * Called by Document when track changes is enabled.
156
+ * @internal
157
+ */
158
+ _setTrackingContext(context: import('../tracking/TrackingContext').TrackingContext): void {
159
+ this.trackingContext = context;
160
+ }
161
+
162
+ /**
163
+ * Gets the table cell property change tracking info
164
+ * @returns tcPrChange or undefined
165
+ */
166
+ getTcPrChange(): TcPrChange | undefined {
167
+ return this.tcPrChange;
168
+ }
169
+
170
+ /**
171
+ * Sets the table cell property change tracking info
172
+ * @param change - Property change info or undefined to clear
173
+ */
174
+ setTcPrChange(change: TcPrChange | undefined): void {
175
+ this.tcPrChange = change;
176
+ }
177
+
178
+ /**
179
+ * Clears the table cell property change tracking
180
+ */
181
+ clearTcPrChange(): void {
182
+ this.tcPrChange = undefined;
183
+ }
184
+
185
+ /**
186
+ * Adds a paragraph to the cell
187
+ * @param paragraph - Paragraph to add
188
+ * @returns This cell for chaining
189
+ */
190
+ addParagraph(paragraph: Paragraph): this {
191
+ this.paragraphs.push(paragraph);
192
+ paragraph._setParentCell(this);
193
+ // Propagate StylesManager from table if available
194
+ const stylesManager = this._parentRow?._getParentTable()?._getStylesManager();
195
+ if (stylesManager) {
196
+ paragraph._setStylesManager(stylesManager);
197
+ }
198
+ return this;
199
+ }
200
+
201
+ /**
202
+ * Creates and adds a new paragraph with text
203
+ * @param text - Text content
204
+ * @returns The created paragraph
205
+ */
206
+ createParagraph(text?: string): Paragraph {
207
+ const para = new Paragraph();
208
+ if (text) {
209
+ para.addText(text);
210
+ }
211
+ this.paragraphs.push(para);
212
+ para._setParentCell(this);
213
+ // Propagate StylesManager from table if available
214
+ const stylesManager = this._parentRow?._getParentTable()?._getStylesManager();
215
+ if (stylesManager) {
216
+ para._setStylesManager(stylesManager);
217
+ }
218
+ return para;
219
+ }
220
+
221
+ /**
222
+ * Gets all paragraphs in the cell
223
+ * @returns Array of paragraphs
224
+ */
225
+ getParagraphs(): Paragraph[] {
226
+ return [...this.paragraphs];
227
+ }
228
+
229
+ /**
230
+ * Removes a paragraph at the specified index
231
+ * @param index - Index of paragraph to remove
232
+ * @returns True if removed, false if index out of bounds
233
+ */
234
+ removeParagraph(index: number): boolean {
235
+ if (index < 0 || index >= this.paragraphs.length) {
236
+ return false;
237
+ }
238
+
239
+ // When tracking enabled, wrap content in w:del instead of removing
240
+ if (this.trackingContext?.isEnabled()) {
241
+ const paragraph = this.paragraphs[index]!;
242
+ const runs = paragraph.getRuns();
243
+ if (runs.length > 0) {
244
+ const author = this.trackingContext.getAuthor();
245
+ const deletion = Revision.createDeletion(author, runs);
246
+ this.trackingContext.getRevisionManager().register(deletion);
247
+ paragraph.addRevision(deletion);
248
+ }
249
+ return true;
250
+ }
251
+
252
+ const removed = this.paragraphs.splice(index, 1);
253
+ const removedPara = removed[0];
254
+ if (removedPara) {
255
+ removedPara._setParentCell(undefined);
256
+ }
257
+
258
+ // Update raw nested content positions
259
+ // Any nested content positioned AFTER the removed paragraph needs its position decremented
260
+ // This maintains correct relative positioning for nested tables, SDTs, etc.
261
+ for (const item of this.rawNestedContent) {
262
+ if (item.position > index) {
263
+ item.position--;
264
+ }
265
+ }
266
+
267
+ return true;
268
+ }
269
+
270
+ /**
271
+ * Adds a paragraph at the specified index
272
+ * @param index - Index to insert at
273
+ * @param paragraph - Paragraph to add
274
+ * @returns This cell for chaining
275
+ */
276
+ addParagraphAt(index: number, paragraph: Paragraph): this {
277
+ if (index < 0) {
278
+ index = 0;
279
+ }
280
+
281
+ // Determine actual insertion index
282
+ const actualIndex = index >= this.paragraphs.length ? this.paragraphs.length : index;
283
+
284
+ if (index >= this.paragraphs.length) {
285
+ this.paragraphs.push(paragraph);
286
+ } else {
287
+ this.paragraphs.splice(index, 0, paragraph);
288
+ }
289
+
290
+ // Update raw nested content positions
291
+ // Any nested content positioned AT OR AFTER the insertion point needs its position incremented
292
+ // This maintains correct relative positioning for nested tables, SDTs, etc.
293
+ for (const item of this.rawNestedContent) {
294
+ if (item.position >= actualIndex) {
295
+ item.position++;
296
+ }
297
+ }
298
+
299
+ paragraph._setParentCell(this);
300
+ // Propagate StylesManager from table if available
301
+ const stylesManager = this._parentRow?._getParentTable()?._getStylesManager();
302
+ if (stylesManager) {
303
+ paragraph._setStylesManager(stylesManager);
304
+ }
305
+
306
+ // When tracking enabled, wrap paragraph content in w:ins revision
307
+ if (this.trackingContext?.isEnabled()) {
308
+ const runs = paragraph.getRuns();
309
+ if (runs.length > 0) {
310
+ const author = this.trackingContext.getAuthor();
311
+ const insertion = Revision.createInsertion(author, runs);
312
+ this.trackingContext.getRevisionManager().register(insertion);
313
+ paragraph.addRevision(insertion);
314
+ }
315
+ }
316
+
317
+ return this;
318
+ }
319
+
320
+ /**
321
+ * Gets the text content of all paragraphs
322
+ * @returns Combined text
323
+ */
324
+ getText(): string {
325
+ return this.paragraphs.map((p) => p.getText()).join("\n");
326
+ }
327
+
328
+ /**
329
+ * Gets all fields from paragraphs in this cell
330
+ *
331
+ * Collects all Field and ComplexField instances from every paragraph
332
+ * in the table cell.
333
+ *
334
+ * @returns Array of fields (Field and ComplexField instances)
335
+ *
336
+ * @example
337
+ * ```typescript
338
+ * const cell = table.getCell(0, 0);
339
+ * const fields = cell?.getFields() || [];
340
+ * console.log(`Cell has ${fields.length} fields`);
341
+ * ```
342
+ */
343
+ getFields(): (import("./Field").Field | import("./Field").ComplexField)[] {
344
+ const fields: (import("./Field").Field | import("./Field").ComplexField)[] = [];
345
+ for (const para of this.paragraphs) {
346
+ fields.push(...para.getFields());
347
+ }
348
+ return fields;
349
+ }
350
+
351
+ /**
352
+ * Finds fields matching a predicate function
353
+ *
354
+ * Searches through all fields in the cell and returns those matching
355
+ * the specified criteria.
356
+ *
357
+ * @param predicate - Function to test each field
358
+ * @returns Array of matching fields
359
+ *
360
+ * @example
361
+ * ```typescript
362
+ * // Find all PAGE fields
363
+ * const pageFields = cell.findFields(f =>
364
+ * f.getInstruction().startsWith('PAGE')
365
+ * );
366
+ *
367
+ * // Find fields with specific switches
368
+ * const mergeFields = cell.findFields(f =>
369
+ * f.getInstruction().includes('MERGEFIELD')
370
+ * );
371
+ * ```
372
+ */
373
+ findFields(
374
+ predicate: (
375
+ field: import("./Field").Field | import("./Field").ComplexField
376
+ ) => boolean
377
+ ): (import("./Field").Field | import("./Field").ComplexField)[] {
378
+ return this.getFields().filter(predicate);
379
+ }
380
+
381
+ /**
382
+ * Removes all fields from paragraphs in this cell
383
+ *
384
+ * Iterates through each paragraph and removes all fields,
385
+ * preserving text content.
386
+ *
387
+ * @returns Count of fields removed
388
+ *
389
+ * @example
390
+ * ```typescript
391
+ * const count = cell.removeAllFields();
392
+ * console.log(`Removed ${count} fields from cell`);
393
+ * ```
394
+ */
395
+ removeAllFields(): number {
396
+ let count = 0;
397
+ for (const para of this.paragraphs) {
398
+ count += para.removeAllFields();
399
+ }
400
+ return count;
401
+ }
402
+
403
+ /**
404
+ * Sets cell width
405
+ * @param twips - Width in twips
406
+ * @returns This cell for chaining
407
+ */
408
+ setWidth(twips: number): this {
409
+ const prev = this.formatting.width;
410
+ this.formatting.width = twips;
411
+ if (this.trackingContext?.isEnabled() && prev !== twips) {
412
+ this.trackingContext.trackTableChange(this, 'width', prev, twips);
413
+ }
414
+ return this;
415
+ }
416
+
417
+ /**
418
+ * Sets cell borders
419
+ * @param borders - Border definitions
420
+ * @returns This cell for chaining
421
+ */
422
+ setBorders(borders: CellBorders): this {
423
+ const prev = this.formatting.borders;
424
+ this.formatting.borders = borders;
425
+ if (this.trackingContext?.isEnabled() && prev !== borders) {
426
+ this.trackingContext.trackTableChange(this, 'borders', prev, borders);
427
+ }
428
+ return this;
429
+ }
430
+
431
+ /**
432
+ * Sets cell shading/background
433
+ * @param shading - Shading definition
434
+ * @returns This cell for chaining
435
+ */
436
+ setShading(shading: CellShading): this {
437
+ const prev = this.formatting.shading;
438
+ this.formatting.shading = shading;
439
+ if (this.trackingContext?.isEnabled() && prev !== shading) {
440
+ this.trackingContext.trackTableChange(this, 'shading', prev, shading);
441
+ }
442
+ return this;
443
+ }
444
+
445
+ /**
446
+ * Sets vertical alignment
447
+ * @param alignment - Vertical alignment
448
+ * @returns This cell for chaining
449
+ */
450
+ setVerticalAlignment(alignment: CellVerticalAlignment): this {
451
+ const prev = this.formatting.verticalAlignment;
452
+ this.formatting.verticalAlignment = alignment;
453
+ if (this.trackingContext?.isEnabled() && prev !== alignment) {
454
+ this.trackingContext.trackTableChange(this, 'verticalAlignment', prev, alignment);
455
+ }
456
+ return this;
457
+ }
458
+
459
+ /**
460
+ * Sets column span (merge cells horizontally)
461
+ * @param span - Number of columns to span
462
+ * @returns This cell for chaining
463
+ */
464
+ setColumnSpan(span: number): this {
465
+ const prev = this.formatting.columnSpan;
466
+ this.formatting.columnSpan = span;
467
+ if (this.trackingContext?.isEnabled() && prev !== span) {
468
+ this.trackingContext.trackTableChange(this, 'columnSpan', prev, span);
469
+ }
470
+ return this;
471
+ }
472
+
473
+ /**
474
+ * Gets the number of columns this cell spans (gridSpan)
475
+ * @returns Column span, defaults to 1 if not set
476
+ */
477
+ getColumnSpan(): number {
478
+ return this.formatting.columnSpan || 1;
479
+ }
480
+
481
+ /**
482
+ * Gets whether to fit text to cell width
483
+ * @returns true if fit text is enabled, false otherwise
484
+ */
485
+ getFitText(): boolean {
486
+ return this.formatting.fitText ?? false;
487
+ }
488
+
489
+ /**
490
+ * Gets whether text wrapping is prevented in cell
491
+ * @returns true if no-wrap is enabled, false otherwise
492
+ */
493
+ getNoWrap(): boolean {
494
+ return this.formatting.noWrap ?? false;
495
+ }
496
+
497
+ /**
498
+ * Gets whether the end-of-cell mark is hidden
499
+ * @returns true if hidden, false otherwise
500
+ */
501
+ getHideMark(): boolean {
502
+ return this.formatting.hideMark ?? false;
503
+ }
504
+
505
+ /**
506
+ * Gets the conditional formatting style (cnfStyle) for this cell
507
+ * @returns 14-character binary string or undefined if not set
508
+ */
509
+ getCnfStyle(): string | undefined {
510
+ return this.formatting.cnfStyle;
511
+ }
512
+
513
+ /**
514
+ * Validates a margin value
515
+ * @param value - Margin value in twips
516
+ * @param side - Name of the margin side (for error messages)
517
+ * @throws {Error} If margin is negative or exceeds maximum
518
+ * @private
519
+ */
520
+ private validateMargin(value: number | undefined, side: string): void {
521
+ if (value === undefined) return;
522
+
523
+ // Margins must be non-negative
524
+ if (value < 0) {
525
+ throw new Error(
526
+ `Invalid ${side} margin: ${value} twips. Cell margins cannot be negative.`
527
+ );
528
+ }
529
+
530
+ // Maximum reasonable margin (1 inch = 1440 twips)
531
+ // Word typically allows up to several inches, but we set a reasonable limit
532
+ const MAX_MARGIN_TWIPS = 14400; // 10 inches
533
+ if (value > MAX_MARGIN_TWIPS) {
534
+ throw new Error(
535
+ `Invalid ${side} margin: ${value} twips exceeds maximum of ${MAX_MARGIN_TWIPS} twips (10 inches).`
536
+ );
537
+ }
538
+ }
539
+
540
+ /**
541
+ * Sets cell margins (spacing inside cell borders)
542
+ * Per ECMA-376 Part 1 §17.4.43
543
+ * @param margins - Margin definitions for each side
544
+ * @returns This cell for chaining
545
+ * @throws {Error} If any margin value is negative or exceeds maximum
546
+ */
547
+ setMargins(margins: CellMargins): this {
548
+ // Validate each margin
549
+ this.validateMargin(margins.top, "top");
550
+ this.validateMargin(margins.bottom, "bottom");
551
+ this.validateMargin(margins.left, "left");
552
+ this.validateMargin(margins.right, "right");
553
+
554
+ const prev = this.formatting.margins;
555
+ this.formatting.margins = margins;
556
+ if (this.trackingContext?.isEnabled() && prev !== margins) {
557
+ this.trackingContext.trackTableChange(this, 'margins', prev, margins);
558
+ }
559
+ return this;
560
+ }
561
+
562
+ /**
563
+ * Sets all cell margins to the same value
564
+ * @param margin - Margin in twips to apply to all sides
565
+ * @returns This cell for chaining
566
+ * @throws {Error} If margin value is negative or exceeds maximum
567
+ */
568
+ setAllMargins(margin: number): this {
569
+ this.validateMargin(margin, "all");
570
+ return this.setMargins({ top: margin, bottom: margin, left: margin, right: margin });
571
+ }
572
+
573
+ /**
574
+ * Sets text direction for cell content
575
+ * Per ECMA-376 Part 1 §17.4.72
576
+ * @param direction - Text flow direction
577
+ * - 'lrTb': Left-to-right, top-to-bottom (default)
578
+ * - 'tbRl': Top-to-bottom, right-to-left (vertical text, East Asian)
579
+ * - 'btLr': Bottom-to-top, left-to-right (vertical text)
580
+ * - 'lrTbV': Left-to-right, top-to-bottom, vertical
581
+ * - 'tbRlV': Top-to-bottom, right-to-left, vertical
582
+ * - 'tbLrV': Top-to-bottom, left-to-right, vertical
583
+ * @returns This cell for chaining
584
+ */
585
+ setTextDirection(direction: TextDirection): this {
586
+ const prev = this.formatting.textDirection;
587
+ this.formatting.textDirection = direction;
588
+ if (this.trackingContext?.isEnabled() && prev !== direction) {
589
+ this.trackingContext.trackTableChange(this, 'textDirection', prev, direction);
590
+ }
591
+ return this;
592
+ }
593
+
594
+ /**
595
+ * Sets whether to fit text to cell width
596
+ * Per ECMA-376 Part 1 §17.4.68
597
+ * @param fit - Whether to expand/compress text to fit cell width
598
+ * @returns This cell for chaining
599
+ */
600
+ setFitText(fit = true): this {
601
+ const prev = this.formatting.fitText;
602
+ this.formatting.fitText = fit;
603
+ if (this.trackingContext?.isEnabled() && prev !== fit) {
604
+ this.trackingContext.trackTableChange(this, 'fitText', prev, fit);
605
+ }
606
+ return this;
607
+ }
608
+
609
+ /**
610
+ * Sets whether to prevent text wrapping in cell
611
+ * Per ECMA-376 Part 1 §17.4.34
612
+ * @param noWrap - Whether to prevent wrapping (default: true)
613
+ * @returns This cell for chaining
614
+ */
615
+ setNoWrap(noWrap = true): this {
616
+ const prev = this.formatting.noWrap;
617
+ this.formatting.noWrap = noWrap;
618
+ if (this.trackingContext?.isEnabled() && prev !== noWrap) {
619
+ this.trackingContext.trackTableChange(this, 'noWrap', prev, noWrap);
620
+ }
621
+ return this;
622
+ }
623
+
624
+ /**
625
+ * Sets whether to hide the end-of-cell mark
626
+ * Per ECMA-376 Part 1 §17.4.24
627
+ * @param hide - Whether to ignore cell end mark in height calculations (default: true)
628
+ * @returns This cell for chaining
629
+ */
630
+ setHideMark(hide = true): this {
631
+ const prev = this.formatting.hideMark;
632
+ this.formatting.hideMark = hide;
633
+ if (this.trackingContext?.isEnabled() && prev !== hide) {
634
+ this.trackingContext.trackTableChange(this, 'hideMark', prev, hide);
635
+ }
636
+ return this;
637
+ }
638
+
639
+ /**
640
+ * Sets conditional formatting style for this cell
641
+ * Per ECMA-376 Part 1 §17.4.7
642
+ * @param cnfStyle - 14-character binary string representing which conditional formats to apply
643
+ * Each bit position controls a different conditional format (e.g., "100000000000" for first row)
644
+ * @returns This cell for chaining
645
+ */
646
+ setConditionalStyle(cnfStyle: string): this {
647
+ const prev = this.formatting.cnfStyle;
648
+ this.formatting.cnfStyle = cnfStyle;
649
+ if (this.trackingContext?.isEnabled() && prev !== cnfStyle) {
650
+ this.trackingContext.trackTableChange(this, 'cnfStyle', prev, cnfStyle);
651
+ }
652
+ return this;
653
+ }
654
+
655
+ /**
656
+ * Sets cell width with type specification
657
+ * Per ECMA-376 Part 1 §17.4.81
658
+ * @param width - Width value
659
+ * @param type - Width type: 'auto' (automatic), 'dxa' (twips), or 'pct' (percentage * 50)
660
+ * @returns This cell for chaining
661
+ */
662
+ setWidthType(width: number, type: CellWidthType = "dxa"): this {
663
+ const prevWidth = this.formatting.width;
664
+ const prevType = this.formatting.widthType;
665
+ this.formatting.width = width;
666
+ this.formatting.widthType = type;
667
+ if (this.trackingContext?.isEnabled()) {
668
+ if (prevWidth !== width) {
669
+ this.trackingContext.trackTableChange(this, 'width', prevWidth, width);
670
+ }
671
+ if (prevType !== type) {
672
+ this.trackingContext.trackTableChange(this, 'widthType', prevType, type);
673
+ }
674
+ }
675
+ return this;
676
+ }
677
+
678
+ /**
679
+ * Sets vertical merge for this cell
680
+ * Per ECMA-376 Part 1 §17.4.85
681
+ * @param merge - Vertical merge type:
682
+ * - 'restart': Start a new vertically merged region (top cell)
683
+ * - 'continue': Continue the current vertically merged region (cells below)
684
+ * @returns This cell for chaining
685
+ */
686
+ setVerticalMerge(merge: VerticalMerge | undefined): this {
687
+ const prev = this.formatting.vMerge;
688
+ this.formatting.vMerge = merge;
689
+ if (this.trackingContext?.isEnabled() && prev !== merge) {
690
+ this.trackingContext.trackTableChange(this, 'vMerge', prev, merge);
691
+ }
692
+ return this;
693
+ }
694
+
695
+ /**
696
+ * Sets the legacy horizontal merge state per ECMA-376 Part 1 §17.4.22
697
+ * @param merge - 'restart' to start a new merge region, 'continue' to continue, or undefined to clear
698
+ * @returns This cell for chaining
699
+ */
700
+ setHorizontalMerge(merge: 'restart' | 'continue' | undefined): this {
701
+ const prev = this.formatting.hMerge;
702
+ this.formatting.hMerge = merge;
703
+ if (this.trackingContext?.isEnabled() && prev !== merge) {
704
+ this.trackingContext.trackTableChange(this, 'hMerge', prev, merge);
705
+ }
706
+ return this;
707
+ }
708
+
709
+ // ============================================================================
710
+ // CELL REVISIONS (w:cellIns, w:cellDel, w:cellMerge)
711
+ // ============================================================================
712
+
713
+ /**
714
+ * Sets the revision marker for this cell
715
+ * Per ECMA-376 Part 1 §17.13.5.4-5.6
716
+ *
717
+ * Table cell revisions track structural changes to table cells:
718
+ * - tableCellInsert (w:cellIns): Cell was inserted
719
+ * - tableCellDelete (w:cellDel): Cell was deleted
720
+ * - tableCellMerge (w:cellMerge): Cell merge/split operation
721
+ *
722
+ * @param revision - Revision marker for this cell
723
+ * @returns This cell for chaining
724
+ *
725
+ * @example
726
+ * ```typescript
727
+ * const revision = new Revision({
728
+ * id: 1,
729
+ * author: 'Alice',
730
+ * date: new Date(),
731
+ * type: 'tableCellInsert',
732
+ * content: [],
733
+ * });
734
+ * cell.setCellRevision(revision);
735
+ * ```
736
+ */
737
+ setCellRevision(revision: Revision): this {
738
+ this.cellRevision = revision;
739
+ return this;
740
+ }
741
+
742
+ /**
743
+ * Gets the revision marker for this cell
744
+ *
745
+ * @returns The cell revision if present, undefined otherwise
746
+ *
747
+ * @example
748
+ * ```typescript
749
+ * const revision = cell.getCellRevision();
750
+ * if (revision) {
751
+ * console.log(`Cell ${revision.getType()} by ${revision.getAuthor()}`);
752
+ * }
753
+ * ```
754
+ */
755
+ getCellRevision(): Revision | undefined {
756
+ return this.cellRevision;
757
+ }
758
+
759
+ /**
760
+ * Checks if this cell has a revision marker
761
+ *
762
+ * @returns True if cell has a revision (insert, delete, or merge)
763
+ */
764
+ hasCellRevision(): boolean {
765
+ return this.cellRevision !== undefined;
766
+ }
767
+
768
+ /**
769
+ * Clears the revision marker for this cell
770
+ *
771
+ * @returns This cell for chaining
772
+ */
773
+ clearCellRevision(): this {
774
+ this.cellRevision = undefined;
775
+ return this;
776
+ }
777
+
778
+ // ============================================================================
779
+ // CONVENIENCE METHODS (for easier paragraph manipulation)
780
+ // ============================================================================
781
+
782
+ /**
783
+ * Sets text alignment for all paragraphs in the cell
784
+ *
785
+ * Applies the specified horizontal alignment to every paragraph
786
+ * in this cell.
787
+ *
788
+ * @param alignment - Paragraph alignment (left, center, right, both)
789
+ * @returns This cell for chaining
790
+ *
791
+ * @example
792
+ * ```typescript
793
+ * cell.setTextAlignment('center');
794
+ * ```
795
+ */
796
+ setTextAlignment(alignment: import('./Paragraph').ParagraphAlignment): this {
797
+ for (const para of this.paragraphs) {
798
+ para.setAlignment(alignment);
799
+ }
800
+ return this;
801
+ }
802
+
803
+ /**
804
+ * Sets the style for all paragraphs in the cell
805
+ *
806
+ * Applies the specified style ID to every paragraph in this cell.
807
+ *
808
+ * @param styleId - Style ID to apply
809
+ * @returns This cell for chaining
810
+ *
811
+ * @example
812
+ * ```typescript
813
+ * cell.setAllParagraphsStyle('TableContent');
814
+ * ```
815
+ */
816
+ setAllParagraphsStyle(styleId: string): this {
817
+ for (const para of this.paragraphs) {
818
+ para.setStyle(styleId);
819
+ }
820
+ return this;
821
+ }
822
+
823
+ /**
824
+ * Sets font for all runs in the cell
825
+ *
826
+ * Applies the specified font to every text run in every paragraph
827
+ * in this cell.
828
+ *
829
+ * @param fontName - Font name to apply
830
+ * @returns Number of runs modified
831
+ *
832
+ * @example
833
+ * ```typescript
834
+ * const count = cell.setAllRunsFont('Arial');
835
+ * ```
836
+ */
837
+ setAllRunsFont(fontName: string): number {
838
+ let count = 0;
839
+ for (const para of this.paragraphs) {
840
+ for (const run of para.getRuns()) {
841
+ run.setFont(fontName);
842
+ count++;
843
+ }
844
+ }
845
+ return count;
846
+ }
847
+
848
+ /**
849
+ * Sets font size for all runs in the cell
850
+ *
851
+ * Applies the specified font size to every text run in every paragraph
852
+ * in this cell.
853
+ *
854
+ * @param size - Font size in half-points (e.g., 24 = 12pt)
855
+ * @returns Number of runs modified
856
+ *
857
+ * @example
858
+ * ```typescript
859
+ * const count = cell.setAllRunsSize(22); // 11pt
860
+ * ```
861
+ */
862
+ setAllRunsSize(size: number): number {
863
+ let count = 0;
864
+ for (const para of this.paragraphs) {
865
+ for (const run of para.getRuns()) {
866
+ run.setSize(size);
867
+ count++;
868
+ }
869
+ }
870
+ return count;
871
+ }
872
+
873
+ /**
874
+ * Sets color for all runs in the cell
875
+ *
876
+ * Applies the specified color to every text run in every paragraph
877
+ * in this cell.
878
+ *
879
+ * @param color - Hex color code (e.g., 'FF0000', '#0000FF')
880
+ * @returns Number of runs modified
881
+ *
882
+ * @example
883
+ * ```typescript
884
+ * const count = cell.setAllRunsColor('000000'); // Black
885
+ * ```
886
+ */
887
+ setAllRunsColor(color: string): number {
888
+ let count = 0;
889
+ for (const para of this.paragraphs) {
890
+ for (const run of para.getRuns()) {
891
+ run.setColor(color);
892
+ count++;
893
+ }
894
+ }
895
+ return count;
896
+ }
897
+
898
+ /**
899
+ * Gets the cell formatting
900
+ * @returns Cell formatting
901
+ */
902
+ getFormatting(): CellFormatting {
903
+ return { ...this.formatting };
904
+ }
905
+
906
+ // ============================================================================
907
+ // Individual Formatting Getters
908
+ // ============================================================================
909
+
910
+ /**
911
+ * Gets the cell width in twips
912
+ * @returns Width in twips or undefined if not set
913
+ */
914
+ getWidth(): number | undefined {
915
+ return this.formatting.width;
916
+ }
917
+
918
+ /**
919
+ * Gets the cell width type
920
+ * @returns Width type ('auto', 'dxa', 'pct', 'nil') or undefined
921
+ */
922
+ getWidthType(): string | undefined {
923
+ return this.formatting.widthType;
924
+ }
925
+
926
+ /**
927
+ * Gets the vertical alignment of cell content
928
+ * @returns Vertical alignment ('top', 'center', 'bottom') or undefined
929
+ */
930
+ getVerticalAlignment(): string | undefined {
931
+ return this.formatting.verticalAlignment;
932
+ }
933
+
934
+ /**
935
+ * Gets the vertical merge state
936
+ * @returns Vertical merge state ('restart', 'continue') or undefined
937
+ */
938
+ getVerticalMerge(): VerticalMerge | undefined {
939
+ return this.formatting.vMerge;
940
+ }
941
+
942
+ /**
943
+ * Gets the legacy horizontal merge state
944
+ * @returns Horizontal merge state ('restart', 'continue') or undefined
945
+ */
946
+ getHorizontalMerge(): 'restart' | 'continue' | undefined {
947
+ return this.formatting.hMerge;
948
+ }
949
+
950
+ /**
951
+ * Gets the cell margins
952
+ * @returns Margins object with top, right, bottom, left or undefined
953
+ */
954
+ getMargins(): CellMargins | undefined {
955
+ return this.formatting.margins;
956
+ }
957
+
958
+ /**
959
+ * Gets the cell borders
960
+ * @returns Borders object or undefined
961
+ */
962
+ getBorders(): CellBorders | undefined {
963
+ return this.formatting.borders;
964
+ }
965
+
966
+ /**
967
+ * Gets the cell shading/background
968
+ * @returns Shading object or undefined
969
+ */
970
+ getShading(): CellShading | undefined {
971
+ return this.formatting.shading;
972
+ }
973
+
974
+ /**
975
+ * Gets the text direction for the cell
976
+ * @returns Text direction or undefined
977
+ */
978
+ getTextDirection(): string | undefined {
979
+ return this.formatting.textDirection;
980
+ }
981
+
982
+ // ============================================================================
983
+ // RAW NESTED CONTENT (Tables, SDTs preserved as XML)
984
+ // ============================================================================
985
+
986
+ /**
987
+ * Adds raw nested content (table or SDT) to the cell
988
+ * Used during parsing to preserve nested tables that cannot be fully modeled
989
+ * @param position - Paragraph index after which this content appears (0 = before first paragraph)
990
+ * @param xml - Raw XML content
991
+ * @param type - Type of content ('table' or 'sdt')
992
+ * @returns This cell for chaining
993
+ */
994
+ addRawNestedContent(
995
+ position: number,
996
+ xml: string,
997
+ type: "table" | "sdt" = "table"
998
+ ): this {
999
+ this.rawNestedContent.push({ position, xml, type });
1000
+ return this;
1001
+ }
1002
+
1003
+ /**
1004
+ * Gets all raw nested content in this cell
1005
+ * @returns Array of raw nested content items
1006
+ */
1007
+ getRawNestedContent(): RawNestedContent[] {
1008
+ return [...this.rawNestedContent];
1009
+ }
1010
+
1011
+ /**
1012
+ * Checks if this cell has any nested tables
1013
+ * @returns True if cell contains nested tables stored as raw XML
1014
+ */
1015
+ hasNestedTables(): boolean {
1016
+ return this.rawNestedContent.some((c) => c.type === "table");
1017
+ }
1018
+
1019
+ /**
1020
+ * Checks if this cell has any raw nested content (tables or SDTs)
1021
+ * @returns True if cell contains any raw nested content
1022
+ */
1023
+ hasRawNestedContent(): boolean {
1024
+ return this.rawNestedContent.length > 0;
1025
+ }
1026
+
1027
+ /**
1028
+ * Clears all raw nested content from this cell
1029
+ * @returns This cell for chaining
1030
+ */
1031
+ clearRawNestedContent(): this {
1032
+ this.rawNestedContent = [];
1033
+ return this;
1034
+ }
1035
+
1036
+ /**
1037
+ * Updates raw nested content at a specific index
1038
+ * Used for revision acceptance in nested tables
1039
+ * @param index - Index in the rawNestedContent array
1040
+ * @param xml - New XML content
1041
+ * @returns True if updated, false if index out of bounds
1042
+ */
1043
+ updateRawNestedContent(index: number, xml: string): boolean {
1044
+ if (index < 0 || index >= this.rawNestedContent.length) {
1045
+ return false;
1046
+ }
1047
+ const item = this.rawNestedContent[index];
1048
+ if (item) {
1049
+ item.xml = xml;
1050
+ return true;
1051
+ }
1052
+ return false;
1053
+ }
1054
+
1055
+ // ============================================================================
1056
+ // TRAILING BLANK PARAGRAPH REMOVAL
1057
+ // ============================================================================
1058
+
1059
+ /**
1060
+ * Removes trailing blank paragraphs from this cell.
1061
+ * A trailing blank is a blank paragraph at the end of the cell, after all content.
1062
+ * This respects ECMA-376 requirement of at least one paragraph per cell.
1063
+ *
1064
+ * @param options.ignorePreserveFlag - If true, removes trailing blanks even if marked preserved (default: false)
1065
+ * @returns Number of paragraphs removed
1066
+ *
1067
+ * @example
1068
+ * ```typescript
1069
+ * // Remove trailing blanks, respecting preserve flags
1070
+ * const removed = cell.removeTrailingBlankParagraphs();
1071
+ *
1072
+ * // Remove all trailing blanks, ignoring preserve flags
1073
+ * const removed = cell.removeTrailingBlankParagraphs({ ignorePreserveFlag: true });
1074
+ * ```
1075
+ */
1076
+ removeTrailingBlankParagraphs(options?: { ignorePreserveFlag?: boolean }): number {
1077
+ let removed = 0;
1078
+ const ignorePreserve = options?.ignorePreserveFlag ?? false;
1079
+
1080
+ // Work backwards from end of paragraphs array
1081
+ while (this.paragraphs.length > 1) {
1082
+ const lastIndex = this.paragraphs.length - 1;
1083
+ const lastPara = this.paragraphs[lastIndex];
1084
+
1085
+ if (!lastPara) break;
1086
+
1087
+ // Check if this is a blank paragraph
1088
+ const isBlank = this.isParaBlank(lastPara);
1089
+
1090
+ // Stop if not blank
1091
+ if (!isBlank) break;
1092
+
1093
+ // Stop if preserved and we're respecting preserve flags
1094
+ if (!ignorePreserve && lastPara.isPreserved()) break;
1095
+
1096
+ // Check if there's raw nested content positioned after this paragraph
1097
+ // If so, we should NOT remove this trailing blank as it maintains structure
1098
+ const hasContentAfter = this.rawNestedContent.some(
1099
+ (item) => item.position >= lastIndex
1100
+ );
1101
+ if (hasContentAfter) break;
1102
+
1103
+ this.removeParagraph(lastIndex);
1104
+ removed++;
1105
+ }
1106
+
1107
+ return removed;
1108
+ }
1109
+
1110
+ /**
1111
+ * Checks if a paragraph is blank (no meaningful content).
1112
+ * A paragraph is considered blank if it has no text, images, shapes, hyperlinks, fields,
1113
+ * cnfStyle (conditional formatting), or other structural elements.
1114
+ *
1115
+ * IMPORTANT: cnfStyle preservation is critical! When a paragraph with cnfStyle is removed,
1116
+ * Word may apply default table style conditional formatting to the cell, causing unexpected
1117
+ * shading changes. A paragraph with cnfStyle="000000000000" (no conditionals) keeps the cell
1118
+ * from matching table style conditionals like firstRow or band1Horz.
1119
+ *
1120
+ * @private
1121
+ */
1122
+ private isParaBlank(para: Paragraph): boolean {
1123
+ // Check for text content
1124
+ const text = para.getText().trim();
1125
+ if (text !== "") return false;
1126
+
1127
+ // Check for cnfStyle (conditional formatting) - critical for shading preservation
1128
+ // Even a "blank" cnfStyle like "000000000000" is meaningful as it prevents
1129
+ // the cell from inheriting table style conditional formatting
1130
+ const cnfStyle = para.getTableConditionalStyle();
1131
+ if (cnfStyle && cnfStyle !== "") {
1132
+ return false;
1133
+ }
1134
+
1135
+ // Check all content items for non-text elements
1136
+ const content = para.getContent();
1137
+ for (const item of content) {
1138
+ // Cast to unknown first for safe duck-typing checks
1139
+ const itemAny = item as unknown as Record<string, unknown>;
1140
+
1141
+ // ImageRun check - ImageRun has getImageElement method
1142
+ if (item && typeof itemAny.getImageElement === "function") {
1143
+ return false;
1144
+ }
1145
+
1146
+ // Shape check - Shape has getShapeType method
1147
+ if (item && typeof itemAny.getShapeType === "function") {
1148
+ return false;
1149
+ }
1150
+
1151
+ // TextBox check - TextBox has getTextContent method
1152
+ if (item && typeof itemAny.getTextContent === "function") {
1153
+ return false;
1154
+ }
1155
+
1156
+ // Hyperlink check - Hyperlink has getUrl method
1157
+ if (item && typeof itemAny.getUrl === "function") {
1158
+ return false;
1159
+ }
1160
+
1161
+ // Field check - Field has getInstruction method
1162
+ if (item && typeof itemAny.getInstruction === "function") {
1163
+ return false;
1164
+ }
1165
+
1166
+ // Revision check - check if revision contains meaningful content
1167
+ if (item && typeof itemAny.getText === "function") {
1168
+ const itemText = (itemAny.getText as () => string)().trim();
1169
+ if (itemText !== "") return false;
1170
+
1171
+ // Also check if revision contains non-text elements (hyperlinks, images, shapes, textboxes)
1172
+ if (typeof itemAny.getContent === "function") {
1173
+ const revContent = (itemAny.getContent as () => unknown[])();
1174
+ for (const revItem of revContent) {
1175
+ const revItemAny = revItem as Record<string, unknown>;
1176
+ // Check if revision content is a Hyperlink using duck typing (getUrl method)
1177
+ if (revItem && typeof revItemAny.getUrl === "function") {
1178
+ return false; // Revision contains hyperlink - not blank
1179
+ }
1180
+ // Check if revision content is an ImageRun (getImageElement method)
1181
+ if (revItem && typeof revItemAny.getImageElement === "function") {
1182
+ return false; // Revision contains image - not blank
1183
+ }
1184
+ // Check if revision content is a Shape (getShapeType method)
1185
+ if (revItem && typeof revItemAny.getShapeType === "function") {
1186
+ return false; // Revision contains shape - not blank
1187
+ }
1188
+ // Check if revision content is a TextBox (getTextContent method)
1189
+ if (revItem && typeof revItemAny.getTextContent === "function") {
1190
+ return false; // Revision contains textbox - not blank
1191
+ }
1192
+ }
1193
+ }
1194
+ }
1195
+ }
1196
+
1197
+ // Check for bookmarks (they count as content)
1198
+ if (
1199
+ para.getBookmarksStart().length > 0 ||
1200
+ para.getBookmarksEnd().length > 0
1201
+ ) {
1202
+ return false;
1203
+ }
1204
+
1205
+ // Check for comments (start/end markers)
1206
+ if (typeof para.getCommentsStart === "function") {
1207
+ const commentsStart = para.getCommentsStart();
1208
+ if (commentsStart && commentsStart.length > 0) {
1209
+ return false;
1210
+ }
1211
+ }
1212
+ if (typeof para.getCommentsEnd === "function") {
1213
+ const commentsEnd = para.getCommentsEnd();
1214
+ if (commentsEnd && commentsEnd.length > 0) {
1215
+ return false;
1216
+ }
1217
+ }
1218
+
1219
+ return true;
1220
+ }
1221
+
1222
+ /**
1223
+ * Sets the parent row reference for this cell.
1224
+ * Called by TableRow when adding cells.
1225
+ * @internal
1226
+ */
1227
+ _setParentRow(row: import('./TableRow').TableRow | undefined): void {
1228
+ this._parentRow = row;
1229
+ }
1230
+
1231
+ /**
1232
+ * Gets the parent row reference for this cell.
1233
+ * @internal
1234
+ */
1235
+ _getParentRow(): import('./TableRow').TableRow | undefined {
1236
+ return this._parentRow;
1237
+ }
1238
+
1239
+ /**
1240
+ * Gets the table style ID by traversing up the parent chain.
1241
+ * @returns Table style ID or undefined if not in a table or no style set
1242
+ */
1243
+ getTableStyleId(): string | undefined {
1244
+ const row = this._parentRow;
1245
+ if (!row) return undefined;
1246
+
1247
+ const table = row._getParentTable();
1248
+ if (!table) return undefined;
1249
+
1250
+ return table.getFormatting().style;
1251
+ }
1252
+
1253
+ /**
1254
+ * Converts the cell to WordprocessingML XML element
1255
+ * @returns XMLElement representing the cell
1256
+ */
1257
+ toXML(): XMLElement {
1258
+ const tcPrChildren: XMLElement[] = [];
1259
+
1260
+ // tcPr children ordered per ECMA-376 CT_TcPr:
1261
+ // cnfStyle → tcW → gridSpan → hMerge → vMerge → tcBorders → shd → noWrap → tcMar →
1262
+ // textDirection → tcFitText → vAlign → hideMark →
1263
+ // cellIns/cellDel/cellMerge → tcPrChange
1264
+
1265
+ // cnfStyle - conditional formatting style (MUST be first child per CT_TcPr)
1266
+ if (this.formatting.cnfStyle) {
1267
+ tcPrChildren.push(
1268
+ XMLBuilder.wSelf("cnfStyle", { "w:val": this.formatting.cnfStyle })
1269
+ );
1270
+ }
1271
+
1272
+ // tcW - cell width
1273
+ if (this.formatting.width !== undefined) {
1274
+ const widthAttrs: Record<string, string | number> = {
1275
+ "w:w": this.formatting.width,
1276
+ "w:type": this.formatting.widthType || "dxa",
1277
+ };
1278
+ tcPrChildren.push(XMLBuilder.wSelf("tcW", widthAttrs));
1279
+ }
1280
+
1281
+ // gridSpan - column span
1282
+ if (this.formatting.columnSpan && this.formatting.columnSpan > 1) {
1283
+ tcPrChildren.push(
1284
+ XMLBuilder.wSelf("gridSpan", { "w:val": this.formatting.columnSpan })
1285
+ );
1286
+ }
1287
+
1288
+ // hMerge - legacy horizontal merge
1289
+ if (this.formatting.hMerge) {
1290
+ tcPrChildren.push(XMLBuilder.wSelf("hMerge", { "w:val": this.formatting.hMerge }));
1291
+ }
1292
+
1293
+ // vMerge - vertical merge
1294
+ // Per OOXML, <w:vMerge/> without val means "continue" (default).
1295
+ // Only "restart" needs an explicit w:val attribute.
1296
+ if (this.formatting.vMerge) {
1297
+ if (this.formatting.vMerge === "restart") {
1298
+ tcPrChildren.push(XMLBuilder.wSelf("vMerge", { "w:val": "restart" }));
1299
+ } else {
1300
+ tcPrChildren.push(XMLBuilder.wSelf("vMerge"));
1301
+ }
1302
+ }
1303
+
1304
+ // tcBorders - cell borders
1305
+ if (this.formatting.borders) {
1306
+ const borderElements: XMLElement[] = [];
1307
+ const borders = this.formatting.borders;
1308
+
1309
+ // Ordered per ECMA-376 CT_TcBorders: top, left, bottom, right
1310
+ if (borders.top) {
1311
+ borderElements.push(XMLBuilder.createBorder("top", borders.top));
1312
+ }
1313
+ if (borders.left) {
1314
+ borderElements.push(XMLBuilder.createBorder("left", borders.left));
1315
+ }
1316
+ if (borders.bottom) {
1317
+ borderElements.push(XMLBuilder.createBorder("bottom", borders.bottom));
1318
+ }
1319
+ if (borders.right) {
1320
+ borderElements.push(XMLBuilder.createBorder("right", borders.right));
1321
+ }
1322
+
1323
+ if (borderElements.length > 0) {
1324
+ tcPrChildren.push(XMLBuilder.w("tcBorders", undefined, borderElements));
1325
+ }
1326
+ }
1327
+
1328
+ // shd - shading
1329
+ if (this.formatting.shading) {
1330
+ const shadingAttrs = buildShadingAttributes(this.formatting.shading);
1331
+ if (Object.keys(shadingAttrs).length > 0) {
1332
+ tcPrChildren.push(XMLBuilder.wSelf("shd", shadingAttrs));
1333
+ }
1334
+ }
1335
+
1336
+ // noWrap
1337
+ if (this.formatting.noWrap) {
1338
+ tcPrChildren.push(XMLBuilder.wSelf("noWrap"));
1339
+ }
1340
+
1341
+ // tcMar - cell margins (ordered per CT_TcMar: top, left, bottom, right)
1342
+ if (this.formatting.margins) {
1343
+ const margins = this.formatting.margins;
1344
+ const marginChildren: XMLElement[] = [];
1345
+
1346
+ if (margins.top !== undefined) {
1347
+ marginChildren.push(
1348
+ XMLBuilder.wSelf("top", {
1349
+ "w:w": margins.top.toString(),
1350
+ "w:type": "dxa",
1351
+ })
1352
+ );
1353
+ }
1354
+ if (margins.left !== undefined) {
1355
+ marginChildren.push(
1356
+ XMLBuilder.wSelf("left", {
1357
+ "w:w": margins.left.toString(),
1358
+ "w:type": "dxa",
1359
+ })
1360
+ );
1361
+ }
1362
+ if (margins.bottom !== undefined) {
1363
+ marginChildren.push(
1364
+ XMLBuilder.wSelf("bottom", {
1365
+ "w:w": margins.bottom.toString(),
1366
+ "w:type": "dxa",
1367
+ })
1368
+ );
1369
+ }
1370
+ if (margins.right !== undefined) {
1371
+ marginChildren.push(
1372
+ XMLBuilder.wSelf("right", {
1373
+ "w:w": margins.right.toString(),
1374
+ "w:type": "dxa",
1375
+ })
1376
+ );
1377
+ }
1378
+
1379
+ if (marginChildren.length > 0) {
1380
+ tcPrChildren.push(XMLBuilder.w("tcMar", undefined, marginChildren));
1381
+ }
1382
+ }
1383
+
1384
+ // textDirection
1385
+ if (this.formatting.textDirection) {
1386
+ tcPrChildren.push(
1387
+ XMLBuilder.wSelf("textDirection", {
1388
+ "w:val": this.formatting.textDirection,
1389
+ })
1390
+ );
1391
+ }
1392
+
1393
+ // tcFitText
1394
+ if (this.formatting.fitText) {
1395
+ tcPrChildren.push(XMLBuilder.wSelf("tcFitText"));
1396
+ }
1397
+
1398
+ // vAlign - vertical alignment
1399
+ if (this.formatting.verticalAlignment) {
1400
+ tcPrChildren.push(
1401
+ XMLBuilder.wSelf("vAlign", {
1402
+ "w:val": this.formatting.verticalAlignment,
1403
+ })
1404
+ );
1405
+ }
1406
+
1407
+ // hideMark
1408
+ if (this.formatting.hideMark) {
1409
+ tcPrChildren.push(XMLBuilder.wSelf("hideMark"));
1410
+ }
1411
+
1412
+ // Add cell revision markers (w:cellIns, w:cellDel, w:cellMerge) per ECMA-376 Part 1 §17.13.5.4-5.6
1413
+ if (this.cellRevision) {
1414
+ const revType = this.cellRevision.getType();
1415
+ const attrs: Record<string, string | number> = {
1416
+ "w:id": this.cellRevision.getId(),
1417
+ "w:author": this.cellRevision.getAuthor(),
1418
+ "w:date": formatDateForXml(this.cellRevision.getDate()),
1419
+ };
1420
+
1421
+ if (revType === "tableCellInsert") {
1422
+ tcPrChildren.push(XMLBuilder.wSelf("cellIns", attrs));
1423
+ } else if (revType === "tableCellDelete") {
1424
+ tcPrChildren.push(XMLBuilder.wSelf("cellDel", attrs));
1425
+ } else if (revType === "tableCellMerge") {
1426
+ // Add vMerge and vMergeOrig attributes if present
1427
+ // ST_AnnotationVMerge: "cont" | "rest" (not "continue" / "restart")
1428
+ const mergeMap: Record<string, string> = { continue: "cont", restart: "rest" };
1429
+ const prevProps = this.cellRevision.getPreviousProperties();
1430
+ if (prevProps?.vMerge) {
1431
+ attrs["w:vMerge"] = mergeMap[prevProps.vMerge] || prevProps.vMerge;
1432
+ }
1433
+ if (prevProps?.vMergeOrig) {
1434
+ attrs["w:vMergeOrig"] = mergeMap[prevProps.vMergeOrig] || prevProps.vMergeOrig;
1435
+ }
1436
+ tcPrChildren.push(XMLBuilder.wSelf("cellMerge", attrs));
1437
+ }
1438
+ }
1439
+
1440
+ // Add table cell property change (w:tcPrChange) per ECMA-376 Part 1 §17.13.5.37
1441
+ // Must be last child of w:tcPr
1442
+ if (this.tcPrChange) {
1443
+ const changeAttrs: Record<string, string | number> = {
1444
+ "w:id": this.tcPrChange.id,
1445
+ "w:author": this.tcPrChange.author,
1446
+ "w:date": this.tcPrChange.date,
1447
+ };
1448
+ const prevTcPrChildren: XMLElement[] = [];
1449
+ const prev = this.tcPrChange.previousProperties;
1450
+ if (prev) {
1451
+ // Ordered per CT_TcPr: tcW → tcBorders → shd → noWrap → tcMar →
1452
+ // textDirection → tcFitText → vAlign → hideMark → cnfStyle
1453
+ if (prev.width !== undefined) {
1454
+ prevTcPrChildren.push(XMLBuilder.wSelf("tcW", {
1455
+ "w:w": prev.width,
1456
+ "w:type": prev.widthType || "dxa",
1457
+ }));
1458
+ }
1459
+ if (prev.borders) {
1460
+ const borderElements: XMLElement[] = [];
1461
+ if (prev.borders.top) borderElements.push(XMLBuilder.createBorder("top", prev.borders.top));
1462
+ if (prev.borders.left) borderElements.push(XMLBuilder.createBorder("left", prev.borders.left));
1463
+ if (prev.borders.bottom) borderElements.push(XMLBuilder.createBorder("bottom", prev.borders.bottom));
1464
+ if (prev.borders.right) borderElements.push(XMLBuilder.createBorder("right", prev.borders.right));
1465
+ if (borderElements.length > 0) {
1466
+ prevTcPrChildren.push(XMLBuilder.w("tcBorders", undefined, borderElements));
1467
+ }
1468
+ }
1469
+ if (prev.shading) {
1470
+ const shadingAttrs = buildShadingAttributes(prev.shading);
1471
+ if (Object.keys(shadingAttrs).length > 0) {
1472
+ prevTcPrChildren.push(XMLBuilder.wSelf("shd", shadingAttrs));
1473
+ }
1474
+ }
1475
+ if (prev.noWrap) {
1476
+ prevTcPrChildren.push(XMLBuilder.wSelf("noWrap"));
1477
+ }
1478
+ if (prev.margins) {
1479
+ const marginChildren: XMLElement[] = [];
1480
+ if (prev.margins.top !== undefined) {
1481
+ marginChildren.push(XMLBuilder.wSelf("top", { "w:w": prev.margins.top.toString(), "w:type": "dxa" }));
1482
+ }
1483
+ if (prev.margins.left !== undefined) {
1484
+ marginChildren.push(XMLBuilder.wSelf("left", { "w:w": prev.margins.left.toString(), "w:type": "dxa" }));
1485
+ }
1486
+ if (prev.margins.bottom !== undefined) {
1487
+ marginChildren.push(XMLBuilder.wSelf("bottom", { "w:w": prev.margins.bottom.toString(), "w:type": "dxa" }));
1488
+ }
1489
+ if (prev.margins.right !== undefined) {
1490
+ marginChildren.push(XMLBuilder.wSelf("right", { "w:w": prev.margins.right.toString(), "w:type": "dxa" }));
1491
+ }
1492
+ if (marginChildren.length > 0) {
1493
+ prevTcPrChildren.push(XMLBuilder.w("tcMar", undefined, marginChildren));
1494
+ }
1495
+ }
1496
+ if (prev.textDirection) {
1497
+ prevTcPrChildren.push(XMLBuilder.wSelf("textDirection", { "w:val": prev.textDirection }));
1498
+ }
1499
+ if (prev.fitText) {
1500
+ prevTcPrChildren.push(XMLBuilder.wSelf("tcFitText"));
1501
+ }
1502
+ if (prev.verticalAlignment) {
1503
+ prevTcPrChildren.push(XMLBuilder.wSelf("vAlign", { "w:val": prev.verticalAlignment }));
1504
+ }
1505
+ if (prev.hideMark) {
1506
+ prevTcPrChildren.push(XMLBuilder.wSelf("hideMark"));
1507
+ }
1508
+ if (prev.cnfStyle) {
1509
+ prevTcPrChildren.push(XMLBuilder.wSelf("cnfStyle", { "w:val": prev.cnfStyle }));
1510
+ }
1511
+ }
1512
+ const prevTcPr = prevTcPrChildren.length > 0
1513
+ ? XMLBuilder.w("tcPr", undefined, prevTcPrChildren)
1514
+ : XMLBuilder.w("tcPr", undefined, []);
1515
+ tcPrChildren.push(XMLBuilder.w("tcPrChange", changeAttrs, [prevTcPr]));
1516
+ }
1517
+
1518
+ // Build cell element
1519
+ const cellChildren: XMLElement[] = [];
1520
+
1521
+ // Add cell properties if there are any
1522
+ if (tcPrChildren.length > 0) {
1523
+ cellChildren.push(XMLBuilder.w("tcPr", undefined, tcPrChildren));
1524
+ }
1525
+
1526
+ // Add paragraphs and raw nested content in correct order
1527
+ // Raw nested content (tables, SDTs) are interspersed with paragraphs
1528
+ if (this.paragraphs.length > 0 || this.rawNestedContent.length > 0) {
1529
+ // Sort raw content by position
1530
+ const sortedRaw = [...this.rawNestedContent].sort(
1531
+ (a, b) => a.position - b.position
1532
+ );
1533
+ let rawIndex = 0;
1534
+
1535
+ for (let i = 0; i < this.paragraphs.length; i++) {
1536
+ // Insert any raw content that comes before this paragraph (position <= i)
1537
+ let rawItem = sortedRaw[rawIndex];
1538
+ while (rawIndex < sortedRaw.length && rawItem && rawItem.position <= i) {
1539
+ // Use __rawXml element for passthrough (supported by XMLBuilder)
1540
+ cellChildren.push({
1541
+ name: "__rawXml",
1542
+ rawXml: rawItem.xml,
1543
+ });
1544
+ rawIndex++;
1545
+ rawItem = sortedRaw[rawIndex];
1546
+ }
1547
+ const para = this.paragraphs[i];
1548
+ if (para) {
1549
+ cellChildren.push(para.toXML());
1550
+ }
1551
+ }
1552
+
1553
+ // Insert any remaining raw content after all paragraphs
1554
+ while (rawIndex < sortedRaw.length) {
1555
+ const rawItem = sortedRaw[rawIndex];
1556
+ if (rawItem) {
1557
+ cellChildren.push({
1558
+ name: "__rawXml",
1559
+ rawXml: rawItem.xml,
1560
+ });
1561
+ }
1562
+ rawIndex++;
1563
+ }
1564
+
1565
+ // If we only have raw content and no paragraphs, we need at least one empty paragraph
1566
+ // per ECMA-376 (table cell must contain at least one block-level element)
1567
+ if (this.paragraphs.length === 0) {
1568
+ cellChildren.push(new Paragraph().toXML());
1569
+ }
1570
+ } else {
1571
+ // Empty cell needs at least one empty paragraph
1572
+ cellChildren.push(new Paragraph().toXML());
1573
+ }
1574
+
1575
+ return XMLBuilder.w("tc", undefined, cellChildren);
1576
+ }
1577
+
1578
+ /**
1579
+ * Creates a new TableCell
1580
+ * @param formatting - Cell formatting
1581
+ * @returns New TableCell instance
1582
+ */
1583
+ static create(formatting?: CellFormatting): TableCell {
1584
+ return new TableCell(formatting);
1585
+ }
1586
+ }