docxmlater 10.1.4 → 10.1.6

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 (372) hide show
  1. package/README.md +759 -754
  2. package/dist/constants/legacyCompatFlags.js +1 -1
  3. package/dist/constants/legacyCompatFlags.js.map +1 -1
  4. package/dist/constants/limits.js.map +1 -1
  5. package/dist/core/Document.d.ts +51 -50
  6. package/dist/core/Document.d.ts.map +1 -1
  7. package/dist/core/Document.js +486 -471
  8. package/dist/core/Document.js.map +1 -1
  9. package/dist/core/DocumentContent.d.ts +9 -9
  10. package/dist/core/DocumentContent.d.ts.map +1 -1
  11. package/dist/core/DocumentContent.js +1 -1
  12. package/dist/core/DocumentContent.js.map +1 -1
  13. package/dist/core/DocumentGenerator.d.ts +11 -11
  14. package/dist/core/DocumentGenerator.d.ts.map +1 -1
  15. package/dist/core/DocumentGenerator.js +251 -251
  16. package/dist/core/DocumentGenerator.js.map +1 -1
  17. package/dist/core/DocumentIdManager.js.map +1 -1
  18. package/dist/core/DocumentParser.d.ts +15 -15
  19. package/dist/core/DocumentParser.d.ts.map +1 -1
  20. package/dist/core/DocumentParser.js +2123 -2155
  21. package/dist/core/DocumentParser.js.map +1 -1
  22. package/dist/core/DocumentValidator.d.ts.map +1 -1
  23. package/dist/core/DocumentValidator.js +2 -5
  24. package/dist/core/DocumentValidator.js.map +1 -1
  25. package/dist/core/Relationship.js.map +1 -1
  26. package/dist/core/RelationshipManager.d.ts.map +1 -1
  27. package/dist/core/RelationshipManager.js +3 -3
  28. package/dist/core/RelationshipManager.js.map +1 -1
  29. package/dist/elements/AlternateContent.js.map +1 -1
  30. package/dist/elements/Bookmark.d.ts.map +1 -1
  31. package/dist/elements/Bookmark.js +3 -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.d.ts.map +1 -1
  36. package/dist/elements/Comment.js +9 -6
  37. package/dist/elements/Comment.js.map +1 -1
  38. package/dist/elements/CommentManager.d.ts.map +1 -1
  39. package/dist/elements/CommentManager.js +18 -17
  40. package/dist/elements/CommentManager.js.map +1 -1
  41. package/dist/elements/CommonTypes.d.ts +21 -21
  42. package/dist/elements/CommonTypes.d.ts.map +1 -1
  43. package/dist/elements/CommonTypes.js +56 -56
  44. package/dist/elements/CommonTypes.js.map +1 -1
  45. package/dist/elements/CustomXml.js.map +1 -1
  46. package/dist/elements/Endnote.d.ts.map +1 -1
  47. package/dist/elements/Endnote.js +6 -6
  48. package/dist/elements/Endnote.js.map +1 -1
  49. package/dist/elements/EndnoteManager.d.ts.map +1 -1
  50. package/dist/elements/EndnoteManager.js +6 -7
  51. package/dist/elements/EndnoteManager.js.map +1 -1
  52. package/dist/elements/Field.d.ts.map +1 -1
  53. package/dist/elements/Field.js +82 -25
  54. package/dist/elements/Field.js.map +1 -1
  55. package/dist/elements/FieldHelpers.d.ts.map +1 -1
  56. package/dist/elements/FieldHelpers.js.map +1 -1
  57. package/dist/elements/FontManager.d.ts.map +1 -1
  58. package/dist/elements/FontManager.js +1 -1
  59. package/dist/elements/FontManager.js.map +1 -1
  60. package/dist/elements/Footer.js +2 -2
  61. package/dist/elements/Footer.js.map +1 -1
  62. package/dist/elements/Footnote.d.ts.map +1 -1
  63. package/dist/elements/Footnote.js +6 -6
  64. package/dist/elements/Footnote.js.map +1 -1
  65. package/dist/elements/FootnoteManager.d.ts.map +1 -1
  66. package/dist/elements/FootnoteManager.js +6 -7
  67. package/dist/elements/FootnoteManager.js.map +1 -1
  68. package/dist/elements/Header.js +2 -2
  69. package/dist/elements/Header.js.map +1 -1
  70. package/dist/elements/HeaderFooterManager.js.map +1 -1
  71. package/dist/elements/Hyperlink.d.ts +5 -3
  72. package/dist/elements/Hyperlink.d.ts.map +1 -1
  73. package/dist/elements/Hyperlink.js +134 -76
  74. package/dist/elements/Hyperlink.js.map +1 -1
  75. package/dist/elements/Image.d.ts.map +1 -1
  76. package/dist/elements/Image.js +238 -106
  77. package/dist/elements/Image.js.map +1 -1
  78. package/dist/elements/ImageManager.d.ts.map +1 -1
  79. package/dist/elements/ImageManager.js +1 -1
  80. package/dist/elements/ImageManager.js.map +1 -1
  81. package/dist/elements/ImageRun.js +1 -1
  82. package/dist/elements/ImageRun.js.map +1 -1
  83. package/dist/elements/MathElement.js.map +1 -1
  84. package/dist/elements/Paragraph.d.ts +24 -24
  85. package/dist/elements/Paragraph.d.ts.map +1 -1
  86. package/dist/elements/Paragraph.js +181 -188
  87. package/dist/elements/Paragraph.js.map +1 -1
  88. package/dist/elements/PreservedElement.js.map +1 -1
  89. package/dist/elements/PropertyChangeTypes.d.ts.map +1 -1
  90. package/dist/elements/PropertyChangeTypes.js +6 -6
  91. package/dist/elements/PropertyChangeTypes.js.map +1 -1
  92. package/dist/elements/RangeMarker.d.ts.map +1 -1
  93. package/dist/elements/RangeMarker.js.map +1 -1
  94. package/dist/elements/Revision.d.ts.map +1 -1
  95. package/dist/elements/Revision.js +4 -5
  96. package/dist/elements/Revision.js.map +1 -1
  97. package/dist/elements/RevisionContent.js.map +1 -1
  98. package/dist/elements/RevisionManager.d.ts.map +1 -1
  99. package/dist/elements/RevisionManager.js +40 -48
  100. package/dist/elements/RevisionManager.js.map +1 -1
  101. package/dist/elements/Run.d.ts +16 -16
  102. package/dist/elements/Run.d.ts.map +1 -1
  103. package/dist/elements/Run.js +256 -238
  104. package/dist/elements/Run.js.map +1 -1
  105. package/dist/elements/Section.d.ts.map +1 -1
  106. package/dist/elements/Section.js +36 -11
  107. package/dist/elements/Section.js.map +1 -1
  108. package/dist/elements/Shape.d.ts.map +1 -1
  109. package/dist/elements/Shape.js.map +1 -1
  110. package/dist/elements/StructuredDocumentTag.d.ts +6 -6
  111. package/dist/elements/StructuredDocumentTag.d.ts.map +1 -1
  112. package/dist/elements/StructuredDocumentTag.js +99 -104
  113. package/dist/elements/StructuredDocumentTag.js.map +1 -1
  114. package/dist/elements/Table.d.ts +11 -11
  115. package/dist/elements/Table.d.ts.map +1 -1
  116. package/dist/elements/Table.js +102 -107
  117. package/dist/elements/Table.js.map +1 -1
  118. package/dist/elements/TableCell.d.ts +10 -10
  119. package/dist/elements/TableCell.d.ts.map +1 -1
  120. package/dist/elements/TableCell.js +105 -106
  121. package/dist/elements/TableCell.js.map +1 -1
  122. package/dist/elements/TableGridChange.d.ts.map +1 -1
  123. package/dist/elements/TableGridChange.js.map +1 -1
  124. package/dist/elements/TableOfContents.d.ts.map +1 -1
  125. package/dist/elements/TableOfContents.js +4 -4
  126. package/dist/elements/TableOfContents.js.map +1 -1
  127. package/dist/elements/TableOfContentsElement.js.map +1 -1
  128. package/dist/elements/TableRow.d.ts.map +1 -1
  129. package/dist/elements/TableRow.js +13 -6
  130. package/dist/elements/TableRow.js.map +1 -1
  131. package/dist/elements/TextBox.d.ts.map +1 -1
  132. package/dist/elements/TextBox.js +3 -5
  133. package/dist/elements/TextBox.js.map +1 -1
  134. package/dist/formatting/AbstractNumbering.d.ts +4 -4
  135. package/dist/formatting/AbstractNumbering.d.ts.map +1 -1
  136. package/dist/formatting/AbstractNumbering.js +54 -49
  137. package/dist/formatting/AbstractNumbering.js.map +1 -1
  138. package/dist/formatting/NumberingInstance.d.ts.map +1 -1
  139. package/dist/formatting/NumberingInstance.js +1 -3
  140. package/dist/formatting/NumberingInstance.js.map +1 -1
  141. package/dist/formatting/NumberingLevel.d.ts +5 -5
  142. package/dist/formatting/NumberingLevel.d.ts.map +1 -1
  143. package/dist/formatting/NumberingLevel.js +119 -125
  144. package/dist/formatting/NumberingLevel.js.map +1 -1
  145. package/dist/formatting/NumberingManager.d.ts +1 -0
  146. package/dist/formatting/NumberingManager.d.ts.map +1 -1
  147. package/dist/formatting/NumberingManager.js +27 -9
  148. package/dist/formatting/NumberingManager.js.map +1 -1
  149. package/dist/formatting/Style.d.ts +11 -11
  150. package/dist/formatting/Style.d.ts.map +1 -1
  151. package/dist/formatting/Style.js +219 -247
  152. package/dist/formatting/Style.js.map +1 -1
  153. package/dist/formatting/StylesManager.d.ts +2 -2
  154. package/dist/formatting/StylesManager.d.ts.map +1 -1
  155. package/dist/formatting/StylesManager.js +96 -102
  156. package/dist/formatting/StylesManager.js.map +1 -1
  157. package/dist/helpers/CleanupHelper.d.ts +1 -1
  158. package/dist/helpers/CleanupHelper.d.ts.map +1 -1
  159. package/dist/helpers/CleanupHelper.js +6 -6
  160. package/dist/helpers/CleanupHelper.js.map +1 -1
  161. package/dist/images/ImageOptimizer.js +7 -7
  162. package/dist/images/ImageOptimizer.js.map +1 -1
  163. package/dist/index.d.ts +9 -9
  164. package/dist/index.d.ts.map +1 -1
  165. package/dist/index.js.map +1 -1
  166. package/dist/managers/DrawingManager.js.map +1 -1
  167. package/dist/tracking/DocumentTrackingContext.d.ts.map +1 -1
  168. package/dist/tracking/DocumentTrackingContext.js +23 -7
  169. package/dist/tracking/DocumentTrackingContext.js.map +1 -1
  170. package/dist/tracking/TrackingContext.d.ts.map +1 -1
  171. package/dist/tracking/TrackingContext.js.map +1 -1
  172. package/dist/types/compatibility-types.js.map +1 -1
  173. package/dist/types/formatting.js.map +1 -1
  174. package/dist/types/list-types.d.ts +6 -6
  175. package/dist/types/list-types.js.map +1 -1
  176. package/dist/types/settings-types.js.map +1 -1
  177. package/dist/types/styleConfig.d.ts +2 -2
  178. package/dist/types/styleConfig.js.map +1 -1
  179. package/dist/utils/ChangelogGenerator.d.ts.map +1 -1
  180. package/dist/utils/ChangelogGenerator.js +97 -101
  181. package/dist/utils/ChangelogGenerator.js.map +1 -1
  182. package/dist/utils/CompatibilityUpgrader.d.ts.map +1 -1
  183. package/dist/utils/CompatibilityUpgrader.js +1 -1
  184. package/dist/utils/CompatibilityUpgrader.js.map +1 -1
  185. package/dist/utils/InMemoryRevisionAcceptor.d.ts.map +1 -1
  186. package/dist/utils/InMemoryRevisionAcceptor.js +1 -6
  187. package/dist/utils/InMemoryRevisionAcceptor.js.map +1 -1
  188. package/dist/utils/MoveOperationHelper.d.ts.map +1 -1
  189. package/dist/utils/MoveOperationHelper.js +1 -1
  190. package/dist/utils/MoveOperationHelper.js.map +1 -1
  191. package/dist/utils/RevisionAwareProcessor.d.ts.map +1 -1
  192. package/dist/utils/RevisionAwareProcessor.js +2 -4
  193. package/dist/utils/RevisionAwareProcessor.js.map +1 -1
  194. package/dist/utils/RevisionWalker.d.ts.map +1 -1
  195. package/dist/utils/RevisionWalker.js +4 -12
  196. package/dist/utils/RevisionWalker.js.map +1 -1
  197. package/dist/utils/SelectiveRevisionAcceptor.d.ts.map +1 -1
  198. package/dist/utils/SelectiveRevisionAcceptor.js +2 -6
  199. package/dist/utils/SelectiveRevisionAcceptor.js.map +1 -1
  200. package/dist/utils/ShadingResolver.d.ts.map +1 -1
  201. package/dist/utils/ShadingResolver.js +1 -1
  202. package/dist/utils/ShadingResolver.js.map +1 -1
  203. package/dist/utils/acceptRevisions.d.ts.map +1 -1
  204. package/dist/utils/acceptRevisions.js +23 -12
  205. package/dist/utils/acceptRevisions.js.map +1 -1
  206. package/dist/utils/cnfStyleDecoder.d.ts +1 -1
  207. package/dist/utils/cnfStyleDecoder.d.ts.map +1 -1
  208. package/dist/utils/cnfStyleDecoder.js +40 -40
  209. package/dist/utils/cnfStyleDecoder.js.map +1 -1
  210. package/dist/utils/corruptionDetection.d.ts.map +1 -1
  211. package/dist/utils/corruptionDetection.js.map +1 -1
  212. package/dist/utils/dateFormatting.js.map +1 -1
  213. package/dist/utils/deepClone.js +1 -1
  214. package/dist/utils/deepClone.js.map +1 -1
  215. package/dist/utils/diagnostics.d.ts.map +1 -1
  216. package/dist/utils/diagnostics.js +1 -1
  217. package/dist/utils/diagnostics.js.map +1 -1
  218. package/dist/utils/errorHandling.js.map +1 -1
  219. package/dist/utils/formatting.d.ts.map +1 -1
  220. package/dist/utils/formatting.js +10 -2
  221. package/dist/utils/formatting.js.map +1 -1
  222. package/dist/utils/list-detection.d.ts +2 -2
  223. package/dist/utils/list-detection.d.ts.map +1 -1
  224. package/dist/utils/list-detection.js +21 -23
  225. package/dist/utils/list-detection.js.map +1 -1
  226. package/dist/utils/logger.d.ts.map +1 -1
  227. package/dist/utils/logger.js +12 -7
  228. package/dist/utils/logger.js.map +1 -1
  229. package/dist/utils/parsingHelpers.js.map +1 -1
  230. package/dist/utils/stripTrackedChanges.d.ts.map +1 -1
  231. package/dist/utils/stripTrackedChanges.js +3 -3
  232. package/dist/utils/stripTrackedChanges.js.map +1 -1
  233. package/dist/utils/textDiff.d.ts +1 -1
  234. package/dist/utils/textDiff.js +8 -8
  235. package/dist/utils/textDiff.js.map +1 -1
  236. package/dist/utils/units.js.map +1 -1
  237. package/dist/utils/validation.d.ts.map +1 -1
  238. package/dist/utils/validation.js +24 -7
  239. package/dist/utils/validation.js.map +1 -1
  240. package/dist/utils/xmlSanitization.d.ts.map +1 -1
  241. package/dist/utils/xmlSanitization.js +3 -3
  242. package/dist/utils/xmlSanitization.js.map +1 -1
  243. package/dist/validation/RevisionAutoFixer.d.ts.map +1 -1
  244. package/dist/validation/RevisionAutoFixer.js +5 -5
  245. package/dist/validation/RevisionAutoFixer.js.map +1 -1
  246. package/dist/validation/RevisionValidator.d.ts.map +1 -1
  247. package/dist/validation/RevisionValidator.js +7 -9
  248. package/dist/validation/RevisionValidator.js.map +1 -1
  249. package/dist/validation/ValidationRules.js +3 -3
  250. package/dist/validation/ValidationRules.js.map +1 -1
  251. package/dist/validation/index.js.map +1 -1
  252. package/dist/xml/XMLBuilder.d.ts +1 -1
  253. package/dist/xml/XMLBuilder.d.ts.map +1 -1
  254. package/dist/xml/XMLBuilder.js +98 -100
  255. package/dist/xml/XMLBuilder.js.map +1 -1
  256. package/dist/xml/XMLParser.d.ts.map +1 -1
  257. package/dist/xml/XMLParser.js +61 -66
  258. package/dist/xml/XMLParser.js.map +1 -1
  259. package/dist/zip/ZipHandler.d.ts.map +1 -1
  260. package/dist/zip/ZipHandler.js.map +1 -1
  261. package/dist/zip/ZipReader.d.ts.map +1 -1
  262. package/dist/zip/ZipReader.js +1 -3
  263. package/dist/zip/ZipReader.js.map +1 -1
  264. package/dist/zip/ZipWriter.d.ts +1 -1
  265. package/dist/zip/ZipWriter.d.ts.map +1 -1
  266. package/dist/zip/ZipWriter.js +28 -36
  267. package/dist/zip/ZipWriter.js.map +1 -1
  268. package/dist/zip/types.js +1 -1
  269. package/dist/zip/types.js.map +1 -1
  270. package/package.json +92 -92
  271. package/src/__tests__/helper-methods.test.ts +512 -512
  272. package/src/constants/legacyCompatFlags.ts +138 -138
  273. package/src/constants/limits.ts +50 -50
  274. package/src/core/Document.ts +1010 -1145
  275. package/src/core/DocumentContent.ts +461 -467
  276. package/src/core/DocumentGenerator.ts +1133 -1104
  277. package/src/core/DocumentIdManager.ts +158 -158
  278. package/src/core/DocumentParser.ts +2347 -2716
  279. package/src/core/DocumentValidator.ts +363 -372
  280. package/src/core/Relationship.ts +367 -367
  281. package/src/core/RelationshipManager.ts +429 -428
  282. package/src/elements/AlternateContent.ts +42 -42
  283. package/src/elements/Bookmark.ts +212 -210
  284. package/src/elements/BookmarkManager.ts +247 -250
  285. package/src/elements/Comment.ts +356 -359
  286. package/src/elements/CommentManager.ts +499 -502
  287. package/src/elements/CommonTypes.ts +524 -549
  288. package/src/elements/CustomXml.ts +36 -36
  289. package/src/elements/Endnote.ts +221 -217
  290. package/src/elements/EndnoteManager.ts +246 -249
  291. package/src/elements/Field.ts +1292 -1233
  292. package/src/elements/FieldHelpers.ts +329 -333
  293. package/src/elements/FontManager.ts +336 -339
  294. package/src/elements/Footer.ts +269 -269
  295. package/src/elements/Footnote.ts +221 -217
  296. package/src/elements/FootnoteManager.ts +246 -249
  297. package/src/elements/Header.ts +269 -269
  298. package/src/elements/HeaderFooterManager.ts +219 -219
  299. package/src/elements/Hyperlink.ts +1288 -1193
  300. package/src/elements/Image.ts +1982 -1756
  301. package/src/elements/ImageManager.ts +437 -432
  302. package/src/elements/ImageRun.ts +59 -59
  303. package/src/elements/MathElement.ts +65 -65
  304. package/src/elements/Paragraph.ts +4347 -4287
  305. package/src/elements/PreservedElement.ts +53 -53
  306. package/src/elements/PropertyChangeTypes.ts +458 -442
  307. package/src/elements/RangeMarker.ts +382 -400
  308. package/src/elements/Revision.ts +1198 -1217
  309. package/src/elements/RevisionContent.ts +73 -73
  310. package/src/elements/RevisionManager.ts +1070 -1070
  311. package/src/elements/Run.ts +3103 -3073
  312. package/src/elements/Section.ts +1521 -1421
  313. package/src/elements/Shape.ts +884 -873
  314. package/src/elements/StructuredDocumentTag.ts +1176 -1207
  315. package/src/elements/Table.ts +2468 -2524
  316. package/src/elements/TableCell.ts +1617 -1621
  317. package/src/elements/TableGridChange.ts +149 -151
  318. package/src/elements/TableOfContents.ts +701 -691
  319. package/src/elements/TableOfContentsElement.ts +89 -89
  320. package/src/elements/TableRow.ts +960 -929
  321. package/src/elements/TextBox.ts +766 -768
  322. package/src/formatting/AbstractNumbering.ts +580 -579
  323. package/src/formatting/NumberingInstance.ts +295 -299
  324. package/src/formatting/NumberingLevel.ts +981 -1040
  325. package/src/formatting/NumberingManager.ts +875 -827
  326. package/src/formatting/Style.ts +1785 -1879
  327. package/src/formatting/StylesManager.ts +1090 -1130
  328. package/src/helpers/CleanupHelper.ts +524 -524
  329. package/src/images/ImageOptimizer.ts +274 -274
  330. package/src/index.ts +559 -554
  331. package/src/managers/DrawingManager.ts +319 -319
  332. package/src/tracking/DocumentTrackingContext.ts +687 -674
  333. package/src/tracking/TrackingContext.ts +175 -173
  334. package/src/types/compatibility-types.ts +49 -49
  335. package/src/types/formatting.ts +210 -210
  336. package/src/types/list-types.ts +14 -14
  337. package/src/types/settings-types.ts +59 -59
  338. package/src/types/styleConfig.ts +189 -189
  339. package/src/utils/ChangelogGenerator.ts +1583 -1581
  340. package/src/utils/CompatibilityUpgrader.ts +235 -237
  341. package/src/utils/InMemoryRevisionAcceptor.ts +691 -696
  342. package/src/utils/MoveOperationHelper.ts +233 -238
  343. package/src/utils/RevisionAwareProcessor.ts +518 -526
  344. package/src/utils/RevisionWalker.ts +427 -457
  345. package/src/utils/SelectiveRevisionAcceptor.ts +662 -683
  346. package/src/utils/ShadingResolver.ts +105 -107
  347. package/src/utils/acceptRevisions.ts +723 -714
  348. package/src/utils/cnfStyleDecoder.ts +212 -217
  349. package/src/utils/corruptionDetection.ts +346 -345
  350. package/src/utils/dateFormatting.ts +20 -20
  351. package/src/utils/deepClone.ts +77 -78
  352. package/src/utils/diagnostics.ts +125 -129
  353. package/src/utils/errorHandling.ts +80 -80
  354. package/src/utils/formatting.ts +220 -213
  355. package/src/utils/list-detection.ts +32 -42
  356. package/src/utils/logger.ts +412 -404
  357. package/src/utils/parsingHelpers.ts +190 -190
  358. package/src/utils/stripTrackedChanges.ts +356 -353
  359. package/src/utils/textDiff.ts +100 -100
  360. package/src/utils/units.ts +421 -421
  361. package/src/utils/validation.ts +553 -542
  362. package/src/utils/xmlSanitization.ts +179 -182
  363. package/src/validation/RevisionAutoFixer.ts +541 -542
  364. package/src/validation/RevisionValidator.ts +470 -460
  365. package/src/validation/ValidationRules.ts +338 -338
  366. package/src/validation/index.ts +30 -30
  367. package/src/xml/XMLBuilder.ts +857 -871
  368. package/src/xml/XMLParser.ts +877 -919
  369. package/src/zip/ZipHandler.ts +629 -637
  370. package/src/zip/ZipReader.ts +295 -299
  371. package/src/zip/ZipWriter.ts +374 -390
  372. package/src/zip/types.ts +116 -116
