docxmlater 11.0.10 → 12.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. package/README.md +53 -13
  2. package/dist/core/Document.d.ts +29 -3
  3. package/dist/core/Document.d.ts.map +1 -1
  4. package/dist/core/Document.js +733 -170
  5. package/dist/core/Document.js.map +1 -1
  6. package/dist/core/DocumentGenerator.d.ts +1 -0
  7. package/dist/core/DocumentGenerator.d.ts.map +1 -1
  8. package/dist/core/DocumentGenerator.js +77 -25
  9. package/dist/core/DocumentGenerator.js.map +1 -1
  10. package/dist/core/DocumentParser.d.ts +7 -3
  11. package/dist/core/DocumentParser.d.ts.map +1 -1
  12. package/dist/core/DocumentParser.js +427 -81
  13. package/dist/core/DocumentParser.js.map +1 -1
  14. package/dist/core/RelationshipManager.d.ts.map +1 -1
  15. package/dist/core/RelationshipManager.js +2 -2
  16. package/dist/core/RelationshipManager.js.map +1 -1
  17. package/dist/elements/Comment.d.ts +15 -1
  18. package/dist/elements/Comment.d.ts.map +1 -1
  19. package/dist/elements/Comment.js +67 -12
  20. package/dist/elements/Comment.js.map +1 -1
  21. package/dist/elements/CommentManager.d.ts +4 -0
  22. package/dist/elements/CommentManager.d.ts.map +1 -1
  23. package/dist/elements/CommentManager.js +74 -16
  24. package/dist/elements/CommentManager.js.map +1 -1
  25. package/dist/elements/EndnoteManager.d.ts.map +1 -1
  26. package/dist/elements/EndnoteManager.js.map +1 -1
  27. package/dist/elements/Field.d.ts +10 -1
  28. package/dist/elements/Field.d.ts.map +1 -1
  29. package/dist/elements/Field.js +76 -34
  30. package/dist/elements/Field.js.map +1 -1
  31. package/dist/elements/FieldHelpers.d.ts +1 -0
  32. package/dist/elements/FieldHelpers.d.ts.map +1 -1
  33. package/dist/elements/FieldHelpers.js +8 -4
  34. package/dist/elements/FieldHelpers.js.map +1 -1
  35. package/dist/elements/Footer.d.ts +3 -1
  36. package/dist/elements/Footer.d.ts.map +1 -1
  37. package/dist/elements/Footer.js +39 -53
  38. package/dist/elements/Footer.js.map +1 -1
  39. package/dist/elements/FootnoteManager.d.ts.map +1 -1
  40. package/dist/elements/FootnoteManager.js.map +1 -1
  41. package/dist/elements/Header.d.ts +3 -1
  42. package/dist/elements/Header.d.ts.map +1 -1
  43. package/dist/elements/Header.js +39 -53
  44. package/dist/elements/Header.js.map +1 -1
  45. package/dist/elements/HeaderFooterManager.d.ts +4 -2
  46. package/dist/elements/HeaderFooterManager.d.ts.map +1 -1
  47. package/dist/elements/HeaderFooterManager.js +36 -6
  48. package/dist/elements/HeaderFooterManager.js.map +1 -1
  49. package/dist/elements/Hyperlink.d.ts +6 -1
  50. package/dist/elements/Hyperlink.d.ts.map +1 -1
  51. package/dist/elements/Hyperlink.js +69 -44
  52. package/dist/elements/Hyperlink.js.map +1 -1
  53. package/dist/elements/Image.d.ts.map +1 -1
  54. package/dist/elements/Image.js +33 -6
  55. package/dist/elements/Image.js.map +1 -1
  56. package/dist/elements/ImageManager.d.ts +1 -0
  57. package/dist/elements/ImageManager.d.ts.map +1 -1
  58. package/dist/elements/ImageManager.js +11 -0
  59. package/dist/elements/ImageManager.js.map +1 -1
  60. package/dist/elements/Paragraph.d.ts +3 -0
  61. package/dist/elements/Paragraph.d.ts.map +1 -1
  62. package/dist/elements/Paragraph.js +91 -26
  63. package/dist/elements/Paragraph.js.map +1 -1
  64. package/dist/elements/Revision.d.ts +4 -0
  65. package/dist/elements/Revision.d.ts.map +1 -1
  66. package/dist/elements/Revision.js +369 -29
  67. package/dist/elements/Revision.js.map +1 -1
  68. package/dist/elements/Run.d.ts +1 -0
  69. package/dist/elements/Run.d.ts.map +1 -1
  70. package/dist/elements/Run.js +24 -4
  71. package/dist/elements/Run.js.map +1 -1
  72. package/dist/elements/Section.d.ts.map +1 -1
  73. package/dist/elements/Section.js +17 -7
  74. package/dist/elements/Section.js.map +1 -1
  75. package/dist/elements/Shape.d.ts.map +1 -1
  76. package/dist/elements/Shape.js +50 -42
  77. package/dist/elements/Shape.js.map +1 -1
  78. package/dist/elements/StructuredDocumentTag.d.ts +9 -1
  79. package/dist/elements/StructuredDocumentTag.d.ts.map +1 -1
  80. package/dist/elements/StructuredDocumentTag.js +46 -6
  81. package/dist/elements/StructuredDocumentTag.js.map +1 -1
  82. package/dist/elements/Table.d.ts +2 -0
  83. package/dist/elements/Table.d.ts.map +1 -1
  84. package/dist/elements/Table.js +90 -42
  85. package/dist/elements/Table.js.map +1 -1
  86. package/dist/elements/TableCell.d.ts +1 -0
  87. package/dist/elements/TableCell.d.ts.map +1 -1
  88. package/dist/elements/TableCell.js +32 -13
  89. package/dist/elements/TableCell.js.map +1 -1
  90. package/dist/elements/TableOfContents.d.ts.map +1 -1
  91. package/dist/elements/TableOfContents.js +5 -7
  92. package/dist/elements/TableOfContents.js.map +1 -1
  93. package/dist/elements/TableRow.d.ts +1 -0
  94. package/dist/elements/TableRow.d.ts.map +1 -1
  95. package/dist/elements/TableRow.js +16 -1
  96. package/dist/elements/TableRow.js.map +1 -1
  97. package/dist/elements/TextBox.d.ts.map +1 -1
  98. package/dist/elements/TextBox.js +55 -42
  99. package/dist/elements/TextBox.js.map +1 -1
  100. package/dist/esm/core/Document.js +735 -172
  101. package/dist/esm/core/Document.js.map +1 -1
  102. package/dist/esm/core/DocumentGenerator.js +77 -25
  103. package/dist/esm/core/DocumentGenerator.js.map +1 -1
  104. package/dist/esm/core/DocumentParser.js +427 -81
  105. package/dist/esm/core/DocumentParser.js.map +1 -1
  106. package/dist/esm/core/RelationshipManager.js +2 -2
  107. package/dist/esm/core/RelationshipManager.js.map +1 -1
  108. package/dist/esm/elements/Comment.js +67 -12
  109. package/dist/esm/elements/Comment.js.map +1 -1
  110. package/dist/esm/elements/CommentManager.js +74 -16
  111. package/dist/esm/elements/CommentManager.js.map +1 -1
  112. package/dist/esm/elements/EndnoteManager.js.map +1 -1
  113. package/dist/esm/elements/Field.js +77 -35
  114. package/dist/esm/elements/Field.js.map +1 -1
  115. package/dist/esm/elements/FieldHelpers.js +7 -4
  116. package/dist/esm/elements/FieldHelpers.js.map +1 -1
  117. package/dist/esm/elements/Footer.js +39 -53
  118. package/dist/esm/elements/Footer.js.map +1 -1
  119. package/dist/esm/elements/FootnoteManager.js.map +1 -1
  120. package/dist/esm/elements/Header.js +39 -53
  121. package/dist/esm/elements/Header.js.map +1 -1
  122. package/dist/esm/elements/HeaderFooterManager.js +36 -6
  123. package/dist/esm/elements/HeaderFooterManager.js.map +1 -1
  124. package/dist/esm/elements/Hyperlink.js +69 -44
  125. package/dist/esm/elements/Hyperlink.js.map +1 -1
  126. package/dist/esm/elements/Image.js +33 -6
  127. package/dist/esm/elements/Image.js.map +1 -1
  128. package/dist/esm/elements/ImageManager.js +11 -0
  129. package/dist/esm/elements/ImageManager.js.map +1 -1
  130. package/dist/esm/elements/Paragraph.js +91 -26
  131. package/dist/esm/elements/Paragraph.js.map +1 -1
  132. package/dist/esm/elements/Revision.js +369 -29
  133. package/dist/esm/elements/Revision.js.map +1 -1
  134. package/dist/esm/elements/Run.js +24 -4
  135. package/dist/esm/elements/Run.js.map +1 -1
  136. package/dist/esm/elements/Section.js +17 -7
  137. package/dist/esm/elements/Section.js.map +1 -1
  138. package/dist/esm/elements/Shape.js +50 -42
  139. package/dist/esm/elements/Shape.js.map +1 -1
  140. package/dist/esm/elements/StructuredDocumentTag.js +46 -6
  141. package/dist/esm/elements/StructuredDocumentTag.js.map +1 -1
  142. package/dist/esm/elements/Table.js +90 -42
  143. package/dist/esm/elements/Table.js.map +1 -1
  144. package/dist/esm/elements/TableCell.js +32 -13
  145. package/dist/esm/elements/TableCell.js.map +1 -1
  146. package/dist/esm/elements/TableOfContents.js +5 -7
  147. package/dist/esm/elements/TableOfContents.js.map +1 -1
  148. package/dist/esm/elements/TableRow.js +16 -1
  149. package/dist/esm/elements/TableRow.js.map +1 -1
  150. package/dist/esm/elements/TextBox.js +55 -42
  151. package/dist/esm/elements/TextBox.js.map +1 -1
  152. package/dist/esm/formatting/AbstractNumbering.js +1 -1
  153. package/dist/esm/formatting/AbstractNumbering.js.map +1 -1
  154. package/dist/esm/formatting/NumberingLevel.js +8 -6
  155. package/dist/esm/formatting/NumberingLevel.js.map +1 -1
  156. package/dist/esm/formatting/NumberingManager.js +16 -1
  157. package/dist/esm/formatting/NumberingManager.js.map +1 -1
  158. package/dist/esm/formatting/Style.js +14 -0
  159. package/dist/esm/formatting/Style.js.map +1 -1
  160. package/dist/esm/formatting/StylesManager.js +14 -2
  161. package/dist/esm/formatting/StylesManager.js.map +1 -1
  162. package/dist/esm/helpers/CleanupHelper.js +103 -26
  163. package/dist/esm/helpers/CleanupHelper.js.map +1 -1
  164. package/dist/esm/images/ImageOptimizer.js +52 -4
  165. package/dist/esm/images/ImageOptimizer.js.map +1 -1
  166. package/dist/esm/index.js +1 -0
  167. package/dist/esm/index.js.map +1 -1
  168. package/dist/esm/internal.js +1 -1
  169. package/dist/esm/internal.js.map +1 -1
  170. package/dist/esm/processors/CompatibilityUpgrader.js +7 -1
  171. package/dist/esm/processors/CompatibilityUpgrader.js.map +1 -1
  172. package/dist/esm/processors/InMemoryRevisionAcceptor.js +134 -13
  173. package/dist/esm/processors/InMemoryRevisionAcceptor.js.map +1 -1
  174. package/dist/esm/processors/RevisionWalker.js +223 -39
  175. package/dist/esm/processors/RevisionWalker.js.map +1 -1
  176. package/dist/esm/processors/SelectiveRevisionAcceptor.js +116 -17
  177. package/dist/esm/processors/SelectiveRevisionAcceptor.js.map +1 -1
  178. package/dist/esm/processors/acceptRevisions.js +20 -11
  179. package/dist/esm/processors/acceptRevisions.js.map +1 -1
  180. package/dist/esm/processors/cnfStyleDecoder.js +7 -7
  181. package/dist/esm/processors/cnfStyleDecoder.js.map +1 -1
  182. package/dist/esm/processors/stripTrackedChanges.js +16 -5
  183. package/dist/esm/processors/stripTrackedChanges.js.map +1 -1
  184. package/dist/esm/utils/deepClone.js +24 -11
  185. package/dist/esm/utils/deepClone.js.map +1 -1
  186. package/dist/esm/utils/units.js +1 -1
  187. package/dist/esm/utils/units.js.map +1 -1
  188. package/dist/esm/utils/validation.js +11 -23
  189. package/dist/esm/utils/validation.js.map +1 -1
  190. package/dist/esm/utils/xmlSanitization.js +131 -7
  191. package/dist/esm/utils/xmlSanitization.js.map +1 -1
  192. package/dist/esm/validation/RevisionAutoFixer.js +69 -55
  193. package/dist/esm/validation/RevisionAutoFixer.js.map +1 -1
  194. package/dist/esm/validation/RevisionValidator.js +24 -20
  195. package/dist/esm/validation/RevisionValidator.js.map +1 -1
  196. package/dist/esm/xml/XMLBuilder.js +52 -20
  197. package/dist/esm/xml/XMLBuilder.js.map +1 -1
  198. package/dist/esm/xml/XMLParser.js +55 -13
  199. package/dist/esm/xml/XMLParser.js.map +1 -1
  200. package/dist/esm/zip/ZipHandler.js +37 -13
  201. package/dist/esm/zip/ZipHandler.js.map +1 -1
  202. package/dist/esm/zip/ZipReader.js +53 -6
  203. package/dist/esm/zip/ZipReader.js.map +1 -1
  204. package/dist/esm/zip/errors.js +7 -0
  205. package/dist/esm/zip/errors.js.map +1 -1
  206. package/dist/esm/zip/types.js +4 -0
  207. package/dist/esm/zip/types.js.map +1 -1
  208. package/dist/formatting/AbstractNumbering.d.ts.map +1 -1
  209. package/dist/formatting/AbstractNumbering.js +1 -1
  210. package/dist/formatting/AbstractNumbering.js.map +1 -1
  211. package/dist/formatting/NumberingLevel.d.ts +1 -1
  212. package/dist/formatting/NumberingLevel.d.ts.map +1 -1
  213. package/dist/formatting/NumberingLevel.js +8 -6
  214. package/dist/formatting/NumberingLevel.js.map +1 -1
  215. package/dist/formatting/NumberingManager.d.ts.map +1 -1
  216. package/dist/formatting/NumberingManager.js +16 -1
  217. package/dist/formatting/NumberingManager.js.map +1 -1
  218. package/dist/formatting/Style.d.ts.map +1 -1
  219. package/dist/formatting/Style.js +14 -0
  220. package/dist/formatting/Style.js.map +1 -1
  221. package/dist/formatting/StylesManager.d.ts +2 -0
  222. package/dist/formatting/StylesManager.d.ts.map +1 -1
  223. package/dist/formatting/StylesManager.js +14 -2
  224. package/dist/formatting/StylesManager.js.map +1 -1
  225. package/dist/helpers/CleanupHelper.d.ts.map +1 -1
  226. package/dist/helpers/CleanupHelper.js +103 -26
  227. package/dist/helpers/CleanupHelper.js.map +1 -1
  228. package/dist/images/ImageOptimizer.d.ts.map +1 -1
  229. package/dist/images/ImageOptimizer.js +52 -4
  230. package/dist/images/ImageOptimizer.js.map +1 -1
  231. package/dist/index.d.ts +3 -1
  232. package/dist/index.d.ts.map +1 -1
  233. package/dist/index.js +4 -2
  234. package/dist/index.js.map +1 -1
  235. package/dist/internal.d.ts +1 -1
  236. package/dist/internal.d.ts.map +1 -1
  237. package/dist/internal.js +2 -1
  238. package/dist/internal.js.map +1 -1
  239. package/dist/processors/CompatibilityUpgrader.d.ts.map +1 -1
  240. package/dist/processors/CompatibilityUpgrader.js +7 -1
  241. package/dist/processors/CompatibilityUpgrader.js.map +1 -1
  242. package/dist/processors/InMemoryRevisionAcceptor.d.ts +1 -0
  243. package/dist/processors/InMemoryRevisionAcceptor.d.ts.map +1 -1
  244. package/dist/processors/InMemoryRevisionAcceptor.js +135 -13
  245. package/dist/processors/InMemoryRevisionAcceptor.js.map +1 -1
  246. package/dist/processors/RevisionWalker.d.ts +9 -3
  247. package/dist/processors/RevisionWalker.d.ts.map +1 -1
  248. package/dist/processors/RevisionWalker.js +223 -39
  249. package/dist/processors/RevisionWalker.js.map +1 -1
  250. package/dist/processors/SelectiveRevisionAcceptor.d.ts.map +1 -1
  251. package/dist/processors/SelectiveRevisionAcceptor.js +116 -17
  252. package/dist/processors/SelectiveRevisionAcceptor.js.map +1 -1
  253. package/dist/processors/acceptRevisions.d.ts +1 -0
  254. package/dist/processors/acceptRevisions.d.ts.map +1 -1
  255. package/dist/processors/acceptRevisions.js +21 -11
  256. package/dist/processors/acceptRevisions.js.map +1 -1
  257. package/dist/processors/cnfStyleDecoder.d.ts.map +1 -1
  258. package/dist/processors/cnfStyleDecoder.js +7 -7
  259. package/dist/processors/cnfStyleDecoder.js.map +1 -1
  260. package/dist/processors/stripTrackedChanges.d.ts.map +1 -1
  261. package/dist/processors/stripTrackedChanges.js +16 -5
  262. package/dist/processors/stripTrackedChanges.js.map +1 -1
  263. package/dist/utils/deepClone.d.ts.map +1 -1
  264. package/dist/utils/deepClone.js +24 -11
  265. package/dist/utils/deepClone.js.map +1 -1
  266. package/dist/utils/units.d.ts.map +1 -1
  267. package/dist/utils/units.js +1 -1
  268. package/dist/utils/units.js.map +1 -1
  269. package/dist/utils/validation.d.ts.map +1 -1
  270. package/dist/utils/validation.js +11 -23
  271. package/dist/utils/validation.js.map +1 -1
  272. package/dist/utils/xmlSanitization.d.ts +2 -0
  273. package/dist/utils/xmlSanitization.d.ts.map +1 -1
  274. package/dist/utils/xmlSanitization.js +133 -7
  275. package/dist/utils/xmlSanitization.js.map +1 -1
  276. package/dist/validation/RevisionAutoFixer.d.ts +6 -2
  277. package/dist/validation/RevisionAutoFixer.d.ts.map +1 -1
  278. package/dist/validation/RevisionAutoFixer.js +68 -54
  279. package/dist/validation/RevisionAutoFixer.js.map +1 -1
  280. package/dist/validation/RevisionValidator.d.ts +2 -0
  281. package/dist/validation/RevisionValidator.d.ts.map +1 -1
  282. package/dist/validation/RevisionValidator.js +26 -21
  283. package/dist/validation/RevisionValidator.js.map +1 -1
  284. package/dist/xml/XMLBuilder.d.ts +1 -0
  285. package/dist/xml/XMLBuilder.d.ts.map +1 -1
  286. package/dist/xml/XMLBuilder.js +52 -20
  287. package/dist/xml/XMLBuilder.js.map +1 -1
  288. package/dist/xml/XMLParser.d.ts +1 -0
  289. package/dist/xml/XMLParser.d.ts.map +1 -1
  290. package/dist/xml/XMLParser.js +55 -13
  291. package/dist/xml/XMLParser.js.map +1 -1
  292. package/dist/zip/ZipHandler.d.ts.map +1 -1
  293. package/dist/zip/ZipHandler.js +37 -13
  294. package/dist/zip/ZipHandler.js.map +1 -1
  295. package/dist/zip/ZipReader.d.ts.map +1 -1
  296. package/dist/zip/ZipReader.js +52 -5
  297. package/dist/zip/ZipReader.js.map +1 -1
  298. package/dist/zip/errors.d.ts +3 -0
  299. package/dist/zip/errors.d.ts.map +1 -1
  300. package/dist/zip/errors.js +9 -1
  301. package/dist/zip/errors.js.map +1 -1
  302. package/dist/zip/types.d.ts +4 -0
  303. package/dist/zip/types.d.ts.map +1 -1
  304. package/dist/zip/types.js +4 -0
  305. package/dist/zip/types.js.map +1 -1
  306. package/package.json +2 -2
  307. package/src/core/Document.ts +1184 -242
  308. package/src/core/DocumentGenerator.ts +106 -26
  309. package/src/core/DocumentParser.ts +681 -114
  310. package/src/core/RelationshipManager.ts +446 -442
  311. package/src/elements/Comment.ts +123 -18
  312. package/src/elements/CommentManager.ts +121 -22
  313. package/src/elements/EndnoteManager.ts +4 -1
  314. package/src/elements/Field.ts +134 -43
  315. package/src/elements/FieldHelpers.ts +20 -4
  316. package/src/elements/Footer.ts +62 -67
  317. package/src/elements/FootnoteManager.ts +4 -1
  318. package/src/elements/Header.ts +62 -67
  319. package/src/elements/HeaderFooterManager.ts +56 -8
  320. package/src/elements/Hyperlink.ts +150 -55
  321. package/src/elements/Image.ts +51 -7
  322. package/src/elements/ImageManager.ts +24 -0
  323. package/src/elements/Paragraph.ts +161 -40
  324. package/src/elements/Revision.ts +446 -47
  325. package/src/elements/Run.ts +44 -6
  326. package/src/elements/Section.ts +25 -9
  327. package/src/elements/Shape.ts +52 -44
  328. package/src/elements/StructuredDocumentTag.ts +83 -8
  329. package/src/elements/Table.ts +151 -50
  330. package/src/elements/TableCell.ts +57 -17
  331. package/src/elements/TableOfContents.ts +10 -9
  332. package/src/elements/TableRow.ts +35 -3
  333. package/src/elements/TextBox.ts +58 -43
  334. package/src/formatting/AbstractNumbering.ts +4 -2
  335. package/src/formatting/NumberingLevel.ts +50 -12
  336. package/src/formatting/NumberingManager.ts +32 -3
  337. package/src/formatting/Style.ts +14 -0
  338. package/src/formatting/StylesManager.ts +29 -2
  339. package/src/helpers/CleanupHelper.ts +122 -30
  340. package/src/images/ImageOptimizer.ts +64 -5
  341. package/src/index.ts +7 -0
  342. package/src/internal.ts +1 -0
  343. package/src/processors/CompatibilityUpgrader.ts +15 -2
  344. package/src/processors/InMemoryRevisionAcceptor.ts +276 -17
  345. package/src/processors/RevisionWalker.ts +390 -46
  346. package/src/processors/SelectiveRevisionAcceptor.ts +164 -28
  347. package/src/processors/acceptRevisions.ts +67 -17
  348. package/src/processors/cnfStyleDecoder.ts +18 -12
  349. package/src/processors/stripTrackedChanges.ts +47 -10
  350. package/src/utils/deepClone.ts +48 -14
  351. package/src/utils/units.ts +3 -1
  352. package/src/utils/validation.ts +30 -32
  353. package/src/utils/xmlSanitization.ts +218 -22
  354. package/src/validation/RevisionAutoFixer.ts +109 -65
  355. package/src/validation/RevisionValidator.ts +48 -23
  356. package/src/xml/XMLBuilder.ts +82 -22
  357. package/src/xml/XMLParser.ts +87 -20
  358. package/src/zip/ZipHandler.ts +61 -21
  359. package/src/zip/ZipReader.ts +135 -13
  360. package/src/zip/errors.ts +15 -0
  361. package/src/zip/types.ts +44 -2
