docxmlater 11.0.10 → 12.0.1

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 (354) hide show
  1. package/README.md +51 -13
  2. package/dist/core/Document.d.ts +28 -2
  3. package/dist/core/Document.d.ts.map +1 -1
  4. package/dist/core/Document.js +725 -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 +726 -171
  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 +58 -8
  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/cnfStyleDecoder.js +7 -7
  179. package/dist/esm/processors/cnfStyleDecoder.js.map +1 -1
  180. package/dist/esm/processors/stripTrackedChanges.js +16 -5
  181. package/dist/esm/processors/stripTrackedChanges.js.map +1 -1
  182. package/dist/esm/utils/deepClone.js +24 -11
  183. package/dist/esm/utils/deepClone.js.map +1 -1
  184. package/dist/esm/utils/units.js +1 -1
  185. package/dist/esm/utils/units.js.map +1 -1
  186. package/dist/esm/utils/validation.js +11 -23
  187. package/dist/esm/utils/validation.js.map +1 -1
  188. package/dist/esm/utils/xmlSanitization.js +131 -7
  189. package/dist/esm/utils/xmlSanitization.js.map +1 -1
  190. package/dist/esm/validation/RevisionAutoFixer.js +69 -55
  191. package/dist/esm/validation/RevisionAutoFixer.js.map +1 -1
  192. package/dist/esm/validation/RevisionValidator.js +24 -20
  193. package/dist/esm/validation/RevisionValidator.js.map +1 -1
  194. package/dist/esm/xml/XMLBuilder.js +52 -20
  195. package/dist/esm/xml/XMLBuilder.js.map +1 -1
  196. package/dist/esm/xml/XMLParser.js +55 -13
  197. package/dist/esm/xml/XMLParser.js.map +1 -1
  198. package/dist/esm/zip/ZipHandler.js +37 -13
  199. package/dist/esm/zip/ZipHandler.js.map +1 -1
  200. package/dist/esm/zip/ZipReader.js +53 -6
  201. package/dist/esm/zip/ZipReader.js.map +1 -1
  202. package/dist/esm/zip/errors.js +7 -0
  203. package/dist/esm/zip/errors.js.map +1 -1
  204. package/dist/esm/zip/types.js +4 -0
  205. package/dist/esm/zip/types.js.map +1 -1
  206. package/dist/formatting/AbstractNumbering.d.ts.map +1 -1
  207. package/dist/formatting/AbstractNumbering.js +1 -1
  208. package/dist/formatting/AbstractNumbering.js.map +1 -1
  209. package/dist/formatting/NumberingLevel.d.ts +1 -1
  210. package/dist/formatting/NumberingLevel.d.ts.map +1 -1
  211. package/dist/formatting/NumberingLevel.js +8 -6
  212. package/dist/formatting/NumberingLevel.js.map +1 -1
  213. package/dist/formatting/NumberingManager.d.ts.map +1 -1
  214. package/dist/formatting/NumberingManager.js +16 -1
  215. package/dist/formatting/NumberingManager.js.map +1 -1
  216. package/dist/formatting/Style.d.ts.map +1 -1
  217. package/dist/formatting/Style.js +14 -0
  218. package/dist/formatting/Style.js.map +1 -1
  219. package/dist/formatting/StylesManager.d.ts +2 -0
  220. package/dist/formatting/StylesManager.d.ts.map +1 -1
  221. package/dist/formatting/StylesManager.js +14 -2
  222. package/dist/formatting/StylesManager.js.map +1 -1
  223. package/dist/helpers/CleanupHelper.d.ts.map +1 -1
  224. package/dist/helpers/CleanupHelper.js +103 -26
  225. package/dist/helpers/CleanupHelper.js.map +1 -1
  226. package/dist/images/ImageOptimizer.d.ts.map +1 -1
  227. package/dist/images/ImageOptimizer.js +52 -4
  228. package/dist/images/ImageOptimizer.js.map +1 -1
  229. package/dist/index.d.ts +3 -1
  230. package/dist/index.d.ts.map +1 -1
  231. package/dist/index.js +4 -2
  232. package/dist/index.js.map +1 -1
  233. package/dist/internal.d.ts +1 -1
  234. package/dist/internal.d.ts.map +1 -1
  235. package/dist/internal.js +2 -1
  236. package/dist/internal.js.map +1 -1
  237. package/dist/processors/CompatibilityUpgrader.d.ts.map +1 -1
  238. package/dist/processors/CompatibilityUpgrader.js +7 -1
  239. package/dist/processors/CompatibilityUpgrader.js.map +1 -1
  240. package/dist/processors/InMemoryRevisionAcceptor.d.ts +1 -0
  241. package/dist/processors/InMemoryRevisionAcceptor.d.ts.map +1 -1
  242. package/dist/processors/InMemoryRevisionAcceptor.js +135 -13
  243. package/dist/processors/InMemoryRevisionAcceptor.js.map +1 -1
  244. package/dist/processors/RevisionWalker.d.ts +2 -0
  245. package/dist/processors/RevisionWalker.d.ts.map +1 -1
  246. package/dist/processors/RevisionWalker.js +58 -8
  247. package/dist/processors/RevisionWalker.js.map +1 -1
  248. package/dist/processors/SelectiveRevisionAcceptor.d.ts.map +1 -1
  249. package/dist/processors/SelectiveRevisionAcceptor.js +116 -17
  250. package/dist/processors/SelectiveRevisionAcceptor.js.map +1 -1
  251. package/dist/processors/cnfStyleDecoder.d.ts.map +1 -1
  252. package/dist/processors/cnfStyleDecoder.js +7 -7
  253. package/dist/processors/cnfStyleDecoder.js.map +1 -1
  254. package/dist/processors/stripTrackedChanges.d.ts.map +1 -1
  255. package/dist/processors/stripTrackedChanges.js +16 -5
  256. package/dist/processors/stripTrackedChanges.js.map +1 -1
  257. package/dist/utils/deepClone.d.ts.map +1 -1
  258. package/dist/utils/deepClone.js +24 -11
  259. package/dist/utils/deepClone.js.map +1 -1
  260. package/dist/utils/units.d.ts.map +1 -1
  261. package/dist/utils/units.js +1 -1
  262. package/dist/utils/units.js.map +1 -1
  263. package/dist/utils/validation.d.ts.map +1 -1
  264. package/dist/utils/validation.js +11 -23
  265. package/dist/utils/validation.js.map +1 -1
  266. package/dist/utils/xmlSanitization.d.ts +2 -0
  267. package/dist/utils/xmlSanitization.d.ts.map +1 -1
  268. package/dist/utils/xmlSanitization.js +133 -7
  269. package/dist/utils/xmlSanitization.js.map +1 -1
  270. package/dist/validation/RevisionAutoFixer.d.ts +6 -2
  271. package/dist/validation/RevisionAutoFixer.d.ts.map +1 -1
  272. package/dist/validation/RevisionAutoFixer.js +68 -54
  273. package/dist/validation/RevisionAutoFixer.js.map +1 -1
  274. package/dist/validation/RevisionValidator.d.ts +2 -0
  275. package/dist/validation/RevisionValidator.d.ts.map +1 -1
  276. package/dist/validation/RevisionValidator.js +26 -21
  277. package/dist/validation/RevisionValidator.js.map +1 -1
  278. package/dist/xml/XMLBuilder.d.ts +1 -0
  279. package/dist/xml/XMLBuilder.d.ts.map +1 -1
  280. package/dist/xml/XMLBuilder.js +52 -20
  281. package/dist/xml/XMLBuilder.js.map +1 -1
  282. package/dist/xml/XMLParser.d.ts +1 -0
  283. package/dist/xml/XMLParser.d.ts.map +1 -1
  284. package/dist/xml/XMLParser.js +55 -13
  285. package/dist/xml/XMLParser.js.map +1 -1
  286. package/dist/zip/ZipHandler.d.ts.map +1 -1
  287. package/dist/zip/ZipHandler.js +37 -13
  288. package/dist/zip/ZipHandler.js.map +1 -1
  289. package/dist/zip/ZipReader.d.ts.map +1 -1
  290. package/dist/zip/ZipReader.js +52 -5
  291. package/dist/zip/ZipReader.js.map +1 -1
  292. package/dist/zip/errors.d.ts +3 -0
  293. package/dist/zip/errors.d.ts.map +1 -1
  294. package/dist/zip/errors.js +9 -1
  295. package/dist/zip/errors.js.map +1 -1
  296. package/dist/zip/types.d.ts +4 -0
  297. package/dist/zip/types.d.ts.map +1 -1
  298. package/dist/zip/types.js +4 -0
  299. package/dist/zip/types.js.map +1 -1
  300. package/package.json +2 -2
  301. package/src/core/Document.ts +1159 -240
  302. package/src/core/DocumentGenerator.ts +106 -26
  303. package/src/core/DocumentParser.ts +681 -114
  304. package/src/core/RelationshipManager.ts +446 -442
  305. package/src/elements/Comment.ts +123 -18
  306. package/src/elements/CommentManager.ts +121 -22
  307. package/src/elements/EndnoteManager.ts +4 -1
  308. package/src/elements/Field.ts +134 -43
  309. package/src/elements/FieldHelpers.ts +20 -4
  310. package/src/elements/Footer.ts +62 -67
  311. package/src/elements/FootnoteManager.ts +4 -1
  312. package/src/elements/Header.ts +62 -67
  313. package/src/elements/HeaderFooterManager.ts +56 -8
  314. package/src/elements/Hyperlink.ts +150 -55
  315. package/src/elements/Image.ts +51 -7
  316. package/src/elements/ImageManager.ts +24 -0
  317. package/src/elements/Paragraph.ts +161 -40
  318. package/src/elements/Revision.ts +446 -47
  319. package/src/elements/Run.ts +44 -6
  320. package/src/elements/Section.ts +25 -9
  321. package/src/elements/Shape.ts +52 -44
  322. package/src/elements/StructuredDocumentTag.ts +83 -8
  323. package/src/elements/Table.ts +151 -50
  324. package/src/elements/TableCell.ts +57 -17
  325. package/src/elements/TableOfContents.ts +10 -9
  326. package/src/elements/TableRow.ts +35 -3
  327. package/src/elements/TextBox.ts +58 -43
  328. package/src/formatting/AbstractNumbering.ts +4 -2
  329. package/src/formatting/NumberingLevel.ts +50 -12
  330. package/src/formatting/NumberingManager.ts +32 -3
  331. package/src/formatting/Style.ts +14 -0
  332. package/src/formatting/StylesManager.ts +29 -2
  333. package/src/helpers/CleanupHelper.ts +122 -30
  334. package/src/images/ImageOptimizer.ts +64 -5
  335. package/src/index.ts +7 -0
  336. package/src/internal.ts +1 -0
  337. package/src/processors/CompatibilityUpgrader.ts +15 -2
  338. package/src/processors/InMemoryRevisionAcceptor.ts +276 -17
  339. package/src/processors/RevisionWalker.ts +83 -7
  340. package/src/processors/SelectiveRevisionAcceptor.ts +164 -28
  341. package/src/processors/cnfStyleDecoder.ts +18 -12
  342. package/src/processors/stripTrackedChanges.ts +47 -10
  343. package/src/utils/deepClone.ts +48 -14
  344. package/src/utils/units.ts +3 -1
  345. package/src/utils/validation.ts +30 -32
  346. package/src/utils/xmlSanitization.ts +218 -22
  347. package/src/validation/RevisionAutoFixer.ts +109 -65
  348. package/src/validation/RevisionValidator.ts +48 -23
  349. package/src/xml/XMLBuilder.ts +82 -22
  350. package/src/xml/XMLParser.ts +87 -20
  351. package/src/zip/ZipHandler.ts +61 -21
  352. package/src/zip/ZipReader.ts +135 -13
  353. package/src/zip/errors.ts +15 -0
  354. package/src/zip/types.ts +44 -2