@@ -1,768 +1,766 @@
1
- /**
2
- * TextBox - Represents a floating text box in a Word document
3
- *
4
- * Text boxes use DrawingML (a:) and WordprocessingML Drawing (wp:) namespaces,
5
- * plus WordprocessingShape (wps:) namespace for Word 2010+ features.
6
- *
7
- * Per ECMA-376 Part 4 (Transitional) and Office Open XML extensions,
8
- * text boxes in Word documents use the wps:wsp element with wps:txbx content.
9
- */
10
-
11
- import { XMLBuilder, XMLElement } from '../xml/XMLBuilder';
12
- import { UNITS } from '../utils/units';
13
- import { Paragraph } from './Paragraph';
14
- import {
15
- ImagePosition,
16
- ImageAnchor,
17
- PositionAnchor,
18
- HorizontalAlignment,
19
- VerticalAlignment
20
- } from './Image';
21
- import { BorderDefinition } from './Paragraph';
22
-
23
- /**
24
- * TextBox margins configuration
25
- */
26
- export interface TextBoxMargins {
27
- /** Top margin in EMUs */
28
- top: number;
29
- /** Bottom margin in EMUs */
30
- bottom: number;
31
- /** Left margin in EMUs */
32
- left: number;
33
- /** Right margin in EMUs */
34
- right: number;
35
- }
36
-
37
- /**
38
- * TextBox fill configuration
39
- */
40
- export interface TextBoxFill {
41
- /** Fill color in hex (e.g., 'F0F0F0') */
42
- color: string;
43
- /** Transparency percentage (0-100) */
44
- transparency?: number;
45
- }
46
-
47
- /**
48
- * TextBox properties
49
- */
50
- export interface TextBoxProperties {
51
- /** Width in EMUs */
52
- width: number;
53
- /** Height in EMUs */
54
- height: number;
55
- /** Paragraphs within the text box */
56
- paragraphs?: Paragraph[];
57
- /** Position configuration */
58
- position?: ImagePosition;
59
- /** Anchor configuration */
60
- anchor?: ImageAnchor;
61
- /** Fill color and transparency */
62
- fill?: TextBoxFill;
63
- /** Border configuration */
64
- borders?: BorderDefinition;
65
- /** Internal margins */
66
- margins?: TextBoxMargins;
67
- /** TextBox name */
68
- name?: string;
69
- /** TextBox description (for accessibility) */
70
- description?: string;
71
- }
72
-
73
- /**
74
- * Represents a floating text box
75
- */
76
- export class TextBox {
77
- private width: number;
78
- private height: number;
79
- private paragraphs: Paragraph[] = [];
80
- private position?: ImagePosition;
81
- private anchor?: ImageAnchor;
82
- private fill?: TextBoxFill;
83
- private borders?: BorderDefinition;
84
- private margins?: TextBoxMargins;
85
- private name: string;
86
- private description: string;
87
- private docPrId = 1;
88
-
89
- /**
90
- * Creates a new text box
91
- * @param properties TextBox properties
92
- * @private Use static factory methods instead (create)
93
- */
94
- private constructor(properties: TextBoxProperties) {
95
- this.width = properties.width;
96
- this.height = properties.height;
97
- if (properties.paragraphs) {
98
- this.paragraphs = properties.paragraphs;
99
- }
100
- this.position = properties.position;
101
- this.anchor = properties.anchor;
102
- this.fill = properties.fill;
103
- this.borders = properties.borders;
104
- this.margins = properties.margins;
105
- this.name = properties.name || 'TextBox';
106
- this.description = properties.description || '';
107
- }
108
-
109
- /**
110
- * Factory method for creating a text box
111
- * @param width Width in EMUs
112
- * @param height Height in EMUs
113
- * @returns New TextBox instance
114
- * @example
115
- * const textbox = TextBox.create(inchesToEmus(3), inchesToEmus(2));
116
- */
117
- static create(width: number, height: number): TextBox {
118
- return new TextBox({ width, height });
119
- }
120
-
121
- /**
122
- * Gets the width in EMUs
123
- * @returns Width
124
- */
125
- getWidth(): number {
126
- return this.width;
127
- }
128
-
129
- /**
130
- * Gets the height in EMUs
131
- * @returns Height
132
- */
133
- getHeight(): number {
134
- return this.height;
135
- }
136
-
137
- /**
138
- * Adds a paragraph to the text box
139
- * @param paragraph Paragraph to add
140
- * @returns This text box for chaining
141
- */
142
- addParagraph(paragraph: Paragraph): this {
143
- this.paragraphs.push(paragraph);
144
- return this;
145
- }
146
-
147
- /**
148
- * Gets all paragraphs
149
- * @returns Array of paragraphs
150
- */
151
- getParagraphs(): Paragraph[] {
152
- return this.paragraphs;
153
- }
154
-
155
- /**
156
- * Sets text box position (for floating text boxes)
157
- * @param horizontal Horizontal positioning configuration
158
- * @param vertical Vertical positioning configuration
159
- * @returns This text box for chaining
160
- */
161
- setPosition(
162
- horizontal: { anchor: PositionAnchor; offset?: number; alignment?: HorizontalAlignment },
163
- vertical: { anchor: PositionAnchor; offset?: number; alignment?: VerticalAlignment }
164
- ): this {
165
- this.position = { horizontal, vertical };
166
- return this;
167
- }
168
-
169
- /**
170
- * Gets the position configuration
171
- * @returns Position or undefined
172
- */
173
- getPosition(): ImagePosition | undefined {
174
- return this.position;
175
- }
176
-
177
- /**
178
- * Sets anchor configuration
179
- * @param options Anchor configuration
180
- * @returns This text box for chaining
181
- */
182
- setAnchor(options: ImageAnchor): this {
183
- this.anchor = options;
184
- return this;
185
- }
186
-
187
- /**
188
- * Gets the anchor configuration
189
- * @returns Anchor configuration or undefined
190
- */
191
- getAnchor(): ImageAnchor | undefined {
192
- return this.anchor;
193
- }
194
-
195
- /**
196
- * Sets the fill color
197
- * @param color Fill color in hex
198
- * @param transparency Optional transparency percentage (0-100)
199
- * @returns This text box for chaining
200
- */
201
- setFill(color: string, transparency?: number): this {
202
- this.fill = { color, transparency };
203
- return this;
204
- }
205
-
206
- /**
207
- * Gets the fill configuration
208
- * @returns Fill or undefined
209
- */
210
- getFill(): TextBoxFill | undefined {
211
- return this.fill;
212
- }
213
-
214
- /**
215
- * Sets the borders
216
- * @param borders Border configuration
217
- * @returns This text box for chaining
218
- */
219
- setBorders(borders: BorderDefinition): this {
220
- this.borders = borders;
221
- return this;
222
- }
223
-
224
- /**
225
- * Gets the borders configuration
226
- * @returns Borders or undefined
227
- */
228
- getBorders(): BorderDefinition | undefined {
229
- return this.borders;
230
- }
231
-
232
- /**
233
- * Sets internal margins
234
- * @param margins Margin configuration
235
- * @returns This text box for chaining
236
- */
237
- setMargins(margins: TextBoxMargins): this {
238
- this.margins = margins;
239
- return this;
240
- }
241
-
242
- /**
243
- * Gets the margins configuration
244
- * @returns Margins or undefined
245
- */
246
- getMargins(): TextBoxMargins | undefined {
247
- return this.margins;
248
- }
249
-
250
- /**
251
- * Sets the text box name
252
- * @param name TextBox name
253
- * @returns This text box for chaining
254
- */
255
- setName(name: string): this {
256
- this.name = name;
257
- return this;
258
- }
259
-
260
- /**
261
- * Gets the text box name
262
- * @returns TextBox name
263
- */
264
- getName(): string {
265
- return this.name;
266
- }
267
-
268
- /**
269
- * Sets the text box description (for accessibility)
270
- * @param description TextBox description
271
- * @returns This text box for chaining
272
- */
273
- setDescription(description: string): this {
274
- this.description = description;
275
- return this;
276
- }
277
-
278
- /**
279
- * Gets the text box description
280
- * @returns TextBox description
281
- */
282
- getDescription(): string {
283
- return this.description;
284
- }
285
-
286
- /**
287
- * Sets the docPr ID (drawing object ID)
288
- * @param id Document property ID
289
- * @returns This text box for chaining
290
- */
291
- setDocPrId(id: number): this {
292
- this.docPrId = id;
293
- return this;
294
- }
295
-
296
- /**
297
- * Checks if this text box is floating (has anchor or position configuration)
298
- * @returns True if floating, false if inline
299
- */
300
- isFloating(): boolean {
301
- return this.anchor !== undefined || this.position !== undefined;
302
- }
303
-
304
- /**
305
- * Generates DrawingML XML for the text box
306
- * Creates either inline or floating (anchor) text box based on configuration
307
- * @returns XML element representing the text box
308
- */
309
- toXML(): XMLElement {
310
- // Text boxes are typically floating, but support inline as well
311
- const textboxElement = this.isFloating() ? this.createAnchor() : this.createInline();
312
-
313
- // Create the drawing structure
314
- return XMLBuilder.w('drawing', undefined, [textboxElement]);
315
- }
316
-
317
- /**
318
- * Creates the wp:inline element for inline text boxes
319
- * @private
320
- */
321
- private createInline(): XMLElement {
322
- const children: XMLElement[] = [];
323
-
324
- // Extent (size)
325
- children.push({
326
- name: 'wp:extent',
327
- attributes: {
328
- cx: this.width.toString(),
329
- cy: this.height.toString(),
330
- },
331
- selfClosing: true,
332
- });
333
-
334
- // Effect extent
335
- children.push({
336
- name: 'wp:effectExtent',
337
- attributes: {
338
- l: '0',
339
- t: '0',
340
- r: '0',
341
- b: '0',
342
- },
343
- selfClosing: true,
344
- });
345
-
346
- // Document properties
347
- children.push({
348
- name: 'wp:docPr',
349
- attributes: {
350
- id: this.docPrId.toString(),
351
- name: this.name,
352
- descr: this.description,
353
- },
354
- selfClosing: true,
355
- });
356
-
357
- // Non-visual graphic frame properties
358
- children.push({
359
- name: 'wp:cNvGraphicFramePr',
360
- selfClosing: true,
361
- });
362
-
363
- // Graphic data (the text box)
364
- children.push(this.createGraphic());
365
-
366
- return {
367
- name: 'wp:inline',
368
- attributes: {
369
- distT: '0',
370
- distB: '0',
371
- distL: '0',
372
- distR: '0',
373
- },
374
- children,
375
- };
376
- }
377
-
378
- /**
379
- * Creates the wp:anchor element for floating text boxes
380
- * @private
381
- */
382
- private createAnchor(): XMLElement {
383
- const children: XMLElement[] = [];
384
-
385
- const anchorConfig = this.anchor || {
386
- behindDoc: false,
387
- locked: false,
388
- layoutInCell: true,
389
- allowOverlap: false,
390
- relativeHeight: 251658240,
391
- };
392
-
393
- // Position H (horizontal)
394
- if (this.position) {
395
- const posH = this.position.horizontal;
396
- const posHChildren: XMLElement[] = [];
397
-
398
- if (posH.offset !== undefined) {
399
- posHChildren.push({
400
- name: 'wp:posOffset',
401
- children: [posH.offset.toString()],
402
- });
403
- } else if (posH.alignment) {
404
- posHChildren.push({
405
- name: 'wp:align',
406
- children: [posH.alignment],
407
- });
408
- }
409
-
410
- children.push({
411
- name: 'wp:positionH',
412
- attributes: {
413
- relativeFrom: posH.anchor,
414
- },
415
- children: posHChildren,
416
- });
417
- }
418
-
419
- // Position V (vertical)
420
- if (this.position) {
421
- const posV = this.position.vertical;
422
- const posVChildren: XMLElement[] = [];
423
-
424
- if (posV.offset !== undefined) {
425
- posVChildren.push({
426
- name: 'wp:posOffset',
427
- children: [posV.offset.toString()],
428
- });
429
- } else if (posV.alignment) {
430
- posVChildren.push({
431
- name: 'wp:align',
432
- children: [posV.alignment],
433
- });
434
- }
435
-
436
- children.push({
437
- name: 'wp:positionV',
438
- attributes: {
439
- relativeFrom: posV.anchor,
440
- },
441
- children: posVChildren,
442
- });
443
- }
444
-
445
- // Extent (size)
446
- children.push({
447
- name: 'wp:extent',
448
- attributes: {
449
- cx: this.width.toString(),
450
- cy: this.height.toString(),
451
- },
452
- selfClosing: true,
453
- });
454
-
455
- // Effect extent
456
- children.push({
457
- name: 'wp:effectExtent',
458
- attributes: {
459
- l: '0',
460
- t: '0',
461
- r: '0',
462
- b: '0',
463
- },
464
- selfClosing: true,
465
- });
466
-
467
- // Wrap square (default for text boxes)
468
- children.push({
469
- name: 'wp:wrapSquare',
470
- attributes: {
471
- wrapText: 'bothSides',
472
- },
473
- selfClosing: true,
474
- });
475
-
476
- // Document properties
477
- children.push({
478
- name: 'wp:docPr',
479
- attributes: {
480
- id: this.docPrId.toString(),
481
- name: this.name,
482
- descr: this.description,
483
- },
484
- selfClosing: true,
485
- });
486
-
487
- // Non-visual graphic frame properties
488
- children.push({
489
- name: 'wp:cNvGraphicFramePr',
490
- selfClosing: true,
491
- });
492
-
493
- // Graphic data (the text box)
494
- children.push(this.createGraphic());
495
-
496
- return {
497
- name: 'wp:anchor',
498
- attributes: {
499
- distT: '0',
500
- distB: '0',
501
- distL: '0',
502
- distR: '0',
503
- simplePos: '0',
504
- relativeHeight: anchorConfig.relativeHeight.toString(),
505
- behindDoc: anchorConfig.behindDoc ? '1' : '0',
506
- locked: anchorConfig.locked ? '1' : '0',
507
- layoutInCell: anchorConfig.layoutInCell ? '1' : '0',
508
- allowOverlap: anchorConfig.allowOverlap ? '1' : '0',
509
- },
510
- children,
511
- };
512
- }
513
-
514
- /**
515
- * Creates the a:graphic element containing the text box
516
- * @private
517
- */
518
- private createGraphic(): XMLElement {
519
- return {
520
- name: 'a:graphic',
521
- attributes: {
522
- 'xmlns:a': 'http://schemas.openxmlformats.org/drawingml/2006/main',
523
- },
524
- children: [
525
- {
526
- name: 'a:graphicData',
527
- attributes: {
528
- uri: 'http://schemas.microsoft.com/office/word/2010/wordprocessingShape',
529
- },
530
- children: [this.createWps()],
531
- },
532
- ],
533
- };
534
- }
535
-
536
- /**
537
- * Creates the wps:wsp element (WordprocessingShape for text box)
538
- * @private
539
- */
540
- private createWps(): XMLElement {
541
- const children: XMLElement[] = [];
542
-
543
- // Non-visual shape properties
544
- children.push({
545
- name: 'wps:cNvSpPr',
546
- attributes: {
547
- 'xmlns:wps': 'http://schemas.microsoft.com/office/word/2010/wordprocessingShape',
548
- txBox: '1', // Indicates this is a text box
549
- },
550
- selfClosing: true,
551
- });
552
-
553
- // Shape properties (size, fill, borders)
554
- children.push(this.createSpPr());
555
-
556
- // Text box content
557
- children.push(this.createTextBoxContent());
558
-
559
- // Body properties (margins/insets)
560
- children.push(this.createBodyPr());
561
-
562
- return {
563
- name: 'wps:wsp',
564
- attributes: {
565
- 'xmlns:wps': 'http://schemas.microsoft.com/office/word/2010/wordprocessingShape',
566
- },
567
- children,
568
- };
569
- }
570
-
571
- /**
572
- * Creates the wps:bodyPr element (text body properties including margins)
573
- * Per ECMA-376 Part 4, bodyPr specifies inset margins via lIns/tIns/rIns/bIns attributes
574
- * @private
575
- */
576
- private createBodyPr(): XMLElement {
577
- const attrs: Record<string, string> = {
578
- rot: '0',
579
- vert: 'horz',
580
- wrap: 'square',
581
- anchor: 't',
582
- };
583
-
584
- // Add margin insets if set
585
- if (this.margins) {
586
- if (this.margins.left !== undefined) attrs.lIns = this.margins.left.toString();
587
- if (this.margins.top !== undefined) attrs.tIns = this.margins.top.toString();
588
- if (this.margins.right !== undefined) attrs.rIns = this.margins.right.toString();
589
- if (this.margins.bottom !== undefined) attrs.bIns = this.margins.bottom.toString();
590
- }
591
-
592
- return {
593
- name: 'wps:bodyPr',
594
- attributes: attrs,
595
- selfClosing: true,
596
- };
597
- }
598
-
599
- /**
600
- * Creates the wps:spPr element (shape properties for text box)
601
- * @private
602
- */
603
- private createSpPr(): XMLElement {
604
- const children: XMLElement[] = [];
605
-
606
- // Transform (position and size)
607
- children.push({
608
- name: 'a:xfrm',
609
- children: [
610
- {
611
- name: 'a:off',
612
- attributes: { x: '0', y: '0' },
613
- selfClosing: true,
614
- },
615
- {
616
- name: 'a:ext',
617
- attributes: {
618
- cx: this.width.toString(),
619
- cy: this.height.toString(),
620
- },
621
- selfClosing: true,
622
- },
623
- ],
624
- });
625
-
626
- // Preset geometry (rectangle for text box)
627
- children.push({
628
- name: 'a:prstGeom',
629
- attributes: {
630
- prst: 'rect',
631
- },
632
- children: [
633
- {
634
- name: 'a:avLst',
635
- selfClosing: true,
636
- },
637
- ],
638
- });
639
-
640
- // Fill
641
- if (this.fill) {
642
- const fillChildren: XMLElement[] = [];
643
-
644
- // Transparency
645
- if (this.fill.transparency !== undefined) {
646
- const alpha = 100 - this.fill.transparency;
647
- fillChildren.push({
648
- name: 'a:alpha',
649
- attributes: {
650
- val: Math.round(alpha * 1000).toString(),
651
- },
652
- selfClosing: true,
653
- });
654
- }
655
-
656
- children.push({
657
- name: 'a:solidFill',
658
- children: [
659
- {
660
- name: 'a:srgbClr',
661
- attributes: {
662
- val: this.fill.color.toUpperCase(),
663
- },
664
- ...(fillChildren.length > 0 ? { children: fillChildren } : { selfClosing: true }),
665
- },
666
- ],
667
- });
668
- } else {
669
- // No fill (transparent background)
670
- children.push({
671
- name: 'a:noFill',
672
- selfClosing: true,
673
- });
674
- }
675
-
676
- // Borders
677
- if (this.borders?.size !== undefined) {
678
- const lnAttrs: Record<string, string> = {
679
- w: (this.borders.size * UNITS.EMUS_PER_POINT).toString(), // Convert points to EMUs
680
- };
681
-
682
- const lnChildren: XMLElement[] = [];
683
-
684
- // Border color
685
- lnChildren.push({
686
- name: 'a:solidFill',
687
- children: [
688
- {
689
- name: 'a:srgbClr',
690
- attributes: {
691
- val: (this.borders.color || '000000').toUpperCase(),
692
- },
693
- selfClosing: true,
694
- },
695
- ],
696
- });
697
-
698
- // Border style
699
- if (this.borders.style && this.borders.style !== 'single') {
700
- const dashMap: Record<string, string> = {
701
- dash: 'dash',
702
- dot: 'dot',
703
- dashDot: 'dashDot',
704
- dashDotDot: 'lgDashDotDot',
705
- };
706
- const dashStyle = dashMap[this.borders.style] || 'solid';
707
- if (dashStyle !== 'solid') {
708
- lnChildren.push({
709
- name: 'a:prstDash',
710
- attributes: {
711
- val: dashStyle,
712
- },
713
- selfClosing: true,
714
- });
715
- }
716
- }
717
-
718
- children.push({
719
- name: 'a:ln',
720
- attributes: lnAttrs,
721
- children: lnChildren,
722
- });
723
- }
724
-
725
- return {
726
- name: 'wps:spPr',
727
- children,
728
- };
729
- }
730
-
731
- /**
732
- * Creates the wps:txbx element (text box content)
733
- * @private
734
- */
735
- private createTextBoxContent(): XMLElement {
736
- if (this.paragraphs.length === 0) {
737
- // Empty text box - create empty paragraph
738
- return {
739
- name: 'wps:txbx',
740
- children: [
741
- {
742
- name: 'w:txbxContent',
743
- children: [
744
- XMLBuilder.w('p', undefined, [
745
- XMLBuilder.w('r', undefined, [
746
- XMLBuilder.w('t', undefined, [''])
747
- ])
748
- ])
749
- ],
750
- },
751
- ],
752
- };
753
- }
754
-
755
- // Convert paragraphs to XML
756
- const paragraphXml = this.paragraphs.map(p => p.toXML());
757
-
758
- return {
759
- name: 'wps:txbx',
760
- children: [
761
- {
762
- name: 'w:txbxContent',
763
- children: paragraphXml,
764
- },
765
- ],
766
- };
767
- }
768
- }
1
+ /**
2
+ * TextBox - Represents a floating text box in a Word document
3
+ *
4
+ * Text boxes use DrawingML (a:) and WordprocessingML Drawing (wp:) namespaces,
5
+ * plus WordprocessingShape (wps:) namespace for Word 2010+ features.
6
+ *
7
+ * Per ECMA-376 Part 4 (Transitional) and Office Open XML extensions,
8
+ * text boxes in Word documents use the wps:wsp element with wps:txbx content.
9
+ */
10
+
11
+ import { XMLBuilder, XMLElement } from '../xml/XMLBuilder';
12
+ import { UNITS } from '../utils/units';
13
+ import { Paragraph } from './Paragraph';
14
+ import {
15
+ ImagePosition,
16
+ ImageAnchor,
17
+ PositionAnchor,
18
+ HorizontalAlignment,
19
+ VerticalAlignment,
20
+ } from './Image';
21
+ import { BorderDefinition } from './Paragraph';
22
+
23
+ /**
24
+ * TextBox margins configuration
25
+ */
26
+ export interface TextBoxMargins {
27
+ /** Top margin in EMUs */
28
+ top: number;
29
+ /** Bottom margin in EMUs */
30
+ bottom: number;
31
+ /** Left margin in EMUs */
32
+ left: number;
33
+ /** Right margin in EMUs */
34
+ right: number;
35
+ }
36
+
37
+ /**
38
+ * TextBox fill configuration
39
+ */
40
+ export interface TextBoxFill {
41
+ /** Fill color in hex (e.g., 'F0F0F0') */
42
+ color: string;
43
+ /** Transparency percentage (0-100) */
44
+ transparency?: number;
45
+ }
46
+
47
+ /**
48
+ * TextBox properties
49
+ */
50
+ export interface TextBoxProperties {
51
+ /** Width in EMUs */
52
+ width: number;
53
+ /** Height in EMUs */
54
+ height: number;
55
+ /** Paragraphs within the text box */
56
+ paragraphs?: Paragraph[];
57
+ /** Position configuration */
58
+ position?: ImagePosition;
59
+ /** Anchor configuration */
60
+ anchor?: ImageAnchor;
61
+ /** Fill color and transparency */
62
+ fill?: TextBoxFill;
63
+ /** Border configuration */
64
+ borders?: BorderDefinition;
65
+ /** Internal margins */
66
+ margins?: TextBoxMargins;
67
+ /** TextBox name */
68
+ name?: string;
69
+ /** TextBox description (for accessibility) */
70
+ description?: string;
71
+ }
72
+
73
+ /**
74
+ * Represents a floating text box
75
+ */
76
+ export class TextBox {
77
+ private width: number;
78
+ private height: number;
79
+ private paragraphs: Paragraph[] = [];
80
+ private position?: ImagePosition;
81
+ private anchor?: ImageAnchor;
82
+ private fill?: TextBoxFill;
83
+ private borders?: BorderDefinition;
84
+ private margins?: TextBoxMargins;
85
+ private name: string;
86
+ private description: string;
87
+ private docPrId = 1;
88
+
89
+ /**
90
+ * Creates a new text box
91
+ * @param properties TextBox properties
92
+ * @private Use static factory methods instead (create)
93
+ */
94
+ private constructor(properties: TextBoxProperties) {
95
+ this.width = properties.width;
96
+ this.height = properties.height;
97
+ if (properties.paragraphs) {
98
+ this.paragraphs = properties.paragraphs;
99
+ }
100
+ this.position = properties.position;
101
+ this.anchor = properties.anchor;
102
+ this.fill = properties.fill;
103
+ this.borders = properties.borders;
104
+ this.margins = properties.margins;
105
+ this.name = properties.name || 'TextBox';
106
+ this.description = properties.description || '';
107
+ }
108
+
109
+ /**
110
+ * Factory method for creating a text box
111
+ * @param width Width in EMUs
112
+ * @param height Height in EMUs
113
+ * @returns New TextBox instance
114
+ * @example
115
+ * const textbox = TextBox.create(inchesToEmus(3), inchesToEmus(2));
116
+ */
117
+ static create(width: number, height: number): TextBox {
118
+ return new TextBox({ width, height });
119
+ }
120
+
121
+ /**
122
+ * Gets the width in EMUs
123
+ * @returns Width
124
+ */
125
+ getWidth(): number {
126
+ return this.width;
127
+ }
128
+
129
+ /**
130
+ * Gets the height in EMUs
131
+ * @returns Height
132
+ */
133
+ getHeight(): number {
134
+ return this.height;
135
+ }
136
+
137
+ /**
138
+ * Adds a paragraph to the text box
139
+ * @param paragraph Paragraph to add
140
+ * @returns This text box for chaining
141
+ */
142
+ addParagraph(paragraph: Paragraph): this {
143
+ this.paragraphs.push(paragraph);
144
+ return this;
145
+ }
146
+
147
+ /**
148
+ * Gets all paragraphs
149
+ * @returns Array of paragraphs
150
+ */
151
+ getParagraphs(): Paragraph[] {
152
+ return this.paragraphs;
153
+ }
154
+
155
+ /**
156
+ * Sets text box position (for floating text boxes)
157
+ * @param horizontal Horizontal positioning configuration
158
+ * @param vertical Vertical positioning configuration
159
+ * @returns This text box for chaining
160
+ */
161
+ setPosition(
162
+ horizontal: { anchor: PositionAnchor; offset?: number; alignment?: HorizontalAlignment },
163
+ vertical: { anchor: PositionAnchor; offset?: number; alignment?: VerticalAlignment }
164
+ ): this {
165
+ this.position = { horizontal, vertical };
166
+ return this;
167
+ }
168
+
169
+ /**
170
+ * Gets the position configuration
171
+ * @returns Position or undefined
172
+ */
173
+ getPosition(): ImagePosition | undefined {
174
+ return this.position;
175
+ }
176
+
177
+ /**
178
+ * Sets anchor configuration
179
+ * @param options Anchor configuration
180
+ * @returns This text box for chaining
181
+ */
182
+ setAnchor(options: ImageAnchor): this {
183
+ this.anchor = options;
184
+ return this;
185
+ }
186
+
187
+ /**
188
+ * Gets the anchor configuration
189
+ * @returns Anchor configuration or undefined
190
+ */
191
+ getAnchor(): ImageAnchor | undefined {
192
+ return this.anchor;
193
+ }
194
+
195
+ /**
196
+ * Sets the fill color
197
+ * @param color Fill color in hex
198
+ * @param transparency Optional transparency percentage (0-100)
199
+ * @returns This text box for chaining
200
+ */
201
+ setFill(color: string, transparency?: number): this {
202
+ this.fill = { color, transparency };
203
+ return this;
204
+ }
205
+
206
+ /**
207
+ * Gets the fill configuration
208
+ * @returns Fill or undefined
209
+ */
210
+ getFill(): TextBoxFill | undefined {
211
+ return this.fill;
212
+ }
213
+
214
+ /**
215
+ * Sets the borders
216
+ * @param borders Border configuration
217
+ * @returns This text box for chaining
218
+ */
219
+ setBorders(borders: BorderDefinition): this {
220
+ this.borders = borders;
221
+ return this;
222
+ }
223
+
224
+ /**
225
+ * Gets the borders configuration
226
+ * @returns Borders or undefined
227
+ */
228
+ getBorders(): BorderDefinition | undefined {
229
+ return this.borders;
230
+ }
231
+
232
+ /**
233
+ * Sets internal margins
234
+ * @param margins Margin configuration
235
+ * @returns This text box for chaining
236
+ */
237
+ setMargins(margins: TextBoxMargins): this {
238
+ this.margins = margins;
239
+ return this;
240
+ }
241
+
242
+ /**
243
+ * Gets the margins configuration
244
+ * @returns Margins or undefined
245
+ */
246
+ getMargins(): TextBoxMargins | undefined {
247
+ return this.margins;
248
+ }
249
+
250
+ /**
251
+ * Sets the text box name
252
+ * @param name TextBox name
253
+ * @returns This text box for chaining
254
+ */
255
+ setName(name: string): this {
256
+ this.name = name;
257
+ return this;
258
+ }
259
+
260
+ /**
261
+ * Gets the text box name
262
+ * @returns TextBox name
263
+ */
264
+ getName(): string {
265
+ return this.name;
266
+ }
267
+
268
+ /**
269
+ * Sets the text box description (for accessibility)
270
+ * @param description TextBox description
271
+ * @returns This text box for chaining
272
+ */
273
+ setDescription(description: string): this {
274
+ this.description = description;
275
+ return this;
276
+ }
277
+
278
+ /**
279
+ * Gets the text box description
280
+ * @returns TextBox description
281
+ */
282
+ getDescription(): string {
283
+ return this.description;
284
+ }
285
+
286
+ /**
287
+ * Sets the docPr ID (drawing object ID)
288
+ * @param id Document property ID
289
+ * @returns This text box for chaining
290
+ */
291
+ setDocPrId(id: number): this {
292
+ this.docPrId = id;
293
+ return this;
294
+ }
295
+
296
+ /**
297
+ * Checks if this text box is floating (has anchor or position configuration)
298
+ * @returns True if floating, false if inline
299
+ */
300
+ isFloating(): boolean {
301
+ return this.anchor !== undefined || this.position !== undefined;
302
+ }
303
+
304
+ /**
305
+ * Generates DrawingML XML for the text box
306
+ * Creates either inline or floating (anchor) text box based on configuration
307
+ * @returns XML element representing the text box
308
+ */
309
+ toXML(): XMLElement {
310
+ // Text boxes are typically floating, but support inline as well
311
+ const textboxElement = this.isFloating() ? this.createAnchor() : this.createInline();
312
+
313
+ // Create the drawing structure
314
+ return XMLBuilder.w('drawing', undefined, [textboxElement]);
315
+ }
316
+
317
+ /**
318
+ * Creates the wp:inline element for inline text boxes
319
+ * @private
320
+ */
321
+ private createInline(): XMLElement {
322
+ const children: XMLElement[] = [];
323
+
324
+ // Extent (size)
325
+ children.push({
326
+ name: 'wp:extent',
327
+ attributes: {
328
+ cx: this.width.toString(),
329
+ cy: this.height.toString(),
330
+ },
331
+ selfClosing: true,
332
+ });
333
+
334
+ // Effect extent
335
+ children.push({
336
+ name: 'wp:effectExtent',
337
+ attributes: {
338
+ l: '0',
339
+ t: '0',
340
+ r: '0',
341
+ b: '0',
342
+ },
343
+ selfClosing: true,
344
+ });
345
+
346
+ // Document properties
347
+ children.push({
348
+ name: 'wp:docPr',
349
+ attributes: {
350
+ id: this.docPrId.toString(),
351
+ name: this.name,
352
+ descr: this.description,
353
+ },
354
+ selfClosing: true,
355
+ });
356
+
357
+ // Non-visual graphic frame properties
358
+ children.push({
359
+ name: 'wp:cNvGraphicFramePr',
360
+ selfClosing: true,
361
+ });
362
+
363
+ // Graphic data (the text box)
364
+ children.push(this.createGraphic());
365
+
366
+ return {
367
+ name: 'wp:inline',
368
+ attributes: {
369
+ distT: '0',
370
+ distB: '0',
371
+ distL: '0',
372
+ distR: '0',
373
+ },
374
+ children,
375
+ };
376
+ }
377
+
378
+ /**
379
+ * Creates the wp:anchor element for floating text boxes
380
+ * @private
381
+ */
382
+ private createAnchor(): XMLElement {
383
+ const children: XMLElement[] = [];
384
+
385
+ const anchorConfig = this.anchor || {
386
+ behindDoc: false,
387
+ locked: false,
388
+ layoutInCell: true,
389
+ allowOverlap: false,
390
+ relativeHeight: 251658240,
391
+ };
392
+
393
+ // Position H (horizontal)
394
+ if (this.position) {
395
+ const posH = this.position.horizontal;
396
+ const posHChildren: XMLElement[] = [];
397
+
398
+ if (posH.offset !== undefined) {
399
+ posHChildren.push({
400
+ name: 'wp:posOffset',
401
+ children: [posH.offset.toString()],
402
+ });
403
+ } else if (posH.alignment) {
404
+ posHChildren.push({
405
+ name: 'wp:align',
406
+ children: [posH.alignment],
407
+ });
408
+ }
409
+
410
+ children.push({
411
+ name: 'wp:positionH',
412
+ attributes: {
413
+ relativeFrom: posH.anchor,
414
+ },
415
+ children: posHChildren,
416
+ });
417
+ }
418
+
419
+ // Position V (vertical)
420
+ if (this.position) {
421
+ const posV = this.position.vertical;
422
+ const posVChildren: XMLElement[] = [];
423
+
424
+ if (posV.offset !== undefined) {
425
+ posVChildren.push({
426
+ name: 'wp:posOffset',
427
+ children: [posV.offset.toString()],
428
+ });
429
+ } else if (posV.alignment) {
430
+ posVChildren.push({
431
+ name: 'wp:align',
432
+ children: [posV.alignment],
433
+ });
434
+ }
435
+
436
+ children.push({
437
+ name: 'wp:positionV',
438
+ attributes: {
439
+ relativeFrom: posV.anchor,
440
+ },
441
+ children: posVChildren,
442
+ });
443
+ }
444
+
445
+ // Extent (size)
446
+ children.push({
447
+ name: 'wp:extent',
448
+ attributes: {
449
+ cx: this.width.toString(),
450
+ cy: this.height.toString(),
451
+ },
452
+ selfClosing: true,
453
+ });
454
+
455
+ // Effect extent
456
+ children.push({
457
+ name: 'wp:effectExtent',
458
+ attributes: {
459
+ l: '0',
460
+ t: '0',
461
+ r: '0',
462
+ b: '0',
463
+ },
464
+ selfClosing: true,
465
+ });
466
+
467
+ // Wrap square (default for text boxes)
468
+ children.push({
469
+ name: 'wp:wrapSquare',
470
+ attributes: {
471
+ wrapText: 'bothSides',
472
+ },
473
+ selfClosing: true,
474
+ });
475
+
476
+ // Document properties
477
+ children.push({
478
+ name: 'wp:docPr',
479
+ attributes: {
480
+ id: this.docPrId.toString(),
481
+ name: this.name,
482
+ descr: this.description,
483
+ },
484
+ selfClosing: true,
485
+ });
486
+
487
+ // Non-visual graphic frame properties
488
+ children.push({
489
+ name: 'wp:cNvGraphicFramePr',
490
+ selfClosing: true,
491
+ });
492
+
493
+ // Graphic data (the text box)
494
+ children.push(this.createGraphic());
495
+
496
+ return {
497
+ name: 'wp:anchor',
498
+ attributes: {
499
+ distT: '0',
500
+ distB: '0',
501
+ distL: '0',
502
+ distR: '0',
503
+ simplePos: '0',
504
+ relativeHeight: anchorConfig.relativeHeight.toString(),
505
+ behindDoc: anchorConfig.behindDoc ? '1' : '0',
506
+ locked: anchorConfig.locked ? '1' : '0',
507
+ layoutInCell: anchorConfig.layoutInCell ? '1' : '0',
508
+ allowOverlap: anchorConfig.allowOverlap ? '1' : '0',
509
+ },
510
+ children,
511
+ };
512
+ }
513
+
514
+ /**
515
+ * Creates the a:graphic element containing the text box
516
+ * @private
517
+ */
518
+ private createGraphic(): XMLElement {
519
+ return {
520
+ name: 'a:graphic',
521
+ attributes: {
522
+ 'xmlns:a': 'http://schemas.openxmlformats.org/drawingml/2006/main',
523
+ },
524
+ children: [
525
+ {
526
+ name: 'a:graphicData',
527
+ attributes: {
528
+ uri: 'http://schemas.microsoft.com/office/word/2010/wordprocessingShape',
529
+ },
530
+ children: [this.createWps()],
531
+ },
532
+ ],
533
+ };
534
+ }
535
+
536
+ /**
537
+ * Creates the wps:wsp element (WordprocessingShape for text box)
538
+ * @private
539
+ */
540
+ private createWps(): XMLElement {
541
+ const children: XMLElement[] = [];
542
+
543
+ // Non-visual shape properties
544
+ children.push({
545
+ name: 'wps:cNvSpPr',
546
+ attributes: {
547
+ 'xmlns:wps': 'http://schemas.microsoft.com/office/word/2010/wordprocessingShape',
548
+ txBox: '1', // Indicates this is a text box
549
+ },
550
+ selfClosing: true,
551
+ });
552
+
553
+ // Shape properties (size, fill, borders)
554
+ children.push(this.createSpPr());
555
+
556
+ // Text box content
557
+ children.push(this.createTextBoxContent());
558
+
559
+ // Body properties (margins/insets)
560
+ children.push(this.createBodyPr());
561
+
562
+ return {
563
+ name: 'wps:wsp',
564
+ attributes: {
565
+ 'xmlns:wps': 'http://schemas.microsoft.com/office/word/2010/wordprocessingShape',
566
+ },
567
+ children,
568
+ };
569
+ }
570
+
571
+ /**
572
+ * Creates the wps:bodyPr element (text body properties including margins)
573
+ * Per ECMA-376 Part 4, bodyPr specifies inset margins via lIns/tIns/rIns/bIns attributes
574
+ * @private
575
+ */
576
+ private createBodyPr(): XMLElement {
577
+ const attrs: Record<string, string> = {
578
+ rot: '0',
579
+ vert: 'horz',
580
+ wrap: 'square',
581
+ anchor: 't',
582
+ };
583
+
584
+ // Add margin insets if set
585
+ if (this.margins) {
586
+ if (this.margins.left !== undefined) attrs.lIns = this.margins.left.toString();
587
+ if (this.margins.top !== undefined) attrs.tIns = this.margins.top.toString();
588
+ if (this.margins.right !== undefined) attrs.rIns = this.margins.right.toString();
589
+ if (this.margins.bottom !== undefined) attrs.bIns = this.margins.bottom.toString();
590
+ }
591
+
592
+ return {
593
+ name: 'wps:bodyPr',
594
+ attributes: attrs,
595
+ selfClosing: true,
596
+ };
597
+ }
598
+
599
+ /**
600
+ * Creates the wps:spPr element (shape properties for text box)
601
+ * @private
602
+ */
603
+ private createSpPr(): XMLElement {
604
+ const children: XMLElement[] = [];
605
+
606
+ // Transform (position and size)
607
+ children.push({
608
+ name: 'a:xfrm',
609
+ children: [
610
+ {
611
+ name: 'a:off',
612
+ attributes: { x: '0', y: '0' },
613
+ selfClosing: true,
614
+ },
615
+ {
616
+ name: 'a:ext',
617
+ attributes: {
618
+ cx: this.width.toString(),
619
+ cy: this.height.toString(),
620
+ },
621
+ selfClosing: true,
622
+ },
623
+ ],
624
+ });
625
+
626
+ // Preset geometry (rectangle for text box)
627
+ children.push({
628
+ name: 'a:prstGeom',
629
+ attributes: {
630
+ prst: 'rect',
631
+ },
632
+ children: [
633
+ {
634
+ name: 'a:avLst',
635
+ selfClosing: true,
636
+ },
637
+ ],
638
+ });
639
+
640
+ // Fill
641
+ if (this.fill) {
642
+ const fillChildren: XMLElement[] = [];
643
+
644
+ // Transparency
645
+ if (this.fill.transparency !== undefined) {
646
+ const alpha = 100 - this.fill.transparency;
647
+ fillChildren.push({
648
+ name: 'a:alpha',
649
+ attributes: {
650
+ val: Math.round(alpha * 1000).toString(),
651
+ },
652
+ selfClosing: true,
653
+ });
654
+ }
655
+
656
+ children.push({
657
+ name: 'a:solidFill',
658
+ children: [
659
+ {
660
+ name: 'a:srgbClr',
661
+ attributes: {
662
+ val: this.fill.color.toUpperCase(),
663
+ },
664
+ ...(fillChildren.length > 0 ? { children: fillChildren } : { selfClosing: true }),
665
+ },
666
+ ],
667
+ });
668
+ } else {
669
+ // No fill (transparent background)
670
+ children.push({
671
+ name: 'a:noFill',
672
+ selfClosing: true,
673
+ });
674
+ }
675
+
676
+ // Borders
677
+ if (this.borders?.size !== undefined) {
678
+ const lnAttrs: Record<string, string> = {
679
+ w: (this.borders.size * UNITS.EMUS_PER_POINT).toString(), // Convert points to EMUs
680
+ };
681
+
682
+ const lnChildren: XMLElement[] = [];
683
+
684
+ // Border color
685
+ lnChildren.push({
686
+ name: 'a:solidFill',
687
+ children: [
688
+ {
689
+ name: 'a:srgbClr',
690
+ attributes: {
691
+ val: (this.borders.color || '000000').toUpperCase(),
692
+ },
693
+ selfClosing: true,
694
+ },
695
+ ],
696
+ });
697
+
698
+ // Border style
699
+ if (this.borders.style && this.borders.style !== 'single') {
700
+ const dashMap: Record<string, string> = {
701
+ dash: 'dash',
702
+ dot: 'dot',
703
+ dashDot: 'dashDot',
704
+ dashDotDot: 'lgDashDotDot',
705
+ };
706
+ const dashStyle = dashMap[this.borders.style] || 'solid';
707
+ if (dashStyle !== 'solid') {
708
+ lnChildren.push({
709
+ name: 'a:prstDash',
710
+ attributes: {
711
+ val: dashStyle,
712
+ },
713
+ selfClosing: true,
714
+ });
715
+ }
716
+ }
717
+
718
+ children.push({
719
+ name: 'a:ln',
720
+ attributes: lnAttrs,
721
+ children: lnChildren,
722
+ });
723
+ }
724
+
725
+ return {
726
+ name: 'wps:spPr',
727
+ children,
728
+ };
729
+ }
730
+
731
+ /**
732
+ * Creates the wps:txbx element (text box content)
733
+ * @private
734
+ */
735
+ private createTextBoxContent(): XMLElement {
736
+ if (this.paragraphs.length === 0) {
737
+ // Empty text box - create empty paragraph
738
+ return {
739
+ name: 'wps:txbx',
740
+ children: [
741
+ {
742
+ name: 'w:txbxContent',
743
+ children: [
744
+ XMLBuilder.w('p', undefined, [
745
+ XMLBuilder.w('r', undefined, [XMLBuilder.w('t', undefined, [''])]),
746
+ ]),
747
+ ],
748
+ },
749
+ ],
750
+ };
751
+ }
752
+
753
+ // Convert paragraphs to XML
754
+ const paragraphXml = this.paragraphs.map((p) => p.toXML());
755
+
756
+ return {
757
+ name: 'wps:txbx',
758
+ children: [
759
+ {
760
+ name: 'w:txbxContent',
761
+ children: paragraphXml,
762
+ },
763
+ ],
764
+ };
765
+ }
766
+ }