@@ -35,10 +35,11 @@ import { UNITS } from '../utils/units.js';
35
35
  function getLogger() {
36
36
  return createScopedLogger(getGlobalLogger(), 'Document');
37
37
  }
38
- import { acceptAllRevisions, cleanupRevisionMetadata } from '../processors/acceptRevisions.js';
38
+ import { acceptAllRevisions, rejectAllRevisions, cleanupRevisionMetadata, } from '../processors/acceptRevisions.js';
39
39
  import { acceptRevisionsInMemory } from '../processors/InMemoryRevisionAcceptor.js';
40
40
  import { stripTrackedChanges } from '../processors/stripTrackedChanges.js';
41
41
  import { diffText, diffHasUnchangedParts } from '../utils/textDiff.js';
42
+ import { reorderRunPropertyChildren } from '../utils/xmlSanitization.js';
42
43
  import { XMLBuilder } from '../xml/XMLBuilder.js';
43
44
  import { XMLParser } from '../xml/XMLParser.js';
44
45
  import { DocumentTrackingContext } from '../tracking/DocumentTrackingContext.js';
@@ -50,7 +51,7 @@ import { DocumentParser } from './DocumentParser.js';
50
51
  import { DocumentValidator } from './DocumentValidator.js';
51
52
  import { DocumentEventEmitter, } from './DocumentEvents.js';