@@ -39,6 +39,7 @@ import { acceptAllRevisions, cleanupRevisionMetadata } from '../processors/accep
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;
@@ -386,7 +392,7 @@ export class Document {
386
392
  }
387
393
  const numberingXml = zipHandler.getFileAsString(DOCX_PATHS.NUMBERING);
388
394
  if (numberingXml) {
389
- doc._originalNumberingXml = numberingXml;
395
+ doc._originalNumberingXml = reorderRunPropertyChildren(numberingXml);
390
396
  }
391
397
  const settingsXml = zipHandler.getFileAsString(DOCX_PATHS.SETTINGS);
392
398
  if (settingsXml) {
@@ -397,6 +403,10 @@ export class Document {
397
403
  if (appPropsXml) {
398
404
  doc._originalAppPropsXml = appPropsXml;
399
405
  }
406
+ const corePropsXml = zipHandler.getFileAsString(DOCX_PATHS.CORE_PROPS);
407
+ if (corePropsXml) {
408
+ doc._originalCorePropsXml = corePropsXml;
409
+ }
400
410
  const webSettingsXml = zipHandler.getFileAsString(DOCX_PATHS.WEB_SETTINGS);
401
411
  if (webSettingsXml) {
402
412
  doc._originalWebSettingsXml = webSettingsXml;
@@ -659,17 +669,17 @@ export class Document {
659
669
  try {
660
670
  const headersFooters = await this.parser.parseHeadersAndFooters(this.zipHandler, result.section, this.relationshipManager, this.imageManager);
661
671
  const registeredHeaderRIds = new Set();
662
- for (const { header, relationshipId } of headersFooters.headers) {
672
+ for (const { header, relationshipId, filename } of headersFooters.headers) {
663
673
  if (registeredHeaderRIds.has(relationshipId))
664
674
  continue;
665
- this.headerFooterManager.registerHeader(header, relationshipId);
675
+ this.headerFooterManager.registerHeader(header, relationshipId, filename);
666
676
  registeredHeaderRIds.add(relationshipId);
667
677
  }
668
678
  const registeredFooterRIds = new Set();
669
- for (const { footer, relationshipId } of headersFooters.footers) {
679
+ for (const { footer, relationshipId, filename } of headersFooters.footers) {
670
680
  if (registeredFooterRIds.has(relationshipId))
671
681
  continue;
672
- this.headerFooterManager.registerFooter(footer, relationshipId);
682
+ this.headerFooterManager.registerFooter(footer, relationshipId, filename);
673
683
  registeredFooterRIds.add(relationshipId);
674
684
  }
675
685
  }
@@ -712,7 +722,7 @@ export class Document {
712
722
  }
713
723
  }
714
724
  const parser = new DocumentParser();
715
- const comments = parser.parseCommentsXml(commentsXml);
725
+ const comments = parser.parseCommentsXml(commentsXml, this._originalCommentCompanionFiles.get(DOCX_PATHS.COMMENTS_EXTENDED));
716
726
  for (const comment of comments) {
717
727
  this.commentManager.registerExisting(comment);
718
728
  }
@@ -726,7 +736,9 @@ export class Document {
726
736
  this._originalFootnotesXml = footnotesXml;
727
737
  try {
728
738
  const parser = new DocumentParser();
729
- const footnotes = parser.parseFootnotesXml(footnotesXml);
739
+ const relsXml = this.zipHandler.getFileAsString('word/_rels/footnotes.xml.rels');
740
+ const partRels = relsXml ? RelationshipManager.fromXml(relsXml) : undefined;
741
+ const footnotes = parser.parseFootnotesXml(footnotesXml, partRels);
730
742
  for (const footnote of footnotes) {
731
743
  const id = footnote.getId();
732
744
  if (!this.footnoteManager.hasFootnote(id)) {
@@ -748,7 +760,9 @@ export class Document {
748
760
  this._originalEndnotesXml = endnotesXml;
749
761
  try {
750
762
  const parser = new DocumentParser();
751
- const endnotes = parser.parseEndnotesXml(endnotesXml);
763
+ const relsXml = this.zipHandler.getFileAsString('word/_rels/endnotes.xml.rels');
764
+ const partRels = relsXml ? RelationshipManager.fromXml(relsXml) : undefined;
765
+ const endnotes = parser.parseEndnotesXml(endnotesXml, partRels);
752
766
  for (const endnote of endnotes) {
753
767
  const id = endnote.getId();
754
768
  if (!this.endnoteManager.hasEndnote(id)) {
@@ -840,12 +854,13 @@ export class Document {
840
854
  paragraph._setStylesManager(this.stylesManager);
841
855
  if (this.trackChangesEnabled && this.trackingContext.isEnabled()) {
842
856
  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);
857
+ const author = this.trackingContext.getAuthor();
858
+ for (const item of paragraph.getContent()) {
859
+ if (item instanceof Run || item instanceof Hyperlink) {
860
+ const insertion = Revision.createInsertion(author, item);
861
+ this.trackingContext.getRevisionManager().register(insertion);
862
+ paragraph.replaceContent(item, [insertion]);
863
+ }
849
864
  }
850
865
  }
851
866
  this.bodyElements.push(paragraph);
@@ -1061,14 +1076,17 @@ export class Document {
1061
1076
  }
1062
1077
  setTitle(title) {
1063
1078
  this.properties.title = title;
1079
+ this._corePropsModified = true;
1064
1080
  return this;
1065
1081
  }
1066
1082
  setSubject(subject) {
1067
1083
  this.properties.subject = subject;
1084
+ this._corePropsModified = true;
1068
1085
  return this;
1069
1086
  }
1070
1087
  setCreator(creator) {
1071
1088
  this.properties.creator = creator;
1089
+ this._corePropsModified = true;
1072
1090
  return this;
1073
1091
  }
1074
1092
  setAuthor(author) {
@@ -1076,18 +1094,22 @@ export class Document {
1076
1094
  }
1077
1095
  setKeywords(keywords) {
1078
1096
  this.properties.keywords = keywords;
1097
+ this._corePropsModified = true;
1079
1098
  return this;
1080
1099
  }
1081
1100
  setDescription(description) {
1082
1101
  this.properties.description = description;
1102
+ this._corePropsModified = true;
1083
1103
  return this;
1084
1104
  }
1085
1105
  setCategory(category) {
1086
1106
  this.properties.category = category;
1107
+ this._corePropsModified = true;
1087
1108
  return this;
1088
1109
  }
1089
1110
  setContentStatus(status) {
1090
1111
  this.properties.contentStatus = status;
1112
+ this._corePropsModified = true;
1091
1113
  return this;
1092
1114
  }
1093
1115
  setApplication(application) {
@@ -1534,8 +1556,48 @@ export class Document {
1534
1556
  if (this._removedParts.has(DOCX_PATHS.CORE_PROPS) ||
1535
1557
  this._removedParts.has('docProps/core.xml'))
1536
1558
  return;
1537
- const xml = this.generator.generateCoreProps(this.properties);
1538
- this.zipHandler.updateFile(DOCX_PATHS.CORE_PROPS, xml);
1559
+ if (this._originalCorePropsXml && !this._corePropsModified) {
1560
+ return;
1561
+ }
1562
+ if (this._originalCorePropsXml && this._corePropsModified) {
1563
+ const mergedXml = this.mergeCorePropsWithOriginal();
1564
+ this.zipHandler.updateFile(DOCX_PATHS.CORE_PROPS, mergedXml);
1565
+ }
1566
+ else {
1567
+ const xml = this.generator.generateCoreProps(this.properties);
1568
+ this.zipHandler.updateFile(DOCX_PATHS.CORE_PROPS, xml);
1569
+ }
1570
+ }
1571
+ mergeCorePropsWithOriginal() {
1572
+ if (!this._originalCorePropsXml) {
1573
+ return this.generator.generateCoreProps(this.properties);
1574
+ }
1575
+ let xml = this._originalCorePropsXml;
1576
+ const mergeField = (tag, value) => {
1577
+ if (value === undefined)
1578
+ return;
1579
+ const escaped = XMLBuilder.sanitizeXmlContent(value);
1580
+ const openClose = new RegExp(`<${tag}\\b[^>]*>[\\s\\S]*?</${tag}>`);
1581
+ const selfClosing = new RegExp(`<${tag}\\b[^>]*/>`);
1582
+ if (openClose.test(xml)) {
1583
+ xml = xml.replace(openClose, `<${tag}>${escaped}</${tag}>`);
1584
+ }
1585
+ else if (selfClosing.test(xml)) {
1586
+ xml = xml.replace(selfClosing, `<${tag}>${escaped}</${tag}>`);
1587
+ }
1588
+ else {
1589
+ xml = xml.replace(/<\/cp:coreProperties>/, ` <${tag}>${escaped}</${tag}>\n</cp:coreProperties>`);
1590
+ }
1591
+ };
1592
+ mergeField('dc:title', this.properties.title);
1593
+ mergeField('dc:subject', this.properties.subject);
1594
+ mergeField('dc:creator', this.properties.creator);
1595
+ mergeField('cp:keywords', this.properties.keywords);
1596
+ mergeField('dc:description', this.properties.description);
1597
+ mergeField('cp:category', this.properties.category);
1598
+ mergeField('cp:contentStatus', this.properties.contentStatus);
1599
+ mergeField('dc:language', this.properties.language);
1600
+ return xml;
1539
1601
  }
1540
1602
  updateAppProps() {
1541
1603
  if (this._removedParts.has(DOCX_PATHS.APP_PROPS) || this._removedParts.has('docProps/app.xml'))
@@ -1565,24 +1627,36 @@ export class Document {
1565
1627
  else if (xml.includes('<Company/>')) {
1566
1628
  xml = xml.replace(/<Company\/>/, `<Company>${escaped}</Company>`);
1567
1629
  }
1630
+ else {
1631
+ xml = xml.replace(/<\/Properties>/, ` <Company>${escaped}</Company>\n</Properties>`);
1632
+ }
1568
1633
  }
1569
1634
  if (this.properties.application !== undefined) {
1570
1635
  const escaped = XMLBuilder.sanitizeXmlContent(this.properties.application);
1571
1636
  if (xml.includes('<Application>')) {
1572
1637
  xml = xml.replace(/<Application>[^<]*<\/Application>/, `<Application>${escaped}</Application>`);
1573
1638
  }
1639
+ else {
1640
+ xml = xml.replace(/<\/Properties>/, ` <Application>${escaped}</Application>\n</Properties>`);
1641
+ }
1574
1642
  }
1575
1643
  if (this.properties.appVersion !== undefined) {
1576
1644
  const escaped = XMLBuilder.sanitizeXmlContent(this.properties.appVersion);
1577
1645
  if (xml.includes('<AppVersion>')) {
1578
1646
  xml = xml.replace(/<AppVersion>[^<]*<\/AppVersion>/, `<AppVersion>${escaped}</AppVersion>`);
1579
1647
  }
1648
+ else {
1649
+ xml = xml.replace(/<\/Properties>/, ` <AppVersion>${escaped}</AppVersion>\n</Properties>`);
1650
+ }
1580
1651
  }
1581
1652
  if (this.properties.manager !== undefined) {
1582
1653
  const escaped = XMLBuilder.sanitizeXmlContent(this.properties.manager);
1583
1654
  if (xml.includes('<Manager>')) {
1584
1655
  xml = xml.replace(/<Manager>[^<]*<\/Manager>/, `<Manager>${escaped}</Manager>`);
1585
1656
  }
1657
+ else {
1658
+ xml = xml.replace(/<\/Properties>/, ` <Manager>${escaped}</Manager>\n</Properties>`);
1659
+ }
1586
1660
  }
1587
1661
  return xml;
1588
1662
  }
@@ -1708,7 +1782,8 @@ export class Document {
1708
1782
  return this.stylesManager.generateStylesXml();
1709
1783
  }
1710
1784
  const modifiedStyleIds = this.stylesManager.getModifiedStyleIds();
1711
- if (modifiedStyleIds.size === 0) {
1785
+ const removedStyleIds = this.stylesManager.getRemovedStyleIds();
1786
+ if (modifiedStyleIds.size === 0 && removedStyleIds.size === 0) {
1712
1787
  return this._originalStylesXml;
1713
1788
  }
1714
1789
  const modifiedStyles = new Map();
@@ -1730,6 +1805,11 @@ export class Document {
1730
1805
  resultXml = resultXml.replace('</w:styles>', `${newStyleXml}\n</w:styles>`);
1731
1806
  }
1732
1807
  }
1808
+ for (const styleId of removedStyleIds) {
1809
+ const escapedStyleId = styleId.replace(/[.*+?^${}()|[\]\\"]/g, '\\$&');
1810
+ const removedPattern = new RegExp(`\\s*<w:style[^>]*\\sw:styleId="${escapedStyleId}"[^>]*>[\\s\\S]*?</w:style>`);
1811
+ resultXml = resultXml.replace(removedPattern, '');
1812
+ }
1733
1813
  return resultXml;
1734
1814
  }
1735
1815
  updateNumberingXml() {
@@ -1928,8 +2008,14 @@ export class Document {
1928
2008
  xml = xml.replace(/<w:revisionView\b[^>]*>[\s\S]*?<\/w:revisionView>/g, '');
1929
2009
  let trackBlock = '';
1930
2010
  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'}"/>`;
2011
+ if (!view.showInsertionsAndDeletions ||
2012
+ !view.showFormatting ||
2013
+ !view.showInkAnnotations ||
2014
+ view.showMarkup === false ||
2015
+ view.showComments === false) {
2016
+ const markupAttr = view.showMarkup === false ? ' w:markup="0"' : '';
2017
+ const commentsAttr = view.showComments === false ? ' w:comments="0"' : '';
2018
+ 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
2019
  }
1934
2020
  if (this.trackChangesEnabled) {
1935
2021
  trackBlock += '\n <w:trackRevisions/>';
@@ -1965,6 +2051,8 @@ export class Document {
1965
2051
  const prot = this.documentProtection;
1966
2052
  const esc = XMLBuilder.escapeXmlAttribute;
1967
2053
  let protXml = `\n <w:documentProtection w:edit="${esc(prot.edit)}" w:enforcement="${prot.enforcement ? '1' : '0'}"`;
2054
+ if (prot.formatting !== undefined)
2055
+ protXml += ` w:formatting="${prot.formatting ? '1' : '0'}"`;
1968
2056
  if (prot.cryptProviderType)
1969
2057
  protXml += ` w:cryptProviderType="${esc(prot.cryptProviderType)}"`;
1970
2058
  if (prot.cryptAlgorithmClass)
@@ -1979,6 +2067,12 @@ export class Document {
1979
2067
  protXml += ` w:hash="${esc(prot.hash)}"`;
1980
2068
  if (prot.salt)
1981
2069
  protXml += ` w:salt="${esc(prot.salt)}"`;
2070
+ if (prot.algorithmName)
2071
+ protXml += ` w:algorithmName="${esc(prot.algorithmName)}"`;
2072
+ if (prot.hashValue)
2073
+ protXml += ` w:hashValue="${esc(prot.hashValue)}"`;
2074
+ if (prot.saltValue)
2075
+ protXml += ` w:saltValue="${esc(prot.saltValue)}"`;
1982
2076
  protXml += '/>';
1983
2077
  if (/<w:autoFormatOverride\b/.test(xml)) {
1984
2078
  xml = xml.replace(/<w:autoFormatOverride\b/, protXml + '\n <w:autoFormatOverride');
@@ -2732,6 +2826,18 @@ export class Document {
2732
2826
  }
2733
2827
  }
2734
2828
  }
2829
+ for (const style of this.stylesManager.getAllStyles()) {
2830
+ const styleNumId = style.getProperties().numPr?.numId;
2831
+ if (styleNumId !== undefined) {
2832
+ usedNumIds.add(styleNumId);
2833
+ }
2834
+ }
2835
+ for (const filePath of [DOCX_PATHS.STYLES, 'word/comments.xml']) {
2836
+ const xml = this.zipHandler.getFileAsString(filePath);
2837
+ if (xml) {
2838
+ this.collectNumIdsFromXml(xml, usedNumIds);
2839
+ }
2840
+ }
2735
2841
  this.numberingManager.cleanupUnusedNumbering(usedNumIds);
2736
2842
  }
2737
2843
  validateNumberingReferences() {
@@ -2833,7 +2939,67 @@ export class Document {
2833
2939
  maxRevId = rev.getId();
2834
2940
  }
2835
2941
  const registeredSet = new Set(existingRevisions);
2836
- let docPrId = 1;
2942
+ let maxPreservedDocPrId = 0;
2943
+ const collectPreservedDocPrIds = (rawXml) => {
2944
+ if (!rawXml || !rawXml.includes('wp:docPr'))
2945
+ return;
2946
+ const docPrPattern = /<wp:docPr\b[^>]*\bid="(\d+)"/g;
2947
+ let match;
2948
+ while ((match = docPrPattern.exec(rawXml)) !== null) {
2949
+ const id = parseInt(match[1], 10);
2950
+ if (id > maxPreservedDocPrId)
2951
+ maxPreservedDocPrId = id;
2952
+ }
2953
+ };
2954
+ const collectFromRun = (run) => {
2955
+ if (run instanceof ImageRun) {
2956
+ collectPreservedDocPrIds(run.getRawRunXml());
2957
+ return;
2958
+ }
2959
+ for (const contentItem of run.getContent()) {
2960
+ collectPreservedDocPrIds(contentItem.rawXml);
2961
+ }
2962
+ };
2963
+ const collectFromParagraph = (para) => {
2964
+ for (const item of para.getContent()) {
2965
+ if (item instanceof Revision) {
2966
+ for (const revItem of item.getContent()) {
2967
+ if (revItem instanceof Run)
2968
+ collectFromRun(revItem);
2969
+ }
2970
+ }
2971
+ else if (item instanceof Run) {
2972
+ collectFromRun(item);
2973
+ }
2974
+ else if (item instanceof PreservedElement) {
2975
+ collectPreservedDocPrIds(item.getRawXml());
2976
+ }
2977
+ }
2978
+ };
2979
+ for (const element of this.bodyElements) {
2980
+ if (element instanceof Paragraph) {
2981
+ collectFromParagraph(element);
2982
+ }
2983
+ else if (element instanceof Table) {
2984
+ for (const row of element.getRows()) {
2985
+ for (const cell of row.getCells()) {
2986
+ for (const para of cell.getParagraphs()) {
2987
+ collectFromParagraph(para);
2988
+ }
2989
+ for (const nested of cell.getRawNestedContent()) {
2990
+ collectPreservedDocPrIds(nested.xml);
2991
+ }
2992
+ }
2993
+ }
2994
+ }
2995
+ else {
2996
+ const passthrough = element;
2997
+ if (typeof passthrough.getRawXml === 'function') {
2998
+ collectPreservedDocPrIds(passthrough.getRawXml());
2999
+ }
3000
+ }
3001
+ }
3002
+ let docPrId = maxPreservedDocPrId + 1;
2837
3003
  const existingParaIds = new Set();
2838
3004
  const paragraphsNeedingIds = [];
2839
3005
  const generateUniqueParaId = () => {
@@ -2865,6 +3031,13 @@ export class Document {
2865
3031
  else if (item instanceof ImageRun) {
2866
3032
  item.getImageElement().setDocPrId(docPrId++);
2867
3033
  }
3034
+ else if (item instanceof Revision) {
3035
+ for (const revItem of item.getContent()) {
3036
+ if (revItem instanceof ImageRun) {
3037
+ revItem.getImageElement().setDocPrId(docPrId++);
3038
+ }
3039
+ }
3040
+ }
2868
3041
  }
2869
3042
  if (para.formatting.paraId) {
2870
3043
  existingParaIds.add(para.formatting.paraId);
@@ -3154,6 +3327,7 @@ export class Document {
3154
3327
  numLevel.setLeftIndent(720 * (levelIndex + 1));
3155
3328
  numLevel.setHangingIndent(360);
3156
3329
  }
3330
+ this.numberingManager.markAbstractNumberingModified(abstractNumId);
3157
3331
  this.applyFormattingToListParagraphs(instance.getNumId());
3158
3332
  count++;
3159
3333
  }
@@ -3180,6 +3354,7 @@ export class Document {
3180
3354
  numLevel.setHangingIndent(360);
3181
3355
  numLevel.setAlignment('left');
3182
3356
  }
3357
+ this.numberingManager.markAbstractNumberingModified(abstractNumId);
3183
3358
  this.applyFormattingToListParagraphs(instance.getNumId());
3184
3359
  count++;
3185
3360
  }
@@ -5002,39 +5177,39 @@ export class Document {
5002
5177
  return this;
5003
5178
  }
5004
5179
  setHeader(header) {
5005
- const relationship = this.relationshipManager.addHeader(`${header.getFilename(1)}`);
5180
+ const relationship = this.relationshipManager.addHeader(this.headerFooterManager.peekHeaderFilename(header));
5006
5181
  this.headerFooterManager.registerHeader(header, relationship.getId());
5007
5182
  this.section.setHeaderReference('default', relationship.getId());
5008
5183
  return this;
5009
5184
  }
5010
5185
  setFirstPageHeader(header) {
5011
5186
  this.section.setTitlePage(true);
5012
- const relationship = this.relationshipManager.addHeader(`${header.getFilename(this.headerFooterManager.getHeaderCount() + 1)}`);
5187
+ const relationship = this.relationshipManager.addHeader(this.headerFooterManager.peekHeaderFilename(header));
5013
5188
  this.headerFooterManager.registerHeader(header, relationship.getId());
5014
5189
  this.section.setHeaderReference('first', relationship.getId());
5015
5190
  return this;
5016
5191
  }
5017
5192
  setEvenPageHeader(header) {
5018
- const relationship = this.relationshipManager.addHeader(`${header.getFilename(this.headerFooterManager.getHeaderCount() + 1)}`);
5193
+ const relationship = this.relationshipManager.addHeader(this.headerFooterManager.peekHeaderFilename(header));
5019
5194
  this.headerFooterManager.registerHeader(header, relationship.getId());
5020
5195
  this.section.setHeaderReference('even', relationship.getId());
5021
5196
  return this;
5022
5197
  }
5023
5198
  setFooter(footer) {
5024
- const relationship = this.relationshipManager.addFooter(`${footer.getFilename(1)}`);
5199
+ const relationship = this.relationshipManager.addFooter(this.headerFooterManager.peekFooterFilename(footer));
5025
5200
  this.headerFooterManager.registerFooter(footer, relationship.getId());
5026
5201
  this.section.setFooterReference('default', relationship.getId());
5027
5202
  return this;
5028
5203
  }
5029
5204
  setFirstPageFooter(footer) {
5030
5205
  this.section.setTitlePage(true);
5031
- const relationship = this.relationshipManager.addFooter(`${footer.getFilename(this.headerFooterManager.getFooterCount() + 1)}`);
5206
+ const relationship = this.relationshipManager.addFooter(this.headerFooterManager.peekFooterFilename(footer));
5032
5207
  this.headerFooterManager.registerFooter(footer, relationship.getId());
5033
5208
  this.section.setFooterReference('first', relationship.getId());
5034
5209
  return this;
5035
5210
  }
5036
5211
  setEvenPageFooter(footer) {
5037
- const relationship = this.relationshipManager.addFooter(`${footer.getFilename(this.headerFooterManager.getFooterCount() + 1)}`);
5212
+ const relationship = this.relationshipManager.addFooter(this.headerFooterManager.peekFooterFilename(footer));
5038
5213
  this.headerFooterManager.registerFooter(footer, relationship.getId());
5039
5214
  this.section.setFooterReference('even', relationship.getId());
5040
5215
  return this;
@@ -5180,6 +5355,20 @@ export class Document {
5180
5355
  }
5181
5356
  }
5182
5357
  this.removeCommentCompanionFiles();
5358
+ const extendedXml = this.commentManager.generateCommentsExtendedXml();
5359
+ if (extendedXml) {
5360
+ this.zipHandler.addFile(DOCX_PATHS.COMMENTS_EXTENDED, extendedXml);
5361
+ const hasExtendedRel = this.relationshipManager
5362
+ .getAllRelationships()
5363
+ .some((rel) => rel.getTarget() === 'commentsExtended.xml');
5364
+ if (!hasExtendedRel) {
5365
+ this.relationshipManager.addRelationship(Relationship.create({
5366
+ id: this.relationshipManager.generateId(),
5367
+ type: 'http://schemas.microsoft.com/office/2011/relationships/commentsExtended',
5368
+ target: 'commentsExtended.xml',
5369
+ }));
5370
+ }
5371
+ }
5183
5372
  }
5184
5373
  else if (this._originalCommentsXml) {
5185
5374
  this.zipHandler.addFile('word/comments.xml', this._originalCommentsXml);
@@ -5217,7 +5406,8 @@ export class Document {
5217
5406
  }
5218
5407
  }
5219
5408
  saveFootnotes() {
5220
- if (this._footnotesModified || this.footnoteManager.getCount() > 0) {
5409
+ if (this._footnotesModified ||
5410
+ (!this._originalFootnotesXml && this.footnoteManager.getCount() > 0)) {
5221
5411
  const xml = this.footnoteManager.generateFootnotesXml();
5222
5412
  this.zipHandler.addFile(DOCX_PATHS.FOOTNOTES, xml);
5223
5413
  const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.FOOTNOTES);
@@ -5235,7 +5425,8 @@ export class Document {
5235
5425
  }
5236
5426
  }
5237
5427
  saveEndnotes() {
5238
- if (this._endnotesModified || this.endnoteManager.getCount() > 0) {
5428
+ if (this._endnotesModified ||
5429
+ (!this._originalEndnotesXml && this.endnoteManager.getCount() > 0)) {
5239
5430
  const xml = this.endnoteManager.generateEndnotesXml();
5240
5431
  this.zipHandler.addFile(DOCX_PATHS.ENDNOTES, xml);
5241
5432
  const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.ENDNOTES);
@@ -5304,7 +5495,12 @@ export class Document {
5304
5495
  xml += '</Relationships>';
5305
5496
  this.zipHandler.addFile(relsPath, xml);
5306
5497
  for (const rel of hyperlinkRels) {
5307
- this.relationshipManager.removeRelationship(rel.id);
5498
+ const mainRel = this.relationshipManager.getRelationship(rel.id);
5499
+ if (mainRel &&
5500
+ mainRel.getType() === RelationshipType.HYPERLINK &&
5501
+ mainRel.getTarget() === rel.url) {
5502
+ this.relationshipManager.removeRelationship(rel.id);
5503
+ }
5308
5504
  }
5309
5505
  }
5310
5506
  }
@@ -5422,13 +5618,25 @@ export class Document {
5422
5618
  parseContentTypes(xml) {
5423
5619
  const defaults = new Set();
5424
5620
  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]}`);
5621
+ const readAttr = (tag, name) => {
5622
+ const match = tag.match(new RegExp(`(?:^|\\s)${name}\\s*=\\s*("([^"]*)"|'([^']*)')`));
5623
+ if (!match)
5624
+ return undefined;
5625
+ return XMLBuilder.unescapeXml(match[2] ?? match[3] ?? '');
5626
+ };
5627
+ for (const tagMatch of xml.matchAll(/<Default\b[^>]*>/g)) {
5628
+ const ext = readAttr(tagMatch[0], 'Extension');
5629
+ const contentType = readAttr(tagMatch[0], 'ContentType');
5630
+ if (ext && contentType) {
5631
+ defaults.add(`${ext}|${contentType}`);
5632
+ }
5428
5633
  }
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]}`);
5634
+ for (const tagMatch of xml.matchAll(/<Override\b[^>]*>/g)) {
5635
+ const partName = readAttr(tagMatch[0], 'PartName');
5636
+ const contentType = readAttr(tagMatch[0], 'ContentType');
5637
+ if (partName && contentType) {
5638
+ overrides.add(`${partName}|${contentType}`);
5639
+ }
5432
5640
  }
5433
5641
  return { defaults, overrides };
5434
5642
  }
@@ -5748,9 +5956,7 @@ export class Document {
5748
5956
  }
5749
5957
  if (element && typeof element.getContent === 'function') {
5750
5958
  for (const item of element.getContent()) {
5751
- if (item &&
5752
- typeof item._setTrackingContext === 'function' &&
5753
- typeof item.getInstruction === 'function') {
5959
+ if (item && typeof item._setTrackingContext === 'function') {
5754
5960
  item._setTrackingContext(this.trackingContext);
5755
5961
  }
5756
5962
  }
@@ -5917,6 +6123,7 @@ export class Document {
5917
6123
  updated.size = sizeInPoints;
5918
6124
  }
5919
6125
  normalStyle.setRunFormatting(updated);
6126
+ this.stylesManager.addStyle(normalStyle);
5920
6127
  return this;
5921
6128
  }
5922
6129
  setDefaultFontSize(sizeInPoints) {
@@ -5932,6 +6139,7 @@ export class Document {
5932
6139
  }
5933
6140
  const existing = normalStyle.getRunFormatting() ?? {};
5934
6141
  normalStyle.setRunFormatting({ ...existing, size: sizeInPoints });
6142
+ this.stylesManager.addStyle(normalStyle);
5935
6143
  return this;
5936
6144
  }
5937
6145
  getUpdateFields() {
@@ -6123,6 +6331,40 @@ export class Document {
6123
6331
  this.endnoteManager.clear();
6124
6332
  this._endnotesModified = true;
6125
6333
  }
6334
+ removeFootnote(id) {
6335
+ const removed = this.footnoteManager.removeFootnote(id);
6336
+ if (removed) {
6337
+ this._footnotesModified = true;
6338
+ this.removeNoteReferenceRuns('footnoteReference', id);
6339
+ }
6340
+ return removed;
6341
+ }
6342
+ removeEndnote(id) {
6343
+ const removed = this.endnoteManager.removeEndnote(id);
6344
+ if (removed) {
6345
+ this._endnotesModified = true;
6346
+ this.removeNoteReferenceRuns('endnoteReference', id);
6347
+ }
6348
+ return removed;
6349
+ }
6350
+ removeNoteReferenceRuns(refType, id) {
6351
+ for (const para of this.getAllParagraphs()) {
6352
+ const content = para.getContent();
6353
+ for (let i = content.length - 1; i >= 0; i--) {
6354
+ const item = content[i];
6355
+ if (!(item instanceof Run)) {
6356
+ continue;
6357
+ }
6358
+ const matches = item
6359
+ .getContent()
6360
+ .some((c) => c.type === refType &&
6361
+ (refType === 'footnoteReference' ? c.footnoteId === id : c.endnoteId === id));
6362
+ if (matches) {
6363
+ para.removeContentAt(i);
6364
+ }
6365
+ }
6366
+ }
6367
+ }
6126
6368
  addCommentToParagraph(paragraph, commentOrAuthor, content, initials) {
6127
6369
  const comment = typeof commentOrAuthor === 'string'
6128
6370
  ? this.createComment(commentOrAuthor, content, initials)
@@ -6173,12 +6415,52 @@ export class Document {
6173
6415
  return this.commentManager.hasReplies(commentId);
6174
6416
  }
6175
6417
  removeComment(id) {
6418
+ const replyIds = this.commentManager.getReplies(id).map((reply) => reply.getId());
6176
6419
  const removed = this.commentManager.removeComment(id);
6177
6420
  if (removed) {
6421
+ this.removeCommentAnchors([id, ...replyIds]);
6178
6422
  this._commentsModified = true;
6179
6423
  }
6180
6424
  return removed;
6181
6425
  }
6426
+ removeCommentAnchors(commentIds) {
6427
+ const strip = (paragraphs) => {
6428
+ let removed = false;
6429
+ for (const paragraph of paragraphs) {
6430
+ for (const commentId of commentIds) {
6431
+ if (paragraph.removeCommentAnchor(commentId)) {
6432
+ removed = true;
6433
+ }
6434
+ }
6435
+ }
6436
+ return removed;
6437
+ };
6438
+ strip(this.getAllParagraphs());
6439
+ for (const entry of this.headerFooterManager.getAllHeaders()) {
6440
+ strip(this.extractParagraphsFromElements(entry.header.getElements()));
6441
+ }
6442
+ for (const entry of this.headerFooterManager.getAllFooters()) {
6443
+ strip(this.extractParagraphsFromElements(entry.footer.getElements()));
6444
+ }
6445
+ let footnotesChanged = false;
6446
+ for (const footnote of this.footnoteManager.getAllFootnotes()) {
6447
+ if (strip(footnote.getParagraphs())) {
6448
+ footnotesChanged = true;
6449
+ }
6450
+ }
6451
+ if (footnotesChanged) {
6452
+ this._footnotesModified = true;
6453
+ }
6454
+ let endnotesChanged = false;
6455
+ for (const endnote of this.endnoteManager.getAllEndnotes()) {
6456
+ if (strip(endnote.getParagraphs())) {
6457
+ endnotesChanged = true;
6458
+ }
6459
+ }
6460
+ if (endnotesChanged) {
6461
+ this._endnotesModified = true;
6462
+ }
6463
+ }
6182
6464
  hasNoRevisions() {
6183
6465
  return this.revisionManager.isEmpty();
6184
6466
  }
@@ -6434,8 +6716,8 @@ export class Document {
6434
6716
  this.parser.clearParseErrors();
6435
6717
  this.stylesManager.clear();
6436
6718
  this.numberingManager.clear();
6437
- this.imageManager.clear();
6438
6719
  this.imageManager.releaseAllImageData();
6720
+ this.imageManager.clear();
6439
6721
  this.relationshipManager.clear();
6440
6722
  this.headerFooterManager.clear();
6441
6723
  this.bookmarkManager.clear();
@@ -6449,6 +6731,7 @@ export class Document {
6449
6731
  this._originalNumberingXml = undefined;
6450
6732
  this._originalSettingsXml = undefined;
6451
6733
  this._originalAppPropsXml = undefined;
6734
+ this._originalCorePropsXml = undefined;
6452
6735
  this._originalFootnotesXml = undefined;
6453
6736
  this._originalEndnotesXml = undefined;
6454
6737
  this._originalCommentsXml = undefined;
@@ -6457,6 +6740,7 @@ export class Document {
6457
6740
  this._originalContentTypes = undefined;
6458
6741
  this._settingsModified = false;
6459
6742
  this._appPropsModified = false;
6743
+ this._corePropsModified = false;
6460
6744
  this._footnotesModified = false;
6461
6745
  this._endnotesModified = false;
6462
6746
  this._originalWebSettingsXml = undefined;
@@ -7014,7 +7298,7 @@ export class Document {
7014
7298
  const matches = originalText.match(wordPattern);
7015
7299
  if (matches) {
7016
7300
  replacementCount += matches.length;
7017
- newText = originalText.replace(wordPattern, replace);
7301
+ newText = originalText.replace(wordPattern, () => replace);
7018
7302
  }
7019
7303
  }
7020
7304
  else {
@@ -7022,7 +7306,7 @@ export class Document {
7022
7306
  const matches = originalText.match(searchPattern);
7023
7307
  if (matches) {
7024
7308
  replacementCount += matches.length;
7025
- newText = originalText.replace(searchPattern, replace);
7309
+ newText = originalText.replace(searchPattern, () => replace);
7026
7310
  }
7027
7311
  }
7028
7312
  if (newText !== originalText) {
@@ -7302,90 +7586,303 @@ export class Document {
7302
7586
  }
7303
7587
  toPlainText(separator = '\n') {
7304
7588
  const paragraphs = this.getAllParagraphs();
7305
- return paragraphs.map((p) => p.getText()).join(separator);
7306
- }
7307
- toMarkdown() {
7589
+ return paragraphs.map((p) => p.getTextIncludingRevisions()).join(separator);
7590
+ }
7591
+ toMarkdown(options) {
7592
+ const ctx = {
7593
+ opts: {
7594
+ htmlFallback: options?.htmlFallback ?? true,
7595
+ footnotes: options?.footnotes ?? true,
7596
+ images: options?.images ?? true,
7597
+ },
7598
+ notes: [],
7599
+ noteMarkers: new Set(),
7600
+ };
7308
7601
  const lines = [];
7309
7602
  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
- }
7603
+ this.bodyElementToMarkdown(element, ctx, lines);
7604
+ }
7605
+ while (lines.length > 0 && lines[lines.length - 1] === '') {
7606
+ lines.pop();
7607
+ }
7608
+ let out = lines.join('\n');
7609
+ if (ctx.opts.footnotes && ctx.notes.length > 0) {
7610
+ const defs = ctx.notes.map((n) => `[^${n.marker}]: ${n.text}`).join('\n');
7611
+ out = out ? `${out}\n\n${defs}` : defs;
7612
+ }
7613
+ return out;
7614
+ }
7615
+ bodyElementToMarkdown(element, ctx, lines) {
7616
+ if (element instanceof Paragraph) {
7617
+ const md = this.paragraphToMarkdown(element, ctx);
7618
+ if (md !== null) {
7619
+ lines.push(md);
7620
+ lines.push('');
7316
7621
  }
7317
- else if (element instanceof Table) {
7318
- lines.push(...this.tableToMarkdown(element));
7622
+ }
7623
+ else if (element instanceof Table) {
7624
+ const tableLines = this.tableToMarkdown(element, ctx);
7625
+ if (tableLines.length > 0) {
7626
+ lines.push(...tableLines);
7319
7627
  lines.push('');
7320
7628
  }
7321
7629
  }
7322
- while (lines.length > 0 && lines[lines.length - 1] === '') {
7323
- lines.pop();
7630
+ else if (element instanceof StructuredDocumentTag) {
7631
+ for (const child of element.getContent()) {
7632
+ this.bodyElementToMarkdown(child, ctx, lines);
7633
+ }
7634
+ }
7635
+ else if (element instanceof PreservedElement) {
7636
+ const text = this.extractTextFromRawXml(element.getRawXml());
7637
+ if (text) {
7638
+ lines.push(this.escapeMarkdown(text));
7639
+ lines.push('');
7640
+ }
7324
7641
  }
7325
- return lines.join('\n');
7326
7642
  }
7327
- paragraphToMarkdown(para) {
7328
- const text = this.paragraphContentToMarkdown(para);
7329
- if (!text && !para.hasNumbering())
7643
+ paragraphToMarkdown(para, ctx) {
7644
+ const inline = this.paragraphContentToMarkdown(para, ctx);
7645
+ const hasNumbering = para.hasNumbering();
7646
+ if (!inline && !hasNumbering)
7330
7647
  return null;
7331
7648
  const headingLevel = para.detectHeadingLevel();
7332
7649
  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}`;
7650
+ return '#'.repeat(headingLevel) + ' ' + this.toSingleLine(inline);
7651
+ }
7652
+ const style = (para.getStyle() ?? '').toLowerCase();
7653
+ const isListStyle = style.includes('listbullet') ||
7654
+ style.includes('list bullet') ||
7655
+ style.includes('listnumber') ||
7656
+ style.includes('list number');
7657
+ if (hasNumbering || isListStyle) {
7658
+ const level = para.getNumbering()?.level ?? 0;
7659
+ const indent = ' '.repeat(Math.max(0, level));
7660
+ const marker = this.isOrderedList(para) ? '1.' : '-';
7661
+ return `${indent}${marker} ${this.toSingleLine(inline)}`;
7662
+ }
7663
+ if (style.includes('quote')) {
7664
+ return this.toSingleLine(inline)
7665
+ .split('\n')
7666
+ .map((l) => `> ${l}`)
7667
+ .join('\n');
7668
+ }
7669
+ return inline;
7670
+ }
7671
+ isOrderedList(para) {
7672
+ const numbering = para.getNumbering();
7673
+ if (numbering) {
7674
+ const instance = this.numberingManager.getInstance(numbering.numId);
7675
+ if (instance) {
7676
+ const abstract = this.numberingManager.getAbstractNumbering(instance.getAbstractNumId());
7677
+ const levelDef = abstract?.getLevel(numbering.level);
7678
+ const format = levelDef?.getFormat();
7679
+ if (format)
7680
+ return format !== 'bullet';
7681
+ }
7339
7682
  }
7340
- return text;
7683
+ const style = (para.getStyle() ?? '').toLowerCase();
7684
+ if (style.includes('bullet'))
7685
+ return false;
7686
+ if (style.includes('number'))
7687
+ return true;
7688
+ return true;
7341
7689
  }
7342
- paragraphContentToMarkdown(para) {
7690
+ paragraphContentToMarkdown(para, ctx) {
7691
+ return this.inlineContentToMarkdown(para.getContent(), ctx);
7692
+ }
7693
+ inlineContentToMarkdown(items, ctx) {
7343
7694
  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}*`;
7695
+ for (const item of items) {
7696
+ if (item instanceof ImageRun) {
7697
+ if (ctx.opts.images) {
7698
+ const image = item.getImageElement();
7699
+ const alt = this.escapeMarkdown(image.getAltText() || 'image');
7700
+ const src = image.getRelationshipId() ?? 'image';
7701
+ parts.push(`![${alt}](${src})`);
7359
7702
  }
7360
- if (fmt.strike) {
7361
- md = `~~${md}~~`;
7703
+ }
7704
+ else if (item instanceof Run) {
7705
+ parts.push(this.runToMarkdown(item, ctx));
7706
+ }
7707
+ else if (item instanceof Hyperlink) {
7708
+ const runs = item.getRuns();
7709
+ let label = runs.length > 0
7710
+ ? runs.map((r) => this.runToMarkdown(r, ctx, true)).join('')
7711
+ : this.escapeMarkdown(item.getText());
7712
+ if (!label)
7713
+ label = this.escapeMarkdown(item.getText());
7714
+ let url = item.getUrl() ?? '';
7715
+ const anchor = item.getAnchor();
7716
+ if (anchor)
7717
+ url = url ? `${url}#${anchor}` : `#${anchor}`;
7718
+ parts.push(`[${label}](${url})`);
7719
+ }
7720
+ else if (item instanceof Revision) {
7721
+ const inner = this.inlineContentToMarkdown(item.getContent(), ctx);
7722
+ if (!inner)
7723
+ continue;
7724
+ const type = item.getType();
7725
+ if (type === 'delete' || type === 'moveFrom') {
7726
+ parts.push(ctx.opts.htmlFallback ? `<del>${inner}</del>` : `~~${inner}~~`);
7362
7727
  }
7363
- if (fmt.font &&
7364
- /^(courier|consolas|monaco|menlo|source code|fira code|jetbrains mono)/i.test(fmt.font)) {
7365
- md = `\`${runText}\``;
7728
+ else {
7729
+ parts.push(inner);
7366
7730
  }
7367
- parts.push(md);
7368
7731
  }
7369
- else if (item instanceof Hyperlink) {
7370
- const url = item.getUrl() || '';
7371
- const linkText = item.getText() || url;
7372
- parts.push(`[${linkText}](${url})`);
7732
+ else if (item instanceof ComplexField) {
7733
+ const result = item.getResult();
7734
+ if (result)
7735
+ parts.push(this.escapeMarkdown(result));
7736
+ }
7737
+ else if (item instanceof Field) {
7738
+ const result = item.getCachedResult();
7739
+ if (result)
7740
+ parts.push(this.escapeMarkdown(result));
7741
+ }
7742
+ else if (item instanceof TextBox) {
7743
+ const text = item
7744
+ .getParagraphs()
7745
+ .map((p) => this.paragraphContentToMarkdown(p, ctx))
7746
+ .filter((s) => s !== '')
7747
+ .join(' ');
7748
+ if (text)
7749
+ parts.push(text);
7750
+ }
7751
+ else if (item instanceof Shape) {
7752
+ const text = item.getText();
7753
+ if (text)
7754
+ parts.push(this.escapeMarkdown(text));
7755
+ }
7756
+ else if (item instanceof PreservedElement) {
7757
+ const type = item.getElementType();
7758
+ if (!type.startsWith('w:comment')) {
7759
+ const text = this.extractTextFromRawXml(item.getRawXml());
7760
+ if (text)
7761
+ parts.push(this.escapeMarkdown(text));
7762
+ }
7373
7763
  }
7374
7764
  }
7375
7765
  return parts.join('');
7376
7766
  }
7377
- tableToMarkdown(table) {
7378
- const data = table.toArray();
7379
- if (data.length === 0)
7767
+ runToMarkdown(run, ctx, inLink = false) {
7768
+ const fmt = run.getFormatting();
7769
+ const isMono = !!(fmt.font &&
7770
+ /^(courier|consolas|monaco|menlo|source code|fira code|jetbrains mono)/i.test(fmt.font));
7771
+ const escape = (s) => (isMono ? s : this.escapeMarkdown(s));
7772
+ const segments = [];
7773
+ const markers = [];
7774
+ for (const content of run.getContent()) {
7775
+ switch (content.type) {
7776
+ case 'text':
7777
+ case 'instructionText':
7778
+ segments.push(escape(content.value ?? ''));
7779
+ break;
7780
+ case 'tab':
7781
+ segments.push('\t');
7782
+ break;
7783
+ case 'carriageReturn':
7784
+ segments.push('\n');
7785
+ break;
7786
+ case 'break':
7787
+ if (content.breakType !== 'page' && content.breakType !== 'column') {
7788
+ segments.push(ctx.opts.htmlFallback ? '<br>' : '\n');
7789
+ }
7790
+ break;
7791
+ case 'noBreakHyphen':
7792
+ segments.push('‑');
7793
+ break;
7794
+ case 'symbol':
7795
+ if (content.symbolChar) {
7796
+ const code = parseInt(content.symbolChar, 16);
7797
+ if (!Number.isNaN(code))
7798
+ segments.push(escape(String.fromCharCode(code)));
7799
+ }
7800
+ break;
7801
+ case 'footnoteReference':
7802
+ if (ctx.opts.footnotes && content.footnoteId !== undefined) {
7803
+ markers.push(this.noteMarker(content.footnoteId, 'fn', ctx));
7804
+ }
7805
+ break;
7806
+ case 'endnoteReference':
7807
+ if (ctx.opts.footnotes && content.endnoteId !== undefined) {
7808
+ markers.push(this.noteMarker(content.endnoteId, 'en', ctx));
7809
+ }
7810
+ break;
7811
+ default:
7812
+ break;
7813
+ }
7814
+ }
7815
+ let text = segments.join('');
7816
+ if (text) {
7817
+ if (isMono) {
7818
+ text = `\`${text}\``;
7819
+ }
7820
+ if (fmt.bold && fmt.italic)
7821
+ text = `***${text}***`;
7822
+ else if (fmt.bold)
7823
+ text = `**${text}**`;
7824
+ else if (fmt.italic)
7825
+ text = `*${text}*`;
7826
+ if (fmt.strike || fmt.dstrike)
7827
+ text = `~~${text}~~`;
7828
+ if (ctx.opts.htmlFallback) {
7829
+ if (fmt.superscript)
7830
+ text = `<sup>${text}</sup>`;
7831
+ else if (fmt.subscript)
7832
+ text = `<sub>${text}</sub>`;
7833
+ if (fmt.highlight && fmt.highlight !== 'none')
7834
+ text = `<mark>${text}</mark>`;
7835
+ if (!inLink) {
7836
+ if (fmt.underline && fmt.underline !== 'none')
7837
+ text = `<u>${text}</u>`;
7838
+ if (fmt.color && fmt.color !== 'auto' && /^[0-9a-fA-F]{6}$/.test(fmt.color)) {
7839
+ text = `<span style="color:#${fmt.color}">${text}</span>`;
7840
+ }
7841
+ }
7842
+ }
7843
+ }
7844
+ return text + markers.join('');
7845
+ }
7846
+ noteMarker(id, kind, ctx) {
7847
+ const marker = `${kind}${id}`;
7848
+ if (!ctx.noteMarkers.has(marker)) {
7849
+ ctx.noteMarkers.add(marker);
7850
+ const note = kind === 'fn' ? this.footnoteManager.getFootnote(id) : this.endnoteManager.getEndnote(id);
7851
+ let text = '';
7852
+ if (note) {
7853
+ text = note
7854
+ .getParagraphs()
7855
+ .map((p) => this.paragraphContentToMarkdown(p, ctx))
7856
+ .filter((s) => s !== '')
7857
+ .join(' ')
7858
+ .replace(/\s*\n\s*/g, ' ')
7859
+ .trim();
7860
+ }
7861
+ ctx.notes.push({ marker, text });
7862
+ }
7863
+ return `[^${marker}]`;
7864
+ }
7865
+ tableToMarkdown(table, ctx) {
7866
+ const rows = table.getRows();
7867
+ if (rows.length === 0)
7380
7868
  return [];
7381
- const colCount = Math.max(...data.map((row) => row.length));
7869
+ const isComplex = rows.some((row) => row
7870
+ .getCells()
7871
+ .some((cell) => (cell.getColumnSpan() ?? 1) > 1 ||
7872
+ cell.getVerticalMerge() !== undefined ||
7873
+ cell.hasNestedTables()));
7874
+ if (isComplex && ctx.opts.htmlFallback) {
7875
+ return this.tableToHtmlBlock(table);
7876
+ }
7877
+ const matrix = rows.map((row) => row.getCells().map((cell) => this.cellToMarkdown(cell, ctx)));
7878
+ const colCount = Math.max(...matrix.map((row) => row.length));
7382
7879
  if (colCount === 0)
7383
7880
  return [];
7384
- const normalized = data.map((row) => {
7881
+ const normalized = matrix.map((row) => {
7385
7882
  const padded = [...row];
7386
7883
  while (padded.length < colCount)
7387
7884
  padded.push('');
7388
- return padded.map((cell) => cell.replace(/\|/g, '\\|').replace(/\n/g, ' ').trim());
7885
+ return padded;
7389
7886
  });
7390
7887
  const lines = [];
7391
7888
  lines.push('| ' + normalized[0].join(' | ') + ' |');
@@ -7395,6 +7892,88 @@ export class Document {
7395
7892
  }
7396
7893
  return lines;
7397
7894
  }
7895
+ cellToMarkdown(cell, ctx) {
7896
+ const text = cell
7897
+ .getParagraphs()
7898
+ .map((p) => this.paragraphContentToMarkdown(p, ctx))
7899
+ .filter((s) => s !== '')
7900
+ .join(' ');
7901
+ return text.replace(/\|/g, '\\|').replace(/<br>/g, ' ').replace(/\n/g, ' ').trim();
7902
+ }
7903
+ tableToHtmlBlock(table) {
7904
+ const rows = table.getRows();
7905
+ const rowCells = rows.map((row) => {
7906
+ let gridStart = 0;
7907
+ return row.getCells().map((cell) => {
7908
+ const span = cell.getColumnSpan() ?? 1;
7909
+ const entry = { cell, gridStart, span };
7910
+ gridStart += span;
7911
+ return entry;
7912
+ });
7913
+ });
7914
+ const html = ['<table>'];
7915
+ for (let ri = 0; ri < rowCells.length; ri++) {
7916
+ html.push('<tr>');
7917
+ for (const { cell, gridStart, span } of rowCells[ri]) {
7918
+ if (cell.getVerticalMerge() === 'continue')
7919
+ continue;
7920
+ let rowspan = 1;
7921
+ if (cell.getVerticalMerge() === 'restart') {
7922
+ for (let rj = ri + 1; rj < rowCells.length; rj++) {
7923
+ const below = rowCells[rj].find((e) => e.gridStart === gridStart);
7924
+ if (below?.cell.getVerticalMerge() === 'continue')
7925
+ rowspan++;
7926
+ else
7927
+ break;
7928
+ }
7929
+ }
7930
+ const tag = ri === 0 ? 'th' : 'td';
7931
+ const attrs = (span > 1 ? ` colspan="${span}"` : '') + (rowspan > 1 ? ` rowspan="${rowspan}"` : '');
7932
+ let content = cell
7933
+ .getParagraphs()
7934
+ .map((p) => this.paragraphContentToHTML(p))
7935
+ .filter((s) => s !== '')
7936
+ .join('<br>') || this.escapeHTML(cell.getText());
7937
+ if (cell.hasNestedTables()) {
7938
+ const nestedText = cell
7939
+ .getRawNestedContent()
7940
+ .map((n) => this.extractTextFromRawXml(n.xml))
7941
+ .filter((s) => s)
7942
+ .join(' ');
7943
+ if (nestedText)
7944
+ content += `<br>${this.escapeHTML(nestedText)}`;
7945
+ }
7946
+ html.push(`<${tag}${attrs}>${content}</${tag}>`);
7947
+ }
7948
+ html.push('</tr>');
7949
+ }
7950
+ html.push('</table>');
7951
+ return html;
7952
+ }
7953
+ escapeMarkdown(text) {
7954
+ return text.replace(/([\\`*_[\]<>])/g, '\\$1');
7955
+ }
7956
+ toSingleLine(text) {
7957
+ return text.replace(/<br>/g, ' ').replace(/\s*\n\s*/g, ' ');
7958
+ }
7959
+ extractTextFromRawXml(xml) {
7960
+ if (!xml.includes('<w:t'))
7961
+ return '';
7962
+ const parts = [];
7963
+ const regex = /<w:t(?:\s[^>]*)?>([\s\S]*?)<\/w:t>/g;
7964
+ let match;
7965
+ while ((match = regex.exec(xml)) !== null) {
7966
+ parts.push(match[1] ?? '');
7967
+ }
7968
+ return parts
7969
+ .join('')
7970
+ .replace(/&lt;/g, '<')
7971
+ .replace(/&gt;/g, '>')
7972
+ .replace(/&quot;/g, '"')
7973
+ .replace(/&apos;/g, "'")
7974
+ .replace(/&amp;/g, '&')
7975
+ .trim();
7976
+ }
7398
7977
  toHTML(options) {
7399
7978
  const parts = [];
7400
7979
  let inList = null;
@@ -7485,8 +8064,12 @@ export class Document {
7485
8064
  parts.push(html);
7486
8065
  }
7487
8066
  else if (item instanceof Hyperlink) {
7488
- const url = this.escapeHTML(item.getUrl() || '');
7489
- const linkText = this.escapeHTML(item.getText() || url);
8067
+ let rawUrl = item.getUrl() ?? '';
8068
+ const anchor = item.getAnchor();
8069
+ if (anchor)
8070
+ rawUrl = rawUrl ? `${rawUrl}#${anchor}` : `#${anchor}`;
8071
+ const url = this.escapeHTML(rawUrl);
8072
+ const linkText = this.escapeHTML(item.getText() || rawUrl);
7490
8073
  parts.push(`<a href="${url}">${linkText}</a>`);
7491
8074
  }
7492
8075
  }
@@ -7634,7 +8217,10 @@ export class Document {
7634
8217
  removeParagraph(paragraphOrIndex) {
7635
8218
  let index;
7636
8219
  if (typeof paragraphOrIndex === 'number') {
7637
- index = paragraphOrIndex;
8220
+ const para = this.getParagraphAt(paragraphOrIndex);
8221
+ if (!para)
8222
+ return false;
8223
+ index = this.bodyElements.indexOf(para);
7638
8224
  }
7639
8225
  else {
7640
8226
  index = this.bodyElements.indexOf(paragraphOrIndex);
@@ -7643,13 +8229,8 @@ export class Document {
7643
8229
  const element = this.bodyElements[index];
7644
8230
  if (element instanceof Paragraph) {
7645
8231
  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
- }
8232
+ this.bindTrackingToElement(element);
8233
+ element.clearContent();
7653
8234
  return true;
7654
8235
  }
7655
8236
  this.bodyElements.splice(index, 1);
@@ -7903,16 +8484,6 @@ export class Document {
7903
8484
  for (const paragraph of this.getAllParagraphs()) {
7904
8485
  extractHyperlinksFromParagraph(paragraph);
7905
8486
  }
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
8487
  return hyperlinks;
7917
8488
  }
7918
8489
  collectAllReferencedHyperlinkIds() {
@@ -8037,17 +8608,6 @@ export class Document {
8037
8608
  bookmarks.push({ bookmark, paragraph });
8038
8609
  }
8039
8610
  }
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
8611
  return bookmarks;
8052
8612
  }
8053
8613
  getFields() {
@@ -8057,18 +8617,6 @@ export class Document {
8057
8617
  results.push({ field, paragraph });
8058
8618
  }
8059
8619
  }
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
8620
  return results;
8073
8621
  }
8074
8622
  getImages() {
@@ -8107,7 +8655,7 @@ export class Document {
8107
8655
  await entry.image.updateImageData(result.data);
8108
8656
  }
8109
8657
  if (result.newExtension !== extension) {
8110
- const newFilename = filename.replace(/\.[^.]+$/, `.${result.newExtension}`);
8658
+ const newFilename = this.resolveUniqueMediaFilename(filename.replace(/\.[^.]+$/, `.${result.newExtension}`), filename);
8111
8659
  this.imageManager.updateEntryFilename(image, newFilename);
8112
8660
  for (const entry of entries) {
8113
8661
  this.updateImageRelationshipTarget(entry.relationshipId, filename, newFilename);
@@ -8118,6 +8666,20 @@ export class Document {
8118
8666
  }
8119
8667
  return { optimizedCount, totalSavedBytes: totalSaved };
8120
8668
  }
8669
+ resolveUniqueMediaFilename(candidate, currentFilename) {
8670
+ const dotIndex = candidate.lastIndexOf('.');
8671
+ const base = dotIndex === -1 ? candidate : candidate.slice(0, dotIndex);
8672
+ const ext = dotIndex === -1 ? '' : candidate.slice(dotIndex);
8673
+ const isTaken = (name) => this.zipHandler.hasFile(`word/media/${name}`) ||
8674
+ this.imageManager.isFilenameOwnedByOther(name, currentFilename);
8675
+ let name = candidate;
8676
+ let counter = 1;
8677
+ while (isTaken(name)) {
8678
+ name = `${base}-${counter}${ext}`;
8679
+ counter++;
8680
+ }
8681
+ return name;
8682
+ }
8121
8683
  updateImageRelationshipTarget(relId, oldFilename, newFilename) {
8122
8684
  const rel = this.relationshipManager.getRelationship(relId);
8123
8685
  if (rel) {
@@ -8241,7 +8803,7 @@ export class Document {
8241
8803
  const toRemove = [];
8242
8804
  this.bodyElements.forEach((element, index) => {
8243
8805
  if (element instanceof Paragraph) {
8244
- const isEmpty = element.getText().trim() === '';
8806
+ const isEmpty = this.isParagraphRemovableBlank(element);
8245
8807
  if (isEmpty && lastWasEmpty) {
8246
8808
  toRemove.push(index);
8247
8809
  }
@@ -8325,11 +8887,30 @@ export class Document {
8325
8887
  }
8326
8888
  return { removed, normalized };
8327
8889
  }
8890
+ isParagraphRemovableBlank(para) {
8891
+ if (para.getText().trim() !== '')
8892
+ return false;
8893
+ if (para.formatting.sectPr !== undefined)
8894
+ return false;
8895
+ if (para.getBookmarksStart().length > 0 || para.getBookmarksEnd().length > 0) {
8896
+ return false;
8897
+ }
8898
+ if (para.getCommentsStart().length > 0 || para.getCommentsEnd().length > 0) {
8899
+ return false;
8900
+ }
8901
+ for (const item of para.getContent()) {
8902
+ if (item instanceof ImageRun || item instanceof Shape || item instanceof RangeMarker) {
8903
+ return false;
8904
+ }
8905
+ }
8906
+ return true;
8907
+ }
8328
8908
  setLanguage(language) {
8329
8909
  if (!this.properties) {
8330
8910
  this.properties = {};
8331
8911
  }
8332
8912
  this.properties.language = language;
8913
+ this._corePropsModified = true;
8333
8914
  return this;
8334
8915
  }
8335
8916
  getLanguage() {
@@ -8445,32 +9026,6 @@ export class Document {
8445
9026
  pattern.lastIndex = 0;
8446
9027
  }
8447
9028
  }
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
9029
  return replacedCount;
8475
9030
  }
8476
9031
  static createEmpty() {