52
53
  import { RelationshipManager } from './RelationshipManager.js';
53
- import { RelationshipType } from './Relationship.js';
54
+ import { Relationship, RelationshipType } from './Relationship.js';
54
55
  import { optimizeImage } from '../images/ImageOptimizer.js';
55
56
  export class Document {
56
57
  constructor(zipHandler, options = {}, initDefaults = true) {
@@ -75,6 +76,9 @@ export class Document {
75
76
  this.bookmarkManager.setIdProvider(() => this.documentIdManager.getNextId(), (existingId) => this.documentIdManager.ensureNextIdAbove(existingId));
76
77
  this.revisionManager.setIdProvider(() => this.documentIdManager.getNextId(), (existingId) => this.documentIdManager.ensureNextIdAbove(existingId));
77
78
  this.commentManager.setIdProvider(() => this.documentIdManager.getNextId(), (existingId) => this.documentIdManager.ensureNextIdAbove(existingId));
79
+ this.commentManager.setModifiedNotifier(() => {
80
+ this._commentsModified = true;
81
+ });
78
82
  this.trackingContext = new DocumentTrackingContext(this.revisionManager);
79
83
  this.parser = new DocumentParser();
80
84
  this.generator = new DocumentGenerator();
@@ -129,6 +133,7 @@ export class Document {
129
133
  _originalNumberingXml;
130
134
  _originalSettingsXml;
131
135
  _originalAppPropsXml;
136
+ _originalCorePropsXml;
132
137
  _originalFootnotesXml;
133
138
  _originalEndnotesXml;
134
139
  _originalCommentsXml;
@@ -138,6 +143,7 @@ export class Document {
138
143
  _saveQueue = Promise.resolve();
139
144
  _removedParts = new Set();
140
145
  _appPropsModified = false;
146
+ _corePropsModified = false;
141
147
  _footnotesModified = false;
142
148
  _endnotesModified = false;
143
149
  _settingsModified = false;
@@ -337,7 +343,7 @@ export class Document {
337
343
  const logger = getLogger();
338
344
  logger.info('Loading document from file', { path: filePath });
339
345
  const zipHandler = new ZipHandler();
340
- await zipHandler.load(filePath);
346
+ await zipHandler.load(filePath, { sizeLimits: options?.sizeLimits });
341
347
  const doc = await Document.initializeFromZip(zipHandler, options);
342
348
  doc._events.emit('afterLoad', { source: 'file', path: filePath });
343
349
  return doc;
@@ -346,7 +352,7 @@ export class Document {
346
352
  const logger = getLogger();
347
353
  logger.info('Loading document from buffer', { bufferSize: buffer.length });
348
354
  const zipHandler = new ZipHandler();
349
- await zipHandler.loadFromBuffer(buffer);
355
+ await zipHandler.loadFromBuffer(buffer, { sizeLimits: options?.sizeLimits });
350
356
  const doc = await Document.initializeFromZip(zipHandler, options);
351
357
  doc._events.emit('afterLoad', { source: 'buffer' });
352
358
  return doc;
@@ -354,12 +360,20 @@ export class Document {
354
360
  static async initializeFromZip(zipHandler, options) {
355
361
  const logger = getLogger();
356
362
  const useInMemoryAccept = options?.acceptRevisions === true;
363
+ if (useInMemoryAccept && options?.revisionHandling === 'reject') {
364
+ throw new Error("Conflicting load options: 'acceptRevisions: true' (in-memory accept) cannot be combined " +
365
+ 'with \'revisionHandling: "reject"\'. Use \'revisionHandling: "reject"\' on its own to ' +
366
+ 'revert the document to its original pre-edit state.');
367
+ }
357
368
  const revisionHandling = useInMemoryAccept
358
369
  ? 'preserve'
359
370
  : (options?.revisionHandling ?? 'accept');
360
371
  if (revisionHandling === 'accept') {
361
372
  await acceptAllRevisions(zipHandler);
362
373
  }
374
+ else if (revisionHandling === 'reject') {
375
+ await rejectAllRevisions(zipHandler);
376
+ }
363
377
  else if (revisionHandling === 'strip') {
364
378
  await stripTrackedChanges(zipHandler);
365
379
  }
@@ -386,7 +400,7 @@ export class Document {
386
400
  }
387
401
  const numberingXml = zipHandler.getFileAsString(DOCX_PATHS.NUMBERING);
388
402
  if (numberingXml) {
389
- doc._originalNumberingXml = numberingXml;
403
+ doc._originalNumberingXml = reorderRunPropertyChildren(numberingXml);
390
404
  }
391
405
  const settingsXml = zipHandler.getFileAsString(DOCX_PATHS.SETTINGS);
392
406
  if (settingsXml) {
@@ -397,6 +411,10 @@ export class Document {
397
411
  if (appPropsXml) {
398
412
  doc._originalAppPropsXml = appPropsXml;
399
413
  }
414
+ const corePropsXml = zipHandler.getFileAsString(DOCX_PATHS.CORE_PROPS);
415
+ if (corePropsXml) {
416
+ doc._originalCorePropsXml = corePropsXml;
417
+ }
400
418
  const webSettingsXml = zipHandler.getFileAsString(DOCX_PATHS.WEB_SETTINGS);
401
419
  if (webSettingsXml) {
402
420
  doc._originalWebSettingsXml = webSettingsXml;
@@ -659,17 +677,17 @@ export class Document {
659
677
  try {
660
678
  const headersFooters = await this.parser.parseHeadersAndFooters(this.zipHandler, result.section, this.relationshipManager, this.imageManager);
661
679
  const registeredHeaderRIds = new Set();
662
- for (const { header, relationshipId } of headersFooters.headers) {
680
+ for (const { header, relationshipId, filename } of headersFooters.headers) {
663
681
  if (registeredHeaderRIds.has(relationshipId))
664
682
  continue;
665
- this.headerFooterManager.registerHeader(header, relationshipId);
683
+ this.headerFooterManager.registerHeader(header, relationshipId, filename);
666
684
  registeredHeaderRIds.add(relationshipId);
667
685
  }
668
686
  const registeredFooterRIds = new Set();
669
- for (const { footer, relationshipId } of headersFooters.footers) {
687
+ for (const { footer, relationshipId, filename } of headersFooters.footers) {
670
688
  if (registeredFooterRIds.has(relationshipId))
671
689
  continue;
672
- this.headerFooterManager.registerFooter(footer, relationshipId);
690
+ this.headerFooterManager.registerFooter(footer, relationshipId, filename);
673
691
  registeredFooterRIds.add(relationshipId);
674
692
  }
675
693
  }
@@ -712,7 +730,7 @@ export class Document {
712
730
  }
713
731
  }
714
732
  const parser = new DocumentParser();
715
- const comments = parser.parseCommentsXml(commentsXml);
733
+ const comments = parser.parseCommentsXml(commentsXml, this._originalCommentCompanionFiles.get(DOCX_PATHS.COMMENTS_EXTENDED));
716
734
  for (const comment of comments) {
717
735
  this.commentManager.registerExisting(comment);
718
736
  }
@@ -726,7 +744,9 @@ export class Document {
726
744
  this._originalFootnotesXml = footnotesXml;
727
745
  try {
728
746
  const parser = new DocumentParser();
729
- const footnotes = parser.parseFootnotesXml(footnotesXml);
747
+ const relsXml = this.zipHandler.getFileAsString('word/_rels/footnotes.xml.rels');
748
+ const partRels = relsXml ? RelationshipManager.fromXml(relsXml) : undefined;
749
+ const footnotes = parser.parseFootnotesXml(footnotesXml, partRels);
730
750
  for (const footnote of footnotes) {
731
751
  const id = footnote.getId();
732
752
  if (!this.footnoteManager.hasFootnote(id)) {
@@ -748,7 +768,9 @@ export class Document {
748
768
  this._originalEndnotesXml = endnotesXml;
749
769
  try {
750
770
  const parser = new DocumentParser();
751
- const endnotes = parser.parseEndnotesXml(endnotesXml);
771
+ const relsXml = this.zipHandler.getFileAsString('word/_rels/endnotes.xml.rels');
772
+ const partRels = relsXml ? RelationshipManager.fromXml(relsXml) : undefined;
773
+ const endnotes = parser.parseEndnotesXml(endnotesXml, partRels);
752
774
  for (const endnote of endnotes) {
753
775
  const id = endnote.getId();
754
776
  if (!this.endnoteManager.hasEndnote(id)) {
@@ -840,12 +862,13 @@ export class Document {
840
862
  paragraph._setStylesManager(this.stylesManager);
841
863
  if (this.trackChangesEnabled && this.trackingContext.isEnabled()) {
842
864
  this.bindTrackingToElement(paragraph);
843
- const runs = paragraph.getRuns();
844
- if (runs.length > 0) {
845
- const author = this.trackingContext.getAuthor();
846
- const insertion = Revision.createInsertion(author, runs);
847
- this.trackingContext.getRevisionManager().register(insertion);
848
- paragraph.addRevision(insertion);
865
+ const author = this.trackingContext.getAuthor();
866
+ for (const item of paragraph.getContent()) {
867
+ if (item instanceof Run || item instanceof Hyperlink) {
868
+ const insertion = Revision.createInsertion(author, item);
869
+ this.trackingContext.getRevisionManager().register(insertion);
870
+ paragraph.replaceContent(item, [insertion]);
871
+ }
849
872
  }
850
873
  }
851
874
  this.bodyElements.push(paragraph);
@@ -1061,14 +1084,17 @@ export class Document {
1061
1084
  }
1062
1085
  setTitle(title) {
1063
1086
  this.properties.title = title;
1087
+ this._corePropsModified = true;
1064
1088
  return this;
1065
1089
  }
1066
1090
  setSubject(subject) {
1067
1091
  this.properties.subject = subject;
1092
+ this._corePropsModified = true;
1068
1093
  return this;
1069
1094
  }
1070
1095
  setCreator(creator) {
1071
1096
  this.properties.creator = creator;
1097
+ this._corePropsModified = true;
1072
1098
  return this;
1073
1099
  }
1074
1100
  setAuthor(author) {
@@ -1076,18 +1102,22 @@ export class Document {
1076
1102
  }
1077
1103
  setKeywords(keywords) {
1078
1104
  this.properties.keywords = keywords;
1105
+ this._corePropsModified = true;
1079
1106
  return this;
1080
1107
  }
1081
1108
  setDescription(description) {
1082
1109
  this.properties.description = description;
1110
+ this._corePropsModified = true;
1083
1111
  return this;
1084
1112
  }
1085
1113
  setCategory(category) {
1086
1114
  this.properties.category = category;
1115
+ this._corePropsModified = true;
1087
1116
  return this;
1088
1117
  }
1089
1118
  setContentStatus(status) {
1090
1119
  this.properties.contentStatus = status;
1120
+ this._corePropsModified = true;
1091
1121
  return this;
1092
1122
  }
1093
1123
  setApplication(application) {
@@ -1534,8 +1564,48 @@ export class Document {
1534
1564
  if (this._removedParts.has(DOCX_PATHS.CORE_PROPS) ||
1535
1565
  this._removedParts.has('docProps/core.xml'))
1536
1566
  return;
1537
- const xml = this.generator.generateCoreProps(this.properties);
1538
- this.zipHandler.updateFile(DOCX_PATHS.CORE_PROPS, xml);
1567
+ if (this._originalCorePropsXml && !this._corePropsModified) {
1568
+ return;
1569
+ }
1570
+ if (this._originalCorePropsXml && this._corePropsModified) {
1571
+ const mergedXml = this.mergeCorePropsWithOriginal();
1572
+ this.zipHandler.updateFile(DOCX_PATHS.CORE_PROPS, mergedXml);
1573
+ }
1574
+ else {
1575
+ const xml = this.generator.generateCoreProps(this.properties);
1576
+ this.zipHandler.updateFile(DOCX_PATHS.CORE_PROPS, xml);
1577
+ }
1578
+ }
1579
+ mergeCorePropsWithOriginal() {
1580
+ if (!this._originalCorePropsXml) {
1581
+ return this.generator.generateCoreProps(this.properties);
1582
+ }
1583
+ let xml = this._originalCorePropsXml;
1584
+ const mergeField = (tag, value) => {
1585
+ if (value === undefined)
1586
+ return;
1587
+ const escaped = XMLBuilder.sanitizeXmlContent(value);
1588
+ const openClose = new RegExp(`<${tag}\\b[^>]*>[\\s\\S]*?</${tag}>`);
1589
+ const selfClosing = new RegExp(`<${tag}\\b[^>]*/>`);
1590
+ if (openClose.test(xml)) {
1591
+ xml = xml.replace(openClose, `<${tag}>${escaped}</${tag}>`);
1592
+ }
1593
+ else if (selfClosing.test(xml)) {
1594
+ xml = xml.replace(selfClosing, `<${tag}>${escaped}</${tag}>`);
1595
+ }
1596
+ else {
1597
+ xml = xml.replace(/<\/cp:coreProperties>/, ` <${tag}>${escaped}</${tag}>\n</cp:coreProperties>`);
1598
+ }
1599
+ };
1600
+ mergeField('dc:title', this.properties.title);
1601
+ mergeField('dc:subject', this.properties.subject);
1602
+ mergeField('dc:creator', this.properties.creator);
1603
+ mergeField('cp:keywords', this.properties.keywords);
1604
+ mergeField('dc:description', this.properties.description);
1605
+ mergeField('cp:category', this.properties.category);
1606
+ mergeField('cp:contentStatus', this.properties.contentStatus);
1607
+ mergeField('dc:language', this.properties.language);
1608
+ return xml;
1539
1609
  }
1540
1610
  updateAppProps() {
1541
1611
  if (this._removedParts.has(DOCX_PATHS.APP_PROPS) || this._removedParts.has('docProps/app.xml'))
@@ -1565,24 +1635,36 @@ export class Document {
1565
1635
  else if (xml.includes('<Company/>')) {
1566
1636
  xml = xml.replace(/<Company\/>/, `<Company>${escaped}</Company>`);
1567
1637
  }
1638
+ else {
1639
+ xml = xml.replace(/<\/Properties>/, ` <Company>${escaped}</Company>\n</Properties>`);
1640
+ }
1568
1641
  }
1569
1642
  if (this.properties.application !== undefined) {
1570
1643
  const escaped = XMLBuilder.sanitizeXmlContent(this.properties.application);
1571
1644
  if (xml.includes('<Application>')) {
1572
1645
  xml = xml.replace(/<Application>[^<]*<\/Application>/, `<Application>${escaped}</Application>`);
1573
1646
  }
1647
+ else {
1648
+ xml = xml.replace(/<\/Properties>/, ` <Application>${escaped}</Application>\n</Properties>`);
1649
+ }
1574
1650
  }
1575
1651
  if (this.properties.appVersion !== undefined) {
1576
1652
  const escaped = XMLBuilder.sanitizeXmlContent(this.properties.appVersion);
1577
1653
  if (xml.includes('<AppVersion>')) {
1578
1654
  xml = xml.replace(/<AppVersion>[^<]*<\/AppVersion>/, `<AppVersion>${escaped}</AppVersion>`);
1579
1655
  }
1656
+ else {
1657
+ xml = xml.replace(/<\/Properties>/, ` <AppVersion>${escaped}</AppVersion>\n</Properties>`);
1658
+ }
1580
1659
  }
1581
1660
  if (this.properties.manager !== undefined) {
1582
1661
  const escaped = XMLBuilder.sanitizeXmlContent(this.properties.manager);
1583
1662
  if (xml.includes('<Manager>')) {
1584
1663
  xml = xml.replace(/<Manager>[^<]*<\/Manager>/, `<Manager>${escaped}</Manager>`);
1585
1664
  }
1665
+ else {
1666
+ xml = xml.replace(/<\/Properties>/, ` <Manager>${escaped}</Manager>\n</Properties>`);
1667
+ }
1586
1668
  }
1587
1669
  return xml;
1588
1670
  }
@@ -1708,7 +1790,8 @@ export class Document {
1708
1790
  return this.stylesManager.generateStylesXml();
1709
1791
  }
1710
1792
  const modifiedStyleIds = this.stylesManager.getModifiedStyleIds();
1711
- if (modifiedStyleIds.size === 0) {
1793
+ const removedStyleIds = this.stylesManager.getRemovedStyleIds();
1794
+ if (modifiedStyleIds.size === 0 && removedStyleIds.size === 0) {
1712
1795
  return this._originalStylesXml;
1713
1796
  }
1714
1797
  const modifiedStyles = new Map();
@@ -1730,6 +1813,11 @@ export class Document {
1730
1813
  resultXml = resultXml.replace('</w:styles>', `${newStyleXml}\n</w:styles>`);
1731
1814
  }
1732
1815
  }
1816
+ for (const styleId of removedStyleIds) {
1817
+ const escapedStyleId = styleId.replace(/[.*+?^${}()|[\]\\"]/g, '\\$&');
1818
+ const removedPattern = new RegExp(`\\s*<w:style[^>]*\\sw:styleId="${escapedStyleId}"[^>]*>[\\s\\S]*?</w:style>`);
1819
+ resultXml = resultXml.replace(removedPattern, '');
1820
+ }
1733
1821
  return resultXml;
1734
1822
  }
1735
1823
  updateNumberingXml() {
@@ -1928,8 +2016,14 @@ export class Document {
1928
2016
  xml = xml.replace(/<w:revisionView\b[^>]*>[\s\S]*?<\/w:revisionView>/g, '');
1929
2017
  let trackBlock = '';
1930
2018
  const view = this.revisionViewSettings;
1931
- if (!view.showInsertionsAndDeletions || !view.showFormatting || !view.showInkAnnotations) {
1932
- trackBlock += `\n <w:revisionView w:insDel="${view.showInsertionsAndDeletions ? '1' : '0'}" w:formatting="${view.showFormatting ? '1' : '0'}" w:inkAnnotations="${view.showInkAnnotations ? '1' : '0'}"/>`;
2019
+ if (!view.showInsertionsAndDeletions ||
2020
+ !view.showFormatting ||
2021
+ !view.showInkAnnotations ||
2022
+ view.showMarkup === false ||
2023
+ view.showComments === false) {
2024
+ const markupAttr = view.showMarkup === false ? ' w:markup="0"' : '';
2025
+ const commentsAttr = view.showComments === false ? ' w:comments="0"' : '';
2026
+ trackBlock += `\n <w:revisionView w:insDel="${view.showInsertionsAndDeletions ? '1' : '0'}" w:formatting="${view.showFormatting ? '1' : '0'}" w:inkAnnotations="${view.showInkAnnotations ? '1' : '0'}"${markupAttr}${commentsAttr}/>`;
1933
2027
  }
1934
2028
  if (this.trackChangesEnabled) {
1935
2029
  trackBlock += '\n <w:trackRevisions/>';
@@ -1965,6 +2059,8 @@ export class Document {
1965
2059
  const prot = this.documentProtection;
1966
2060
  const esc = XMLBuilder.escapeXmlAttribute;
1967
2061
  let protXml = `\n <w:documentProtection w:edit="${esc(prot.edit)}" w:enforcement="${prot.enforcement ? '1' : '0'}"`;
2062
+ if (prot.formatting !== undefined)
2063
+ protXml += ` w:formatting="${prot.formatting ? '1' : '0'}"`;
1968
2064
  if (prot.cryptProviderType)
1969
2065
  protXml += ` w:cryptProviderType="${esc(prot.cryptProviderType)}"`;
1970
2066
  if (prot.cryptAlgorithmClass)
@@ -1979,6 +2075,12 @@ export class Document {
1979
2075
  protXml += ` w:hash="${esc(prot.hash)}"`;
1980
2076
  if (prot.salt)
1981
2077
  protXml += ` w:salt="${esc(prot.salt)}"`;
2078
+ if (prot.algorithmName)
2079
+ protXml += ` w:algorithmName="${esc(prot.algorithmName)}"`;
2080
+ if (prot.hashValue)
2081
+ protXml += ` w:hashValue="${esc(prot.hashValue)}"`;
2082
+ if (prot.saltValue)
2083
+ protXml += ` w:saltValue="${esc(prot.saltValue)}"`;
1982
2084
  protXml += '/>';
1983
2085
  if (/<w:autoFormatOverride\b/.test(xml)) {
1984
2086
  xml = xml.replace(/<w:autoFormatOverride\b/, protXml + '\n <w:autoFormatOverride');
@@ -2732,6 +2834,18 @@ export class Document {
2732
2834
  }
2733
2835
  }
2734
2836
  }
2837
+ for (const style of this.stylesManager.getAllStyles()) {
2838
+ const styleNumId = style.getProperties().numPr?.numId;
2839
+ if (styleNumId !== undefined) {
2840
+ usedNumIds.add(styleNumId);
2841
+ }
2842
+ }
2843
+ for (const filePath of [DOCX_PATHS.STYLES, 'word/comments.xml']) {
2844
+ const xml = this.zipHandler.getFileAsString(filePath);
2845
+ if (xml) {
2846
+ this.collectNumIdsFromXml(xml, usedNumIds);
2847
+ }
2848
+ }
2735
2849
  this.numberingManager.cleanupUnusedNumbering(usedNumIds);
2736
2850
  }
2737
2851
  validateNumberingReferences() {
@@ -2833,7 +2947,67 @@ export class Document {
2833
2947
  maxRevId = rev.getId();
2834
2948
  }
2835
2949
  const registeredSet = new Set(existingRevisions);
2836
- let docPrId = 1;
2950
+ let maxPreservedDocPrId = 0;
2951
+ const collectPreservedDocPrIds = (rawXml) => {
2952
+ if (!rawXml || !rawXml.includes('wp:docPr'))
2953
+ return;
2954
+ const docPrPattern = /<wp:docPr\b[^>]*\bid="(\d+)"/g;
2955
+ let match;
2956
+ while ((match = docPrPattern.exec(rawXml)) !== null) {
2957
+ const id = parseInt(match[1], 10);
2958
+ if (id > maxPreservedDocPrId)
2959
+ maxPreservedDocPrId = id;
2960
+ }
2961
+ };
2962
+ const collectFromRun = (run) => {
2963
+ if (run instanceof ImageRun) {
2964
+ collectPreservedDocPrIds(run.getRawRunXml());
2965
+ return;
2966
+ }
2967
+ for (const contentItem of run.getContent()) {
2968
+ collectPreservedDocPrIds(contentItem.rawXml);
2969
+ }
2970
+ };
2971
+ const collectFromParagraph = (para) => {
2972
+ for (const item of para.getContent()) {
2973
+ if (item instanceof Revision) {
2974
+ for (const revItem of item.getContent()) {
2975
+ if (revItem instanceof Run)
2976
+ collectFromRun(revItem);
2977
+ }
2978
+ }
2979
+ else if (item instanceof Run) {
2980
+ collectFromRun(item);
2981
+ }
2982
+ else if (item instanceof PreservedElement) {
2983
+ collectPreservedDocPrIds(item.getRawXml());
2984
+ }
2985
+ }
2986
+ };
2987
+ for (const element of this.bodyElements) {
2988
+ if (element instanceof Paragraph) {
2989
+ collectFromParagraph(element);
2990
+ }
2991
+ else if (element instanceof Table) {
2992
+ for (const row of element.getRows()) {
2993
+ for (const cell of row.getCells()) {
2994
+ for (const para of cell.getParagraphs()) {
2995
+ collectFromParagraph(para);
2996
+ }
2997
+ for (const nested of cell.getRawNestedContent()) {
2998
+ collectPreservedDocPrIds(nested.xml);
2999
+ }
3000
+ }
3001
+ }
3002
+ }
3003
+ else {
3004
+ const passthrough = element;
3005
+ if (typeof passthrough.getRawXml === 'function') {
3006
+ collectPreservedDocPrIds(passthrough.getRawXml());
3007
+ }
3008
+ }
3009
+ }
3010
+ let docPrId = maxPreservedDocPrId + 1;
2837
3011
  const existingParaIds = new Set();
2838
3012
  const paragraphsNeedingIds = [];
2839
3013
  const generateUniqueParaId = () => {
@@ -2865,6 +3039,13 @@ export class Document {
2865
3039
  else if (item instanceof ImageRun) {
2866
3040
  item.getImageElement().setDocPrId(docPrId++);
2867
3041
  }
3042
+ else if (item instanceof Revision) {
3043
+ for (const revItem of item.getContent()) {
3044
+ if (revItem instanceof ImageRun) {
3045
+ revItem.getImageElement().setDocPrId(docPrId++);
3046
+ }
3047
+ }
3048
+ }
2868
3049
  }
2869
3050
  if (para.formatting.paraId) {
2870
3051
  existingParaIds.add(para.formatting.paraId);
@@ -3154,6 +3335,7 @@ export class Document {
3154
3335
  numLevel.setLeftIndent(720 * (levelIndex + 1));
3155
3336
  numLevel.setHangingIndent(360);
3156
3337
  }
3338
+ this.numberingManager.markAbstractNumberingModified(abstractNumId);
3157
3339
  this.applyFormattingToListParagraphs(instance.getNumId());
3158
3340
  count++;
3159
3341
  }
@@ -3180,6 +3362,7 @@ export class Document {
3180
3362
  numLevel.setHangingIndent(360);
3181
3363
  numLevel.setAlignment('left');
3182
3364
  }
3365
+ this.numberingManager.markAbstractNumberingModified(abstractNumId);
3183
3366
  this.applyFormattingToListParagraphs(instance.getNumId());
3184
3367
  count++;
3185
3368
  }
@@ -5002,39 +5185,39 @@ export class Document {
5002
5185
  return this;
5003
5186
  }
5004
5187
  setHeader(header) {
5005
- const relationship = this.relationshipManager.addHeader(`${header.getFilename(1)}`);
5188
+ const relationship = this.relationshipManager.addHeader(this.headerFooterManager.peekHeaderFilename(header));
5006
5189
  this.headerFooterManager.registerHeader(header, relationship.getId());
5007
5190
  this.section.setHeaderReference('default', relationship.getId());
5008
5191
  return this;
5009
5192
  }
5010
5193
  setFirstPageHeader(header) {
5011
5194
  this.section.setTitlePage(true);
5012
- const relationship = this.relationshipManager.addHeader(`${header.getFilename(this.headerFooterManager.getHeaderCount() + 1)}`);
5195
+ const relationship = this.relationshipManager.addHeader(this.headerFooterManager.peekHeaderFilename(header));
5013
5196
  this.headerFooterManager.registerHeader(header, relationship.getId());
5014
5197
  this.section.setHeaderReference('first', relationship.getId());
5015
5198
  return this;
5016
5199
  }
5017
5200
  setEvenPageHeader(header) {
5018
- const relationship = this.relationshipManager.addHeader(`${header.getFilename(this.headerFooterManager.getHeaderCount() + 1)}`);
5201
+ const relationship = this.relationshipManager.addHeader(this.headerFooterManager.peekHeaderFilename(header));
5019
5202
  this.headerFooterManager.registerHeader(header, relationship.getId());
5020
5203
  this.section.setHeaderReference('even', relationship.getId());
5021
5204
  return this;
5022
5205
  }
5023
5206
  setFooter(footer) {
5024
- const relationship = this.relationshipManager.addFooter(`${footer.getFilename(1)}`);
5207
+ const relationship = this.relationshipManager.addFooter(this.headerFooterManager.peekFooterFilename(footer));
5025
5208
  this.headerFooterManager.registerFooter(footer, relationship.getId());
5026
5209
  this.section.setFooterReference('default', relationship.getId());
5027
5210
  return this;
5028
5211
  }
5029
5212
  setFirstPageFooter(footer) {
5030
5213
  this.section.setTitlePage(true);
5031
- const relationship = this.relationshipManager.addFooter(`${footer.getFilename(this.headerFooterManager.getFooterCount() + 1)}`);
5214
+ const relationship = this.relationshipManager.addFooter(this.headerFooterManager.peekFooterFilename(footer));
5032
5215
  this.headerFooterManager.registerFooter(footer, relationship.getId());
5033
5216
  this.section.setFooterReference('first', relationship.getId());
5034
5217
  return this;
5035
5218
  }
5036
5219
  setEvenPageFooter(footer) {
5037
- const relationship = this.relationshipManager.addFooter(`${footer.getFilename(this.headerFooterManager.getFooterCount() + 1)}`);
5220
+ const relationship = this.relationshipManager.addFooter(this.headerFooterManager.peekFooterFilename(footer));
5038
5221
  this.headerFooterManager.registerFooter(footer, relationship.getId());
5039
5222
  this.section.setFooterReference('even', relationship.getId());
5040
5223
  return this;
@@ -5180,6 +5363,20 @@ export class Document {
5180
5363
  }
5181
5364
  }
5182
5365
  this.removeCommentCompanionFiles();
5366
+ const extendedXml = this.commentManager.generateCommentsExtendedXml();
5367
+ if (extendedXml) {
5368
+ this.zipHandler.addFile(DOCX_PATHS.COMMENTS_EXTENDED, extendedXml);
5369
+ const hasExtendedRel = this.relationshipManager
5370
+ .getAllRelationships()
5371
+ .some((rel) => rel.getTarget() === 'commentsExtended.xml');
5372
+ if (!hasExtendedRel) {
5373
+ this.relationshipManager.addRelationship(Relationship.create({
5374
+ id: this.relationshipManager.generateId(),
5375
+ type: 'http://schemas.microsoft.com/office/2011/relationships/commentsExtended',
5376
+ target: 'commentsExtended.xml',
5377
+ }));
5378
+ }
5379
+ }
5183
5380
  }
5184
5381
  else if (this._originalCommentsXml) {
5185
5382
  this.zipHandler.addFile('word/comments.xml', this._originalCommentsXml);
@@ -5217,7 +5414,8 @@ export class Document {
5217
5414
  }
5218
5415
  }
5219
5416
  saveFootnotes() {
5220
- if (this._footnotesModified || this.footnoteManager.getCount() > 0) {
5417
+ if (this._footnotesModified ||
5418
+ (!this._originalFootnotesXml && this.footnoteManager.getCount() > 0)) {
5221
5419
  const xml = this.footnoteManager.generateFootnotesXml();
5222
5420
  this.zipHandler.addFile(DOCX_PATHS.FOOTNOTES, xml);
5223
5421
  const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.FOOTNOTES);
@@ -5235,7 +5433,8 @@ export class Document {
5235
5433
  }
5236
5434
  }
5237
5435
  saveEndnotes() {
5238
- if (this._endnotesModified || this.endnoteManager.getCount() > 0) {
5436
+ if (this._endnotesModified ||
5437
+ (!this._originalEndnotesXml && this.endnoteManager.getCount() > 0)) {
5239
5438
  const xml = this.endnoteManager.generateEndnotesXml();
5240
5439
  this.zipHandler.addFile(DOCX_PATHS.ENDNOTES, xml);
5241
5440
  const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.ENDNOTES);
@@ -5304,7 +5503,12 @@ export class Document {
5304
5503
  xml += '</Relationships>';
5305
5504
  this.zipHandler.addFile(relsPath, xml);
5306
5505
  for (const rel of hyperlinkRels) {
5307
- this.relationshipManager.removeRelationship(rel.id);
5506
+ const mainRel = this.relationshipManager.getRelationship(rel.id);
5507
+ if (mainRel &&
5508
+ mainRel.getType() === RelationshipType.HYPERLINK &&
5509
+ mainRel.getTarget() === rel.url) {
5510
+ this.relationshipManager.removeRelationship(rel.id);
5511
+ }
5308
5512
  }
5309
5513
  }
5310
5514
  }
@@ -5422,13 +5626,25 @@ export class Document {
5422
5626
  parseContentTypes(xml) {
5423
5627
  const defaults = new Set();
5424
5628
  const overrides = new Set();
5425
- const defaultMatches = xml.matchAll(/<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"\s*\/>/g);
5426
- for (const match of defaultMatches) {
5427
- defaults.add(`${match[1]}|${match[2]}`);
5629
+ const readAttr = (tag, name) => {
5630
+ const match = tag.match(new RegExp(`(?:^|\\s)${name}\\s*=\\s*("([^"]*)"|'([^']*)')`));
5631
+ if (!match)
5632
+ return undefined;
5633
+ return XMLBuilder.unescapeXml(match[2] ?? match[3] ?? '');
5634
+ };
5635
+ for (const tagMatch of xml.matchAll(/<Default\b[^>]*>/g)) {
5636
+ const ext = readAttr(tagMatch[0], 'Extension');
5637
+ const contentType = readAttr(tagMatch[0], 'ContentType');
5638
+ if (ext && contentType) {
5639
+ defaults.add(`${ext}|${contentType}`);
5640
+ }
5428
5641
  }
5429
- const overrideMatches = xml.matchAll(/<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"\s*\/>/g);
5430
- for (const match of overrideMatches) {
5431
- overrides.add(`${match[1]}|${match[2]}`);
5642
+ for (const tagMatch of xml.matchAll(/<Override\b[^>]*>/g)) {
5643
+ const partName = readAttr(tagMatch[0], 'PartName');
5644
+ const contentType = readAttr(tagMatch[0], 'ContentType');
5645
+ if (partName && contentType) {
5646
+ overrides.add(`${partName}|${contentType}`);
5647
+ }
5432
5648
  }
5433
5649
  return { defaults, overrides };
5434
5650
  }
@@ -5748,9 +5964,7 @@ export class Document {
5748
5964
  }
5749
5965
  if (element && typeof element.getContent === 'function') {
5750
5966
  for (const item of element.getContent()) {
5751
- if (item &&
5752
- typeof item._setTrackingContext === 'function' &&
5753
- typeof item.getInstruction === 'function') {
5967
+ if (item && typeof item._setTrackingContext === 'function') {
5754
5968
  item._setTrackingContext(this.trackingContext);
5755
5969
  }
5756
5970
  }
@@ -5917,6 +6131,7 @@ export class Document {
5917
6131
  updated.size = sizeInPoints;
5918
6132
  }
5919
6133
  normalStyle.setRunFormatting(updated);
6134
+ this.stylesManager.addStyle(normalStyle);
5920
6135
  return this;
5921
6136
  }
5922
6137
  setDefaultFontSize(sizeInPoints) {
@@ -5932,6 +6147,7 @@ export class Document {
5932
6147
  }
5933
6148
  const existing = normalStyle.getRunFormatting() ?? {};
5934
6149
  normalStyle.setRunFormatting({ ...existing, size: sizeInPoints });
6150
+ this.stylesManager.addStyle(normalStyle);
5935
6151
  return this;
5936
6152
  }
5937
6153
  getUpdateFields() {
@@ -6123,6 +6339,40 @@ export class Document {
6123
6339
  this.endnoteManager.clear();
6124
6340
  this._endnotesModified = true;
6125
6341
  }
6342
+ removeFootnote(id) {
6343
+ const removed = this.footnoteManager.removeFootnote(id);
6344
+ if (removed) {
6345
+ this._footnotesModified = true;
6346
+ this.removeNoteReferenceRuns('footnoteReference', id);
6347
+ }
6348
+ return removed;
6349
+ }
6350
+ removeEndnote(id) {
6351
+ const removed = this.endnoteManager.removeEndnote(id);
6352
+ if (removed) {
6353
+ this._endnotesModified = true;
6354
+ this.removeNoteReferenceRuns('endnoteReference', id);
6355
+ }
6356
+ return removed;
6357
+ }
6358
+ removeNoteReferenceRuns(refType, id) {
6359
+ for (const para of this.getAllParagraphs()) {
6360
+ const content = para.getContent();
6361
+ for (let i = content.length - 1; i >= 0; i--) {
6362
+ const item = content[i];
6363
+ if (!(item instanceof Run)) {
6364
+ continue;
6365
+ }
6366
+ const matches = item
6367
+ .getContent()
6368
+ .some((c) => c.type === refType &&
6369
+ (refType === 'footnoteReference' ? c.footnoteId === id : c.endnoteId === id));
6370
+ if (matches) {
6371
+ para.removeContentAt(i);
6372
+ }
6373
+ }
6374
+ }
6375
+ }
6126
6376
  addCommentToParagraph(paragraph, commentOrAuthor, content, initials) {
6127
6377
  const comment = typeof commentOrAuthor === 'string'
6128
6378
  ? this.createComment(commentOrAuthor, content, initials)
@@ -6173,12 +6423,52 @@ export class Document {
6173
6423
  return this.commentManager.hasReplies(commentId);
6174
6424
  }
6175
6425
  removeComment(id) {
6426
+ const replyIds = this.commentManager.getReplies(id).map((reply) => reply.getId());
6176
6427
  const removed = this.commentManager.removeComment(id);
6177
6428
  if (removed) {
6429
+ this.removeCommentAnchors([id, ...replyIds]);
6178
6430
  this._commentsModified = true;
6179
6431
  }
6180
6432
  return removed;
6181
6433
  }
6434
+ removeCommentAnchors(commentIds) {
6435
+ const strip = (paragraphs) => {
6436
+ let removed = false;
6437
+ for (const paragraph of paragraphs) {
6438
+ for (const commentId of commentIds) {
6439
+ if (paragraph.removeCommentAnchor(commentId)) {
6440
+ removed = true;
6441
+ }
6442
+ }
6443
+ }
6444
+ return removed;
6445
+ };
6446
+ strip(this.getAllParagraphs());
6447
+ for (const entry of this.headerFooterManager.getAllHeaders()) {
6448
+ strip(this.extractParagraphsFromElements(entry.header.getElements()));
6449
+ }
6450
+ for (const entry of this.headerFooterManager.getAllFooters()) {
6451
+ strip(this.extractParagraphsFromElements(entry.footer.getElements()));
6452
+ }
6453
+ let footnotesChanged = false;
6454
+ for (const footnote of this.footnoteManager.getAllFootnotes()) {
6455
+ if (strip(footnote.getParagraphs())) {
6456
+ footnotesChanged = true;
6457
+ }
6458
+ }
6459
+ if (footnotesChanged) {
6460
+ this._footnotesModified = true;
6461
+ }
6462
+ let endnotesChanged = false;
6463
+ for (const endnote of this.endnoteManager.getAllEndnotes()) {
6464
+ if (strip(endnote.getParagraphs())) {
6465
+ endnotesChanged = true;
6466
+ }
6467
+ }
6468
+ if (endnotesChanged) {
6469
+ this._endnotesModified = true;
6470
+ }
6471
+ }
6182
6472
  hasNoRevisions() {
6183
6473
  return this.revisionManager.isEmpty();
6184
6474
  }
@@ -6434,8 +6724,8 @@ export class Document {
6434
6724
  this.parser.clearParseErrors();
6435
6725
  this.stylesManager.clear();
6436
6726
  this.numberingManager.clear();
6437
- this.imageManager.clear();
6438
6727
  this.imageManager.releaseAllImageData();
6728
+ this.imageManager.clear();
6439
6729
  this.relationshipManager.clear();
6440
6730
  this.headerFooterManager.clear();
6441
6731
  this.bookmarkManager.clear();
@@ -6449,6 +6739,7 @@ export class Document {
6449
6739
  this._originalNumberingXml = undefined;
6450
6740
  this._originalSettingsXml = undefined;
6451
6741
  this._originalAppPropsXml = undefined;
6742
+ this._originalCorePropsXml = undefined;
6452
6743
  this._originalFootnotesXml = undefined;
6453
6744
  this._originalEndnotesXml = undefined;
6454
6745
  this._originalCommentsXml = undefined;
@@ -6457,6 +6748,7 @@ export class Document {
6457
6748
  this._originalContentTypes = undefined;
6458
6749
  this._settingsModified = false;
6459
6750
  this._appPropsModified = false;
6751
+ this._corePropsModified = false;
6460
6752
  this._footnotesModified = false;
6461
6753
  this._endnotesModified = false;
6462
6754
  this._originalWebSettingsXml = undefined;
@@ -7014,7 +7306,7 @@ export class Document {
7014
7306
  const matches = originalText.match(wordPattern);
7015
7307
  if (matches) {
7016
7308
  replacementCount += matches.length;
7017
- newText = originalText.replace(wordPattern, replace);
7309
+ newText = originalText.replace(wordPattern, () => replace);
7018
7310
  }
7019
7311
  }
7020
7312
  else {
@@ -7022,7 +7314,7 @@ export class Document {
7022
7314
  const matches = originalText.match(searchPattern);
7023
7315
  if (matches) {
7024
7316
  replacementCount += matches.length;
7025
- newText = originalText.replace(searchPattern, replace);
7317
+ newText = originalText.replace(searchPattern, () => replace);
7026
7318
  }
7027
7319
  }
7028
7320
  if (newText !== originalText) {
@@ -7302,90 +7594,303 @@ export class Document {
7302
7594
  }
7303
7595
  toPlainText(separator = '\n') {
7304
7596
  const paragraphs = this.getAllParagraphs();
7305
- return paragraphs.map((p) => p.getText()).join(separator);
7306
- }
7307
- toMarkdown() {
7597
+ return paragraphs.map((p) => p.getTextIncludingRevisions()).join(separator);
7598
+ }
7599
+ toMarkdown(options) {
7600
+ const ctx = {
7601
+ opts: {
7602
+ htmlFallback: options?.htmlFallback ?? true,
7603
+ footnotes: options?.footnotes ?? true,
7604
+ images: options?.images ?? true,
7605
+ },
7606
+ notes: [],
7607
+ noteMarkers: new Set(),
7608
+ };
7308
7609
  const lines = [];
7309
7610
  for (const element of this.bodyElements) {
7310
- if (element instanceof Paragraph) {
7311
- const mdLine = this.paragraphToMarkdown(element);
7312
- if (mdLine !== null) {
7313
- lines.push(mdLine);
7314
- lines.push('');
7315
- }
7611
+ this.bodyElementToMarkdown(element, ctx, lines);
7612
+ }
7613
+ while (lines.length > 0 && lines[lines.length - 1] === '') {
7614
+ lines.pop();
7615
+ }
7616
+ let out = lines.join('\n');
7617
+ if (ctx.opts.footnotes && ctx.notes.length > 0) {
7618
+ const defs = ctx.notes.map((n) => `[^${n.marker}]: ${n.text}`).join('\n');
7619
+ out = out ? `${out}\n\n${defs}` : defs;
7620
+ }
7621
+ return out;
7622
+ }
7623
+ bodyElementToMarkdown(element, ctx, lines) {
7624
+ if (element instanceof Paragraph) {
7625
+ const md = this.paragraphToMarkdown(element, ctx);
7626
+ if (md !== null) {
7627
+ lines.push(md);
7628
+ lines.push('');
7316
7629
  }
7317
- else if (element instanceof Table) {
7318
- lines.push(...this.tableToMarkdown(element));
7630
+ }
7631
+ else if (element instanceof Table) {
7632
+ const tableLines = this.tableToMarkdown(element, ctx);
7633
+ if (tableLines.length > 0) {
7634
+ lines.push(...tableLines);
7319
7635
  lines.push('');
7320
7636
  }
7321
7637
  }
7322
- while (lines.length > 0 && lines[lines.length - 1] === '') {
7323
- lines.pop();
7638
+ else if (element instanceof StructuredDocumentTag) {
7639
+ for (const child of element.getContent()) {
7640
+ this.bodyElementToMarkdown(child, ctx, lines);
7641
+ }
7642
+ }
7643
+ else if (element instanceof PreservedElement) {
7644
+ const text = this.extractTextFromRawXml(element.getRawXml());
7645
+ if (text) {
7646
+ lines.push(this.escapeMarkdown(text));
7647
+ lines.push('');
7648
+ }
7324
7649
  }
7325
- return lines.join('\n');
7326
7650
  }
7327
- paragraphToMarkdown(para) {
7328
- const text = this.paragraphContentToMarkdown(para);
7329
- if (!text && !para.hasNumbering())
7651
+ paragraphToMarkdown(para, ctx) {
7652
+ const inline = this.paragraphContentToMarkdown(para, ctx);
7653
+ const hasNumbering = para.hasNumbering();
7654
+ if (!inline && !hasNumbering)
7330
7655
  return null;
7331
7656
  const headingLevel = para.detectHeadingLevel();
7332
7657
  if (headingLevel !== null && headingLevel >= 1 && headingLevel <= 6) {
7333
- return '#'.repeat(headingLevel) + ' ' + text;
7334
- }
7335
- if (para.hasNumbering()) {
7336
- const style = para.getStyle();
7337
- const isBullet = style?.toLowerCase().includes('bullet') || style?.toLowerCase().includes('list bullet');
7338
- return isBullet ? `- ${text}` : `1. ${text}`;
7658
+ return '#'.repeat(headingLevel) + ' ' + this.toSingleLine(inline);
7659
+ }
7660
+ const style = (para.getStyle() ?? '').toLowerCase();
7661
+ const isListStyle = style.includes('listbullet') ||
7662
+ style.includes('list bullet') ||
7663
+ style.includes('listnumber') ||
7664
+ style.includes('list number');
7665
+ if (hasNumbering || isListStyle) {
7666
+ const level = para.getNumbering()?.level ?? 0;
7667
+ const indent = ' '.repeat(Math.max(0, level));
7668
+ const marker = this.isOrderedList(para) ? '1.' : '-';
7669
+ return `${indent}${marker} ${this.toSingleLine(inline)}`;
7670
+ }
7671
+ if (style.includes('quote')) {
7672
+ return this.toSingleLine(inline)
7673
+ .split('\n')
7674
+ .map((l) => `> ${l}`)
7675
+ .join('\n');
7676
+ }
7677
+ return inline;
7678
+ }
7679
+ isOrderedList(para) {
7680
+ const numbering = para.getNumbering();
7681
+ if (numbering) {
7682
+ const instance = this.numberingManager.getInstance(numbering.numId);
7683
+ if (instance) {
7684
+ const abstract = this.numberingManager.getAbstractNumbering(instance.getAbstractNumId());
7685
+ const levelDef = abstract?.getLevel(numbering.level);
7686
+ const format = levelDef?.getFormat();
7687
+ if (format)
7688
+ return format !== 'bullet';
7689
+ }
7339
7690
  }
7340
- return text;
7691
+ const style = (para.getStyle() ?? '').toLowerCase();
7692
+ if (style.includes('bullet'))
7693
+ return false;
7694
+ if (style.includes('number'))
7695
+ return true;
7696
+ return true;
7697
+ }
7698
+ paragraphContentToMarkdown(para, ctx) {
7699
+ return this.inlineContentToMarkdown(para.getContent(), ctx);
7341
7700
  }
7342
- paragraphContentToMarkdown(para) {
7701
+ inlineContentToMarkdown(items, ctx) {
7343
7702
  const parts = [];
7344
- for (const item of para.getContent()) {
7345
- if (item instanceof Run) {
7346
- const runText = item.getText();
7347
- if (!runText)
7348
- continue;
7349
- const fmt = item.getFormatting();
7350
- let md = runText;
7351
- if (fmt.bold && fmt.italic) {
7352
- md = `***${md}***`;
7353
- }
7354
- else if (fmt.bold) {
7355
- md = `**${md}**`;
7356
- }
7357
- else if (fmt.italic) {
7358
- md = `*${md}*`;
7703
+ for (const item of items) {
7704
+ if (item instanceof ImageRun) {
7705
+ if (ctx.opts.images) {
7706
+ const image = item.getImageElement();
7707
+ const alt = this.escapeMarkdown(image.getAltText() || 'image');
7708
+ const src = image.getRelationshipId() ?? 'image';
7709
+ parts.push(`![${alt}](${src})`);
7359
7710
  }
7360
- if (fmt.strike) {
7361
- md = `~~${md}~~`;
7711
+ }
7712
+ else if (item instanceof Run) {
7713
+ parts.push(this.runToMarkdown(item, ctx));
7714
+ }
7715
+ else if (item instanceof Hyperlink) {
7716
+ const runs = item.getRuns();
7717
+ let label = runs.length > 0
7718
+ ? runs.map((r) => this.runToMarkdown(r, ctx, true)).join('')
7719
+ : this.escapeMarkdown(item.getText());
7720
+ if (!label)
7721
+ label = this.escapeMarkdown(item.getText());
7722
+ let url = item.getUrl() ?? '';
7723
+ const anchor = item.getAnchor();
7724
+ if (anchor)
7725
+ url = url ? `${url}#${anchor}` : `#${anchor}`;
7726
+ parts.push(`[${label}](${url})`);
7727
+ }
7728
+ else if (item instanceof Revision) {
7729
+ const inner = this.inlineContentToMarkdown(item.getContent(), ctx);
7730
+ if (!inner)
7731
+ continue;
7732
+ const type = item.getType();
7733
+ if (type === 'delete' || type === 'moveFrom') {
7734
+ parts.push(ctx.opts.htmlFallback ? `<del>${inner}</del>` : `~~${inner}~~`);
7362
7735
  }
7363
- if (fmt.font &&
7364
- /^(courier|consolas|monaco|menlo|source code|fira code|jetbrains mono)/i.test(fmt.font)) {
7365
- md = `\`${runText}\``;
7736
+ else {
7737
+ parts.push(inner);
7366
7738
  }
7367
- parts.push(md);
7368
7739
  }
7369
- else if (item instanceof Hyperlink) {
7370
- const url = item.getUrl() || '';
7371
- const linkText = item.getText() || url;
7372
- parts.push(`[${linkText}](${url})`);
7740
+ else if (item instanceof ComplexField) {
7741
+ const result = item.getResult();
7742
+ if (result)
7743
+ parts.push(this.escapeMarkdown(result));
7744
+ }
7745
+ else if (item instanceof Field) {
7746
+ const result = item.getCachedResult();
7747
+ if (result)
7748
+ parts.push(this.escapeMarkdown(result));
7749
+ }
7750
+ else if (item instanceof TextBox) {
7751
+ const text = item
7752
+ .getParagraphs()
7753
+ .map((p) => this.paragraphContentToMarkdown(p, ctx))
7754
+ .filter((s) => s !== '')
7755
+ .join(' ');
7756
+ if (text)
7757
+ parts.push(text);
7758
+ }
7759
+ else if (item instanceof Shape) {
7760
+ const text = item.getText();
7761
+ if (text)
7762
+ parts.push(this.escapeMarkdown(text));
7763
+ }
7764
+ else if (item instanceof PreservedElement) {
7765
+ const type = item.getElementType();
7766
+ if (!type.startsWith('w:comment')) {
7767
+ const text = this.extractTextFromRawXml(item.getRawXml());
7768
+ if (text)
7769
+ parts.push(this.escapeMarkdown(text));
7770
+ }
7373
7771
  }
7374
7772
  }
7375
7773
  return parts.join('');
7376
7774
  }
7377
- tableToMarkdown(table) {
7378
- const data = table.toArray();
7379
- if (data.length === 0)
7775
+ runToMarkdown(run, ctx, inLink = false) {
7776
+ const fmt = run.getFormatting();
7777
+ const isMono = !!(fmt.font &&
7778
+ /^(courier|consolas|monaco|menlo|source code|fira code|jetbrains mono)/i.test(fmt.font));
7779
+ const escape = (s) => (isMono ? s : this.escapeMarkdown(s));
7780
+ const segments = [];
7781
+ const markers = [];
7782
+ for (const content of run.getContent()) {
7783
+ switch (content.type) {
7784
+ case 'text':
7785
+ case 'instructionText':
7786
+ segments.push(escape(content.value ?? ''));
7787
+ break;
7788
+ case 'tab':
7789
+ segments.push('\t');
7790
+ break;
7791
+ case 'carriageReturn':
7792
+ segments.push('\n');
7793
+ break;
7794
+ case 'break':
7795
+ if (content.breakType !== 'page' && content.breakType !== 'column') {
7796
+ segments.push(ctx.opts.htmlFallback ? '<br>' : '\n');
7797
+ }
7798
+ break;
7799
+ case 'noBreakHyphen':
7800
+ segments.push('‑');
7801
+ break;
7802
+ case 'symbol':
7803
+ if (content.symbolChar) {
7804
+ const code = parseInt(content.symbolChar, 16);
7805
+ if (!Number.isNaN(code))
7806
+ segments.push(escape(String.fromCharCode(code)));
7807
+ }
7808
+ break;
7809
+ case 'footnoteReference':
7810
+ if (ctx.opts.footnotes && content.footnoteId !== undefined) {
7811
+ markers.push(this.noteMarker(content.footnoteId, 'fn', ctx));
7812
+ }
7813
+ break;
7814
+ case 'endnoteReference':
7815
+ if (ctx.opts.footnotes && content.endnoteId !== undefined) {
7816
+ markers.push(this.noteMarker(content.endnoteId, 'en', ctx));
7817
+ }
7818
+ break;
7819
+ default:
7820
+ break;
7821
+ }
7822
+ }
7823
+ let text = segments.join('');
7824
+ if (text) {
7825
+ if (isMono) {
7826
+ text = `\`${text}\``;
7827
+ }
7828
+ if (fmt.bold && fmt.italic)
7829
+ text = `***${text}***`;
7830
+ else if (fmt.bold)
7831
+ text = `**${text}**`;
7832
+ else if (fmt.italic)
7833
+ text = `*${text}*`;
7834
+ if (fmt.strike || fmt.dstrike)
7835
+ text = `~~${text}~~`;
7836
+ if (ctx.opts.htmlFallback) {
7837
+ if (fmt.superscript)
7838
+ text = `<sup>${text}</sup>`;
7839
+ else if (fmt.subscript)
7840
+ text = `<sub>${text}</sub>`;
7841
+ if (fmt.highlight && fmt.highlight !== 'none')
7842
+ text = `<mark>${text}</mark>`;
7843
+ if (!inLink) {
7844
+ if (fmt.underline && fmt.underline !== 'none')
7845
+ text = `<u>${text}</u>`;
7846
+ if (fmt.color && fmt.color !== 'auto' && /^[0-9a-fA-F]{6}$/.test(fmt.color)) {
7847
+ text = `<span style="color:#${fmt.color}">${text}</span>`;
7848
+ }
7849
+ }
7850
+ }
7851
+ }
7852
+ return text + markers.join('');
7853
+ }
7854
+ noteMarker(id, kind, ctx) {
7855
+ const marker = `${kind}${id}`;
7856
+ if (!ctx.noteMarkers.has(marker)) {
7857
+ ctx.noteMarkers.add(marker);
7858
+ const note = kind === 'fn' ? this.footnoteManager.getFootnote(id) : this.endnoteManager.getEndnote(id);
7859
+ let text = '';
7860
+ if (note) {
7861
+ text = note
7862
+ .getParagraphs()
7863
+ .map((p) => this.paragraphContentToMarkdown(p, ctx))
7864
+ .filter((s) => s !== '')
7865
+ .join(' ')
7866
+ .replace(/\s*\n\s*/g, ' ')
7867
+ .trim();
7868
+ }
7869
+ ctx.notes.push({ marker, text });
7870
+ }
7871
+ return `[^${marker}]`;
7872
+ }
7873
+ tableToMarkdown(table, ctx) {
7874
+ const rows = table.getRows();
7875
+ if (rows.length === 0)
7380
7876
  return [];
7381
- const colCount = Math.max(...data.map((row) => row.length));
7877
+ const isComplex = rows.some((row) => row
7878
+ .getCells()
7879
+ .some((cell) => (cell.getColumnSpan() ?? 1) > 1 ||
7880
+ cell.getVerticalMerge() !== undefined ||
7881
+ cell.hasNestedTables()));
7882
+ if (isComplex && ctx.opts.htmlFallback) {
7883
+ return this.tableToHtmlBlock(table);
7884
+ }
7885
+ const matrix = rows.map((row) => row.getCells().map((cell) => this.cellToMarkdown(cell, ctx)));
7886
+ const colCount = Math.max(...matrix.map((row) => row.length));
7382
7887
  if (colCount === 0)
7383
7888
  return [];
7384
- const normalized = data.map((row) => {
7889
+ const normalized = matrix.map((row) => {
7385
7890
  const padded = [...row];
7386
7891
  while (padded.length < colCount)
7387
7892
  padded.push('');
7388
- return padded.map((cell) => cell.replace(/\|/g, '\\|').replace(/\n/g, ' ').trim());
7893
+ return padded;
7389
7894
  });
7390
7895
  const lines = [];
7391
7896
  lines.push('| ' + normalized[0].join(' | ') + ' |');
@@ -7395,6 +7900,88 @@ export class Document {
7395
7900
  }
7396
7901
  return lines;
7397
7902
  }
7903
+ cellToMarkdown(cell, ctx) {
7904
+ const text = cell
7905
+ .getParagraphs()
7906
+ .map((p) => this.paragraphContentToMarkdown(p, ctx))
7907
+ .filter((s) => s !== '')
7908
+ .join(' ');
7909
+ return text.replace(/\|/g, '\\|').replace(/<br>/g, ' ').replace(/\n/g, ' ').trim();
7910
+ }
7911
+ tableToHtmlBlock(table) {
7912
+ const rows = table.getRows();
7913
+ const rowCells = rows.map((row) => {
7914
+ let gridStart = 0;
7915
+ return row.getCells().map((cell) => {
7916
+ const span = cell.getColumnSpan() ?? 1;
7917
+ const entry = { cell, gridStart, span };
7918
+ gridStart += span;
7919
+ return entry;
7920
+ });
7921
+ });
7922
+ const html = ['<table>'];
7923
+ for (let ri = 0; ri < rowCells.length; ri++) {
7924
+ html.push('<tr>');
7925
+ for (const { cell, gridStart, span } of rowCells[ri]) {
7926
+ if (cell.getVerticalMerge() === 'continue')
7927
+ continue;
7928
+ let rowspan = 1;
7929
+ if (cell.getVerticalMerge() === 'restart') {
7930
+ for (let rj = ri + 1; rj < rowCells.length; rj++) {
7931
+ const below = rowCells[rj].find((e) => e.gridStart === gridStart);
7932
+ if (below?.cell.getVerticalMerge() === 'continue')
7933
+ rowspan++;
7934
+ else
7935
+ break;
7936
+ }
7937
+ }
7938
+ const tag = ri === 0 ? 'th' : 'td';
7939
+ const attrs = (span > 1 ? ` colspan="${span}"` : '') + (rowspan > 1 ? ` rowspan="${rowspan}"` : '');
7940
+ let content = cell
7941
+ .getParagraphs()
7942
+ .map((p) => this.paragraphContentToHTML(p))
7943
+ .filter((s) => s !== '')
7944
+ .join('<br>') || this.escapeHTML(cell.getText());
7945
+ if (cell.hasNestedTables()) {
7946
+ const nestedText = cell
7947
+ .getRawNestedContent()
7948
+ .map((n) => this.extractTextFromRawXml(n.xml))
7949
+ .filter((s) => s)
7950
+ .join(' ');
7951
+ if (nestedText)
7952
+ content += `<br>${this.escapeHTML(nestedText)}`;
7953
+ }
7954
+ html.push(`<${tag}${attrs}>${content}</${tag}>`);
7955
+ }
7956
+ html.push('</tr>');
7957
+ }
7958
+ html.push('</table>');
7959
+ return html;
7960
+ }
7961
+ escapeMarkdown(text) {
7962
+ return text.replace(/([\\`*_[\]<>])/g, '\\$1');
7963
+ }
7964
+ toSingleLine(text) {
7965
+ return text.replace(/<br>/g, ' ').replace(/\s*\n\s*/g, ' ');
7966
+ }
7967
+ extractTextFromRawXml(xml) {
7968
+ if (!xml.includes('<w:t'))
7969
+ return '';
7970
+ const parts = [];
7971
+ const regex = /<w:t(?:\s[^>]*)?>([\s\S]*?)<\/w:t>/g;
7972
+ let match;
7973
+ while ((match = regex.exec(xml)) !== null) {
7974
+ parts.push(match[1] ?? '');
7975
+ }
7976
+ return parts
7977
+ .join('')
7978
+ .replace(/&lt;/g, '<')
7979
+ .replace(/&gt;/g, '>')
7980
+ .replace(/&quot;/g, '"')
7981
+ .replace(/&apos;/g, "'")
7982
+ .replace(/&amp;/g, '&')
7983
+ .trim();
7984
+ }
7398
7985
  toHTML(options) {
7399
7986
  const parts = [];
7400
7987
  let inList = null;
@@ -7485,8 +8072,12 @@ export class Document {
7485
8072
  parts.push(html);
7486
8073
  }
7487
8074
  else if (item instanceof Hyperlink) {
7488
- const url = this.escapeHTML(item.getUrl() || '');
7489
- const linkText = this.escapeHTML(item.getText() || url);
8075
+ let rawUrl = item.getUrl() ?? '';
8076
+ const anchor = item.getAnchor();
8077
+ if (anchor)
8078
+ rawUrl = rawUrl ? `${rawUrl}#${anchor}` : `#${anchor}`;
8079
+ const url = this.escapeHTML(rawUrl);
8080
+ const linkText = this.escapeHTML(item.getText() || rawUrl);
7490
8081
  parts.push(`<a href="${url}">${linkText}</a>`);
7491
8082
  }
7492
8083
  }
@@ -7634,7 +8225,10 @@ export class Document {
7634
8225
  removeParagraph(paragraphOrIndex) {
7635
8226
  let index;
7636
8227
  if (typeof paragraphOrIndex === 'number') {
7637
- index = paragraphOrIndex;
8228
+ const para = this.getParagraphAt(paragraphOrIndex);
8229
+ if (!para)
8230
+ return false;
8231
+ index = this.bodyElements.indexOf(para);
7638
8232
  }
7639
8233
  else {
7640
8234
  index = this.bodyElements.indexOf(paragraphOrIndex);
@@ -7643,13 +8237,8 @@ export class Document {
7643
8237
  const element = this.bodyElements[index];
7644
8238
  if (element instanceof Paragraph) {
7645
8239
  if (this.trackChangesEnabled && this.trackingContext.isEnabled()) {
7646
- const runs = element.getRuns();
7647
- if (runs.length > 0) {
7648
- const author = this.trackingContext.getAuthor();
7649
- const deletion = Revision.createDeletion(author, runs);
7650
- this.trackingContext.getRevisionManager().register(deletion);
7651
- element.addRevision(deletion);
7652
- }
8240
+ this.bindTrackingToElement(element);
8241
+ element.clearContent();
7653
8242
  return true;
7654
8243
  }
7655
8244
  this.bodyElements.splice(index, 1);
@@ -7903,16 +8492,6 @@ export class Document {
7903
8492
  for (const paragraph of this.getAllParagraphs()) {
7904
8493
  extractHyperlinksFromParagraph(paragraph);
7905
8494
  }
7906
- for (const table of this.getTables()) {
7907
- for (const row of table.getRows()) {
7908
- for (const cell of row.getCells()) {
7909
- const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
7910
- for (const para of cellParagraphs) {
7911
- extractHyperlinksFromParagraph(para);
7912
- }
7913
- }
7914
- }
7915
- }
7916
8495
  return hyperlinks;
7917
8496
  }
7918
8497
  collectAllReferencedHyperlinkIds() {
@@ -8037,17 +8616,6 @@ export class Document {
8037
8616
  bookmarks.push({ bookmark, paragraph });
8038
8617
  }
8039
8618
  }
8040
- for (const table of this.getTables()) {
8041
- for (const row of table.getRows()) {
8042
- for (const cell of row.getCells()) {
8043
- for (const para of cell.getParagraphs()) {
8044
- for (const bookmark of para.getBookmarksStart()) {
8045
- bookmarks.push({ bookmark, paragraph: para });
8046
- }
8047
- }
8048
- }
8049
- }
8050
- }
8051
8619
  return bookmarks;
8052
8620
  }
8053
8621
  getFields() {
@@ -8057,18 +8625,6 @@ export class Document {
8057
8625
  results.push({ field, paragraph });
8058
8626
  }
8059
8627
  }
8060
- for (const table of this.getTables()) {
8061
- for (const row of table.getRows()) {
8062
- for (const cell of row.getCells()) {
8063
- const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
8064
- for (const para of cellParagraphs) {
8065
- for (const field of para.getFields()) {
8066
- results.push({ field, paragraph: para, table });
8067
- }
8068
- }
8069
- }
8070
- }
8071
- }
8072
8628
  return results;
8073
8629
  }
8074
8630
  getImages() {
@@ -8107,7 +8663,7 @@ export class Document {
8107
8663
  await entry.image.updateImageData(result.data);
8108
8664
  }
8109
8665
  if (result.newExtension !== extension) {
8110
- const newFilename = filename.replace(/\.[^.]+$/, `.${result.newExtension}`);
8666
+ const newFilename = this.resolveUniqueMediaFilename(filename.replace(/\.[^.]+$/, `.${result.newExtension}`), filename);
8111
8667
  this.imageManager.updateEntryFilename(image, newFilename);
8112
8668
  for (const entry of entries) {
8113
8669
  this.updateImageRelationshipTarget(entry.relationshipId, filename, newFilename);
@@ -8118,6 +8674,20 @@ export class Document {
8118
8674
  }
8119
8675
  return { optimizedCount, totalSavedBytes: totalSaved };
8120
8676
  }
8677
+ resolveUniqueMediaFilename(candidate, currentFilename) {
8678
+ const dotIndex = candidate.lastIndexOf('.');
8679
+ const base = dotIndex === -1 ? candidate : candidate.slice(0, dotIndex);
8680
+ const ext = dotIndex === -1 ? '' : candidate.slice(dotIndex);
8681
+ const isTaken = (name) => this.zipHandler.hasFile(`word/media/${name}`) ||
8682
+ this.imageManager.isFilenameOwnedByOther(name, currentFilename);
8683
+ let name = candidate;
8684
+ let counter = 1;
8685
+ while (isTaken(name)) {
8686
+ name = `${base}-${counter}${ext}`;
8687
+ counter++;
8688
+ }
8689
+ return name;
8690
+ }
8121
8691
  updateImageRelationshipTarget(relId, oldFilename, newFilename) {
8122
8692
  const rel = this.relationshipManager.getRelationship(relId);
8123
8693
  if (rel) {
@@ -8241,7 +8811,7 @@ export class Document {
8241
8811
  const toRemove = [];
8242
8812
  this.bodyElements.forEach((element, index) => {
8243
8813
  if (element instanceof Paragraph) {
8244
- const isEmpty = element.getText().trim() === '';
8814
+ const isEmpty = this.isParagraphRemovableBlank(element);
8245
8815
  if (isEmpty && lastWasEmpty) {
8246
8816
  toRemove.push(index);
8247
8817
  }
@@ -8325,11 +8895,30 @@ export class Document {
8325
8895
  }
8326
8896
  return { removed, normalized };
8327
8897
  }
8898
+ isParagraphRemovableBlank(para) {
8899
+ if (para.getText().trim() !== '')
8900
+ return false;
8901
+ if (para.formatting.sectPr !== undefined)
8902
+ return false;
8903
+ if (para.getBookmarksStart().length > 0 || para.getBookmarksEnd().length > 0) {
8904
+ return false;
8905
+ }
8906
+ if (para.getCommentsStart().length > 0 || para.getCommentsEnd().length > 0) {
8907
+ return false;
8908
+ }
8909
+ for (const item of para.getContent()) {
8910
+ if (item instanceof ImageRun || item instanceof Shape || item instanceof RangeMarker) {
8911
+ return false;
8912
+ }
8913
+ }
8914
+ return true;
8915
+ }
8328
8916
  setLanguage(language) {
8329
8917
  if (!this.properties) {
8330
8918
  this.properties = {};
8331
8919
  }
8332
8920
  this.properties.language = language;
8921
+ this._corePropsModified = true;
8333
8922
  return this;
8334
8923
  }
8335
8924
  getLanguage() {
@@ -8445,32 +9034,6 @@ export class Document {
8445
9034
  pattern.lastIndex = 0;
8446
9035
  }
8447
9036
  }
8448
- for (const table of this.getTables()) {
8449
- for (const row of table.getRows()) {
8450
- for (const cell of row.getCells()) {
8451
- for (const para of cell.getParagraphs()) {
8452
- for (const run of para.getRuns()) {
8453
- const text = run.getText();
8454
- if (!text)
8455
- continue;
8456
- const runFormatting = run.getFormatting();
8457
- if (matchBold && !runFormatting.bold)
8458
- continue;
8459
- if (matchItalic && !runFormatting.italic)
8460
- continue;
8461
- if (pattern.test(text)) {
8462
- const newText = text.replace(pattern, replace);
8463
- if (newText !== text) {
8464
- run.setText(newText);
8465
- replacedCount++;
8466
- }
8467
- }
8468
- pattern.lastIndex = 0;
8469
- }
8470
- }
8471
- }
8472
- }
8473
- }
8474
9037
  return replacedCount;
8475
9038
  }
8476
9039
  static createEmpty() {