docxmlater 11.0.9 → 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 +431 -85
  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 +431 -85
  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 +692 -120
  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
@@ -171,12 +171,28 @@ export class NumberingLevel {
171
171
  private properties: Required<
172
172
  Omit<
173
173
  NumberingLevelProperties,
174
- 'lvlRestart' | 'underline' | 'pStyle' | 'tentative' | 'tplc' | 'lvlPicBulletId' | 'legacy'
174
+ | 'fontSize'
175
+ | 'color'
176
+ | 'lvlRestart'
177
+ | 'underline'
178
+ | 'pStyle'
179
+ | 'tentative'
180
+ | 'tplc'
181
+ | 'lvlPicBulletId'
182
+ | 'legacy'
175
183
  >
176
184
  > &
177
185
  Pick<
178
186
  NumberingLevelProperties,
179
- 'lvlRestart' | 'underline' | 'pStyle' | 'tentative' | 'tplc' | 'lvlPicBulletId' | 'legacy'
187
+ | 'fontSize'
188
+ | 'color'
189
+ | 'lvlRestart'
190
+ | 'underline'
191
+ | 'pStyle'
192
+ | 'tentative'
193
+ | 'tplc'
194
+ | 'lvlPicBulletId'
195
+ | 'legacy'
180
196
  >;
181
197
 
182
198
  /**
@@ -195,11 +211,14 @@ export class NumberingLevel {
195
211
  properties.leftIndent !== undefined ? properties.leftIndent : 720 + properties.level * 360,
196
212
  hangingIndent: properties.hangingIndent !== undefined ? properties.hangingIndent : 360,
197
213
  font: properties.font || 'Calibri',
198
- fontSize: properties.fontSize || 22, // 11pt default
214
+ // undefined = inherit per ECMA-376 (absent w:sz/w:color rPr child);
215
+ // forcing defaults here would inject explicit size/color into
216
+ // re-serialized levels that the source document never specified
217
+ fontSize: properties.fontSize,
199
218
  isLegalNumberingStyle:
200
219
  properties.isLegalNumberingStyle !== undefined ? properties.isLegalNumberingStyle : false,
201
220
  suffix: properties.suffix || 'tab',
202
- color: properties.color || '000000',
221
+ color: properties.color,
203
222
  bold: properties.bold !== undefined ? properties.bold : false,
204
223
  italic: properties.italic !== undefined ? properties.italic : false,
205
224
  underline: properties.underline,
@@ -310,12 +329,28 @@ export class NumberingLevel {
310
329
  getProperties(): Required<
311
330
  Omit<
312
331
  NumberingLevelProperties,
313
- 'lvlRestart' | 'underline' | 'pStyle' | 'tentative' | 'tplc' | 'lvlPicBulletId' | 'legacy'
332
+ | 'fontSize'
333
+ | 'color'
334
+ | 'lvlRestart'
335
+ | 'underline'
336
+ | 'pStyle'
337
+ | 'tentative'
338
+ | 'tplc'
339
+ | 'lvlPicBulletId'
340
+ | 'legacy'
314
341
  >
315
342
  > &
316
343
  Pick<
317
344
  NumberingLevelProperties,
318
- 'lvlRestart' | 'underline' | 'pStyle' | 'tentative' | 'tplc' | 'lvlPicBulletId' | 'legacy'
345
+ | 'fontSize'
346
+ | 'color'
347
+ | 'lvlRestart'
348
+ | 'underline'
349
+ | 'pStyle'
350
+ | 'tentative'
351
+ | 'tplc'
352
+ | 'lvlPicBulletId'
353
+ | 'legacy'
319
354
  > {
320
355
  return { ...this.properties };
321
356
  }
@@ -616,13 +651,15 @@ export class NumberingLevel {
616
651
  // ... b, bCs, i, iCs, ... color (#19), ... sz (#24), szCs (#25), ... u (#27)
617
652
 
618
653
  // Color (#19)
619
- if (this.properties.color) {
654
+ if (this.properties.color !== undefined) {
620
655
  rPrChildren.push(XMLBuilder.wSelf('color', { 'w:val': this.properties.color }));
621
656
  }
622
657
 
623
- // Font size (#24, #25)
624
- rPrChildren.push(XMLBuilder.wSelf('sz', { 'w:val': this.properties.fontSize.toString() }));
625
- rPrChildren.push(XMLBuilder.wSelf('szCs', { 'w:val': this.properties.fontSize.toString() }));
658
+ // Font size (#24, #25) — omit when unset so the level inherits size
659
+ if (this.properties.fontSize !== undefined) {
660
+ rPrChildren.push(XMLBuilder.wSelf('sz', { 'w:val': this.properties.fontSize.toString() }));
661
+ rPrChildren.push(XMLBuilder.wSelf('szCs', { 'w:val': this.properties.fontSize.toString() }));
662
+ }
626
663
 
627
664
  // Underline (#27)
628
665
  if (this.properties.underline) {
@@ -1123,9 +1160,10 @@ export class NumberingLevel {
1123
1160
  if (hangingMatch?.[1]) hangingIndent = parseInt(hangingMatch[1], 10);
1124
1161
  }
1125
1162
 
1126
- // Extract font and size from <w:rPr>
1163
+ // Extract font and size from <w:rPr>. An absent w:sz means the level
1164
+ // inherits its size — keep it undefined so toXML doesn't fabricate one.
1127
1165
  let font = 'Calibri';
1128
- let fontSize = 22;
1166
+ let fontSize: number | undefined;
1129
1167
 
1130
1168
  const rFontsMatch = /<w:rFonts[^>]*\/>/.exec(xml);
1131
1169
  if (rFontsMatch) {
@@ -542,6 +542,10 @@ export class NumberingManager {
542
542
  numLevel.setLeftIndent(leftIndent);
543
543
  numLevel.setHangingIndent(hangingIndent);
544
544
 
545
+ // NumberingLevel setters don't notify the manager; without this flag the
546
+ // save pipeline preserves the original numbering.xml and drops the change
547
+ this.markAbstractNumberingModified(instance.getAbstractNumId());
548
+
545
549
  return true;
546
550
  }
547
551
 
@@ -585,6 +589,10 @@ export class NumberingManager {
585
589
  level.setHangingIndent(standardIndent.hangingIndent);
586
590
  }
587
591
 
592
+ // NumberingLevel setters don't notify the manager; without this flag the
593
+ // save pipeline preserves the original numbering.xml and drops the change
594
+ this.markAbstractNumberingModified(instance.getAbstractNumId());
595
+
588
596
  return true;
589
597
  }
590
598
 
@@ -708,8 +716,10 @@ export class NumberingManager {
708
716
  * For each group with >1 member, picks the lowest abstractNumId as canonical,
709
717
  * remaps all instances pointing to non-canonical IDs, and removes duplicates.
710
718
  *
711
- * This is safe because multiple num instances can reference the same abstractNum —
712
- * each instance maintains its own independent counter via level overrides.
719
+ * Numbering counters belong to the abstract numbering definition: instances
720
+ * sharing an abstractNum continue one sequence in document order unless a
721
+ * startOverride restarts them (ECMA-376 §17.9.27). Remapped instances get a
722
+ * startOverride per counted level so each list keeps its independent restart.
713
723
  *
714
724
  * @param options Optional configuration (e.g., protected IDs to skip)
715
725
  * @returns Summary of what was consolidated
@@ -748,9 +758,23 @@ export class NumberingManager {
748
758
  groupsConsolidated++;
749
759
 
750
760
  // Remap instances pointing to duplicate abstractNums
761
+ const canonicalAbstract = this.abstractNumberings.get(canonicalId);
751
762
  for (const instance of this.instances.values()) {
752
763
  if (duplicateIds.has(instance.getAbstractNumId())) {
753
764
  instance.setAbstractNumId(canonicalId);
765
+
766
+ // Counters live on the abstractNum, so without a startOverride the
767
+ // remapped list would continue the canonical sequence instead of
768
+ // restarting — bullets carry no counter and are skipped
769
+ if (canonicalAbstract) {
770
+ for (const level of canonicalAbstract.getAllLevels()) {
771
+ const levelIndex = level.getLevel();
772
+ if (level.getFormat() === 'bullet') continue;
773
+ if (instance.getLevelOverride(levelIndex) !== undefined) continue;
774
+ instance.setLevelOverride(levelIndex, level.getProperties().start);
775
+ }
776
+ }
777
+
754
778
  this._modifiedNumIds.add(instance.getNumId());
755
779
  instancesRemapped++;
756
780
  }
@@ -785,11 +809,16 @@ export class NumberingManager {
785
809
 
786
810
  for (const level of abstractNum.getAllLevels()) {
787
811
  const props = level.getProperties();
812
+ // pStyle, lvlPicBulletId, and legacy all affect rendering (style-to-level
813
+ // linkage, picture bullet image, legacy indentation), so definitions
814
+ // differing on any of them must not be treated as duplicates
788
815
  parts.push(
789
816
  `${props.level}|${props.format}|${props.text}|${props.font}|${props.fontSize}|` +
790
817
  `${props.color}|${props.leftIndent}|${props.hangingIndent}|${props.alignment}|` +
791
818
  `${props.start}|${props.bold}|${props.italic}|${props.underline ?? ''}|` +
792
- `${props.suffix}|${props.isLegalNumberingStyle}|${props.lvlRestart ?? ''}`
819
+ `${props.suffix}|${props.isLegalNumberingStyle}|${props.lvlRestart ?? ''}|` +
820
+ `${props.pStyle ?? ''}|${props.lvlPicBulletId ?? ''}|` +
821
+ `${JSON.stringify(props.legacy ?? null)}`
793
822
  );
794
823
  }
795
824
 
@@ -1636,6 +1636,8 @@ export class Style {
1636
1636
  },
1637
1637
  runFormatting: {
1638
1638
  font: 'Verdana',
1639
+ fontHAnsi: 'Verdana',
1640
+ fontCs: 'Verdana',
1639
1641
  size: 12,
1640
1642
  color: '000000',
1641
1643
  },
@@ -1684,6 +1686,8 @@ export class Style {
1684
1686
  },
1685
1687
  runFormatting: {
1686
1688
  font: 'Verdana',
1689
+ fontHAnsi: 'Verdana',
1690
+ fontCs: 'Verdana',
1687
1691
  size: sizes[level - 1],
1688
1692
  bold: true,
1689
1693
  color: '000000',
@@ -1713,6 +1717,8 @@ export class Style {
1713
1717
  uiPriority: 9,
1714
1718
  runFormatting: {
1715
1719
  font: 'Verdana',
1720
+ fontHAnsi: 'Verdana',
1721
+ fontCs: 'Verdana',
1716
1722
  size: sizes[level - 1],
1717
1723
  bold: true,
1718
1724
  color: '000000',
@@ -1738,6 +1744,8 @@ export class Style {
1738
1744
  },
1739
1745
  runFormatting: {
1740
1746
  font: 'Verdana',
1747
+ fontHAnsi: 'Verdana',
1748
+ fontCs: 'Verdana',
1741
1749
  size: 28,
1742
1750
  color: '000000',
1743
1751
  },
@@ -1762,6 +1770,8 @@ export class Style {
1762
1770
  },
1763
1771
  runFormatting: {
1764
1772
  font: 'Verdana',
1773
+ fontHAnsi: 'Verdana',
1774
+ fontCs: 'Verdana',
1765
1775
  size: 14,
1766
1776
  color: '000000',
1767
1777
  italic: true,
@@ -1798,6 +1808,8 @@ export class Style {
1798
1808
  },
1799
1809
  runFormatting: {
1800
1810
  font: 'Verdana',
1811
+ fontHAnsi: 'Verdana',
1812
+ fontCs: 'Verdana',
1801
1813
  size: 12,
1802
1814
  color: '000000',
1803
1815
  },
@@ -1818,6 +1830,8 @@ export class Style {
1818
1830
  runFormatting: {
1819
1831
  bold: true,
1820
1832
  font: 'Verdana',
1833
+ fontHAnsi: 'Verdana',
1834
+ fontCs: 'Verdana',
1821
1835
  size: 14,
1822
1836
  color: '000000', // Black
1823
1837
  },
@@ -74,6 +74,9 @@ export class StylesManager {
74
74
  // Track which specific styles have been modified (for selective merging)
75
75
  private _modifiedStyleIds = new Set<string>();
76
76
 
77
+ // Track which styles have been removed (for removal from original XML during merge)
78
+ private _removedStyleIds = new Set<string>();
79
+
77
80
  // Latent styles configuration
78
81
  private latentStyles?: LatentStylesConfig;
79
82
  private latentStyleExceptions: LatentStyleException[] = [];
@@ -176,6 +179,9 @@ export class StylesManager {
176
179
  }
177
180
  this.styles.set(style.getStyleId(), style);
178
181
  this._modifiedStyleIds.add(style.getStyleId());
182
+ // Re-adding a previously removed style cancels the pending removal —
183
+ // otherwise the merge would strip the style right after appending it
184
+ this._removedStyleIds.delete(style.getStyleId());
179
185
  this._modified = true;
180
186
  return this;
181
187
  }
@@ -218,7 +224,10 @@ export class StylesManager {
218
224
  const source = this.getStyle(sourceId);
219
225
  if (!source) return undefined;
220
226
 
221
- const props = source.getProperties();
227
+ // Use Style.clone()'s deepClone path so nested formatting (runFormatting,
228
+ // paragraphFormatting, tableStyle) is fully copied — not reference-shared
229
+ // with the source — matching this method's documented deep-copy guarantee.
230
+ const props = source.clone().getProperties();
222
231
  props.styleId = newId;
223
232
  props.name = newName ?? newId;
224
233
  props.isDefault = false; // Clones should never be default
@@ -249,7 +258,15 @@ export class StylesManager {
249
258
  * @returns True if the style was removed
250
259
  */
251
260
  removeStyle(styleId: string): boolean {
252
- return this.styles.delete(styleId);
261
+ const deleted = this.styles.delete(styleId);
262
+ if (deleted) {
263
+ // Drop from the modified set so the merge cannot resurrect the style
264
+ // (getStyle lazily re-creates built-ins, which would re-append it)
265
+ this._modifiedStyleIds.delete(styleId);
266
+ this._removedStyleIds.add(styleId);
267
+ this._modified = true;
268
+ }
269
+ return deleted;
253
270
  }
254
271
 
255
272
  /**
@@ -276,6 +293,7 @@ export class StylesManager {
276
293
  resetModified(): void {
277
294
  this._modified = false;
278
295
  this._modifiedStyleIds.clear();
296
+ this._removedStyleIds.clear();
279
297
  }
280
298
 
281
299
  /**
@@ -287,6 +305,15 @@ export class StylesManager {
287
305
  return new Set(this._modifiedStyleIds);
288
306
  }
289
307
 
308
+ /**
309
+ * Gets the IDs of styles that have been removed since loading
310
+ * Used for removal from original styles.xml during merge
311
+ * @returns Set of removed style IDs
312
+ */
313
+ getRemovedStyleIds(): Set<string> {
314
+ return new Set(this._removedStyleIds);
315
+ }
316
+
290
317
  /**
291
318
  * Gets styles by type
292
319
  * @param type - Style type to filter by
@@ -21,6 +21,7 @@ import { Hyperlink } from '../elements/Hyperlink.js';
21
21
  import { Paragraph } from '../elements/Paragraph.js';
22
22
  import { Table } from '../elements/Table.js';
23
23
  import { StructuredDocumentTag } from '../elements/StructuredDocumentTag.js';
24
+ import { StylesManager } from '../formatting/StylesManager.js';
24
25
 
25
26
  export interface CleanupOptions {
26
27
  /** Unlock all SDTs to enable editing */
@@ -264,24 +265,98 @@ export class CleanupHelper {
264
265
  }
265
266
 
266
267
  private cleanupStyles(): number {
267
- // Implementation for unused styles removal
268
- // Scan all paragraphs and runs for used styles
268
+ const stylesManager = this.doc.getStylesManager();
269
269
  const usedStyles = new Set<string>();
270
- for (const para of this.doc.getAllParagraphs()) {
270
+
271
+ const collectParagraph = (para: Paragraph): void => {
271
272
  const paraStyle = para.getFormatting().style;
272
273
  if (paraStyle) usedStyles.add(paraStyle);
273
274
  for (const run of para.getRuns()) {
274
275
  const runStyle = run.getFormatting().characterStyle;
275
276
  if (runStyle) usedStyles.add(runStyle);
276
277
  }
278
+ };
279
+
280
+ // Body paragraphs (getAllParagraphs walks table cells and nested SDTs)
281
+ for (const para of this.doc.getAllParagraphs()) {
282
+ collectParagraph(para);
283
+ }
284
+
285
+ // Table styles (w:tblStyle) — tables reference styles without any
286
+ // paragraph carrying the ID, so a paragraph-only scan misses them
287
+ for (const table of this.doc.getAllTables()) {
288
+ const tableStyle = table.getFormatting().style;
289
+ if (tableStyle) usedStyles.add(tableStyle);
290
+ }
291
+
292
+ // Headers and footers live outside the body walk
293
+ const headerFooterManager = this.doc.getHeaderFooterManager();
294
+ const headerFooterElements = [
295
+ ...headerFooterManager.getAllHeaders().flatMap((entry) => entry.header.getElements()),
296
+ ...headerFooterManager.getAllFooters().flatMap((entry) => entry.footer.getElements()),
297
+ ];
298
+ for (const element of headerFooterElements) {
299
+ if (element instanceof Paragraph) {
300
+ collectParagraph(element);
301
+ } else if (element instanceof Table) {
302
+ const tableStyle = element.getFormatting().style;
303
+ if (tableStyle) usedStyles.add(tableStyle);
304
+ for (const row of element.getRows()) {
305
+ for (const cell of row.getCells()) {
306
+ for (const para of cell.getParagraphs()) {
307
+ collectParagraph(para);
308
+ }
309
+ }
310
+ }
311
+ }
312
+ }
313
+
314
+ // Footnotes and endnotes also live outside the body walk
315
+ for (const footnote of this.doc.getFootnoteManager().getAllFootnotes()) {
316
+ for (const para of footnote.getParagraphs()) {
317
+ collectParagraph(para);
318
+ }
319
+ }
320
+ for (const endnote of this.doc.getEndnoteManager().getAllEndnotes()) {
321
+ for (const para of endnote.getParagraphs()) {
322
+ collectParagraph(para);
323
+ }
324
+ }
325
+
326
+ // Numbering levels can bind a paragraph style (w:pStyle, ECMA-376 §17.9.23)
327
+ for (const abstractNum of this.doc.getNumberingManager().getAllAbstractNumberings()) {
328
+ for (const level of abstractNum.getAllLevels()) {
329
+ const pStyle = level.getParagraphStyle();
330
+ if (pStyle) usedStyles.add(pStyle);
331
+ }
332
+ }
333
+
334
+ // Expand to basedOn/link/next ancestors transitively — removing a chain
335
+ // member leaves dangling references that break style resolution in Word
336
+ const allStyles = stylesManager.getAllStyles();
337
+ const stylesById = new Map(allStyles.map((style) => [style.getStyleId(), style]));
338
+ const pending = [...usedStyles];
339
+ while (pending.length > 0) {
340
+ const style = stylesById.get(pending.pop()!);
341
+ if (!style) continue;
342
+ const props = style.getProperties();
343
+ for (const ref of [props.basedOn, props.link, props.next]) {
344
+ if (ref && !usedStyles.has(ref)) {
345
+ usedStyles.add(ref);
346
+ pending.push(ref);
347
+ }
348
+ }
277
349
  }
278
350
 
279
- // Remove unused styles
351
+ // Remove unused styles — but never built-ins or part defaults, which
352
+ // apply without being referenced (mirrors StylesManager.cleanupUnusedStyles)
280
353
  let removed = 0;
281
- const allStyles = this.doc.getStylesManager().getAllStyles();
282
354
  for (const style of allStyles) {
283
- if (!usedStyles.has(style.getStyleId())) {
284
- this.doc.getStylesManager().removeStyle(style.getStyleId());
355
+ const styleId = style.getStyleId();
356
+ if (usedStyles.has(styleId)) continue;
357
+ if (StylesManager.isBuiltInStyle(styleId)) continue;
358
+ if (style.getProperties().isDefault) continue;
359
+ if (stylesManager.removeStyle(styleId)) {
285
360
  removed++;
286
361
  }
287
362
  }
@@ -330,38 +405,55 @@ export class CleanupHelper {
330
405
  return removed;
331
406
  }
332
407
 
408
+ // Lock state must be cleared on the in-memory model, not the ZIP copy of
409
+ // word/document.xml: prepareSave() regenerates document.xml from the model,
410
+ // so raw-XML edits here would be overwritten and the locks re-emitted.
333
411
  private unlockFields(): number {
334
- const zipHandler = this.doc.getZipHandler();
335
- const docXml = zipHandler.getFileAsString('word/document.xml');
336
- if (!docXml) return 0;
412
+ let count = 0;
337
413
 
338
- // Count matches first, then replace (avoid regex re-execution)
339
- const pattern = /w:fldLock="(1|true)"/g;
340
- const matches = docXml.match(pattern) || [];
341
- if (matches.length === 0) return 0;
414
+ for (const para of this.doc.getAllParagraphs()) {
415
+ // w:fldChar w:fldLock on complex-field runs (ECMA-376 §17.16.18).
416
+ // getRuns() includes runs inside revisions and hyperlinks; the
417
+ // returned content elements are live references, so clearing the
418
+ // flag mutates the run itself.
419
+ for (const run of para.getRuns()) {
420
+ for (const item of run.getContent()) {
421
+ if (item.type === 'fieldChar' && item.fieldCharLocked === true) {
422
+ item.fieldCharLocked = undefined;
423
+ count++;
424
+ }
425
+ }
426
+ }
342
427
 
343
- // Remove w:fldLock="1" or w:fldLock="true"
344
- const updatedXml = docXml.replace(pattern, '');
345
- zipHandler.updateFile('word/document.xml', updatedXml);
428
+ // w:fldLock on simple fields (w:fldSimple, ECMA-376 §17.16.16).
429
+ // Field keeps the flag private with no mutator, so clear it
430
+ // structurally — the regenerated fldSimple then omits the attribute.
431
+ for (const item of para.getContent()) {
432
+ if (item instanceof Field) {
433
+ const lockable = item as unknown as { fldLock?: boolean };
434
+ if (lockable.fldLock === true) {
435
+ lockable.fldLock = undefined;
436
+ count++;
437
+ }
438
+ }
439
+ }
440
+ }
346
441
 
347
- return matches.length;
442
+ return count;
348
443
  }
349
444
 
350
445
  private unlockFrames(): number {
351
- const zipHandler = this.doc.getZipHandler();
352
- const docXml = zipHandler.getFileAsString('word/document.xml');
353
- if (!docXml) return 0;
354
-
355
- // Count matches first, then replace (avoid regex re-execution)
356
- const pattern = /w:anchorLock="(1|true)"/g;
357
- const matches = docXml.match(pattern) || [];
358
- if (matches.length === 0) return 0;
446
+ let count = 0;
359
447
 
360
- // Remove w:anchorLock="1" or w:anchorLock="true"
361
- const updatedXml = docXml.replace(pattern, '');
362
- zipHandler.updateFile('word/document.xml', updatedXml);
448
+ for (const para of this.doc.getAllParagraphs()) {
449
+ const framePr = para.getFormatting().framePr;
450
+ if (framePr?.anchorLock === true) {
451
+ para.setFrameProperties({ ...framePr, anchorLock: undefined });
452
+ count++;
453
+ }
454
+ }
363
455
 
364
- return matches.length;
456
+ return count;
365
457
  }
366
458
 
367
459
  private sanitizeTables(): number {
@@ -144,6 +144,26 @@ export function optimizePng(buffer: Buffer): Buffer | null {
144
144
  // BMP → PNG Conversion
145
145
  // =============================================================================
146
146
 
147
+ /**
148
+ * Maps a 32-bit BI_BITFIELDS channel mask to its byte index within a
149
+ * little-endian pixel. Only byte-aligned 8-bit masks map losslessly onto
150
+ * PNG's 8-bit channels; anything else returns null.
151
+ */
152
+ function maskToByteIndex(mask: number): number | null {
153
+ switch (mask) {
154
+ case 0x000000ff:
155
+ return 0;
156
+ case 0x0000ff00:
157
+ return 1;
158
+ case 0x00ff0000:
159
+ return 2;
160
+ case 0xff000000:
161
+ return 3;
162
+ default:
163
+ return null;
164
+ }
165
+ }
166
+
147
167
  /**
148
168
  * Converts a BMP buffer to PNG format. Lossless conversion.
149
169
  * Supports 24-bit (RGB) and 32-bit (RGBA) uncompressed BMPs.
@@ -186,6 +206,45 @@ export function convertBmpToPng(buffer: Buffer): Buffer | null {
186
206
  // Validate buffer has enough pixel data
187
207
  if (pixelDataOffset + absHeight * rowSize > buffer.length) return null;
188
208
 
209
+ // Channel byte offsets within a pixel; defaults match the standard BGR(A) layout
210
+ let rOff = 2;
211
+ let gOff = 1;
212
+ let bOff = 0;
213
+ let aOff = 3;
214
+ let forceOpaqueAlpha = false;
215
+
216
+ if (bitsPerPixel === 32) {
217
+ if (compression === 3) {
218
+ // BI_BITFIELDS: channel masks follow BITMAPINFOHEADER (the same file
219
+ // offsets hold the masks inside V4/V5 headers), so they must be honored
220
+ // to keep the conversion lossless
221
+ if (buffer.length < 66) return null;
222
+ const rIdx = maskToByteIndex(buffer.readUInt32LE(54));
223
+ const gIdx = maskToByteIndex(buffer.readUInt32LE(58));
224
+ const bIdx = maskToByteIndex(buffer.readUInt32LE(62));
225
+ if (rIdx === null || gIdx === null || bIdx === null) return null;
226
+ if (rIdx === gIdx || rIdx === bIdx || gIdx === bIdx) return null;
227
+ rOff = rIdx;
228
+ gOff = gIdx;
229
+ bOff = bIdx;
230
+ aOff = 6 - rIdx - gIdx - bIdx; // the byte not claimed by R/G/B carries alpha
231
+ } else {
232
+ // BI_RGB: the 4th byte is reserved and writers commonly zero it, so
233
+ // copying it as alpha would render the whole image transparent. Treat it
234
+ // as alpha only when at least one pixel carries a non-zero value.
235
+ forceOpaqueAlpha = true;
236
+ for (let y = 0; forceOpaqueAlpha && y < absHeight; y++) {
237
+ const rowOffset = pixelDataOffset + y * rowSize;
238
+ for (let x = 0; x < width; x++) {
239
+ if (buffer[rowOffset + x * 4 + 3] !== 0) {
240
+ forceOpaqueAlpha = false;
241
+ break;
242
+ }
243
+ }
244
+ }
245
+ }
246
+ }
247
+
189
248
  // PNG color type: 2 = RGB (24-bit), 6 = RGBA (32-bit)
190
249
  const colorType = bitsPerPixel === 32 ? 6 : 2;
191
250
  const pngBytesPerPixel = bitsPerPixel === 32 ? 4 : 3;
@@ -216,13 +275,13 @@ export function convertBmpToPng(buffer: Buffer): Buffer | null {
216
275
  const bmpPixelOffset = bmpRowOffset + x * bytesPerPixel;
217
276
  const pngPixelOffset = pngRowOffset + 1 + x * pngBytesPerPixel;
218
277
 
219
- // Convert BGR(A) RGB(A)
220
- rawData[pngPixelOffset] = buffer[bmpPixelOffset + 2]!; // R
221
- rawData[pngPixelOffset + 1] = buffer[bmpPixelOffset + 1]!; // G
222
- rawData[pngPixelOffset + 2] = buffer[bmpPixelOffset]!; // B
278
+ // Route channels by resolved byte offsets (BGR(A) unless masks say otherwise)
279
+ rawData[pngPixelOffset] = buffer[bmpPixelOffset + rOff]!; // R
280
+ rawData[pngPixelOffset + 1] = buffer[bmpPixelOffset + gOff]!; // G
281
+ rawData[pngPixelOffset + 2] = buffer[bmpPixelOffset + bOff]!; // B
223
282
 
224
283
  if (bitsPerPixel === 32) {
225
- rawData[pngPixelOffset + 3] = buffer[bmpPixelOffset + 3]!; // A
284
+ rawData[pngPixelOffset + 3] = forceOpaqueAlpha ? 0xff : buffer[bmpPixelOffset + aOff]!; // A
226
285
  }
227
286
  }
228
287
  }
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ export {
13
13
  DocumentOptions,
14
14
  DocumentLoadOptions,
15
15
  DocumentPart,
16
+ MarkdownConversionOptions,
16
17
  } from './core/Document.js';
17
18
 
18
19
  // =============================================================================
@@ -411,6 +412,12 @@ export {
411
412
  CorruptionType,
412
413
  } from './utils/corruptionDetection.js';
413
414
  export { isError, toError, wrapError, getErrorMessage } from './utils/errorHandling.js';
415
+ // Deliberately on the stable path: ResourceLimitError is the security-relevant,
416
+ // catchable error thrown by Document.load/loadFromBuffer for oversized/zip-bomb
417
+ // untrusted input, and SizeLimitOptions is its tuning type. The rest of the
418
+ // DocxError hierarchy stays on the `docxmlater/internal` subpath.
419
+ export { ResourceLimitError } from './zip/errors.js';
420
+ export type { SizeLimitOptions } from './zip/types.js';
414
421
  export {
415
422
  REVISION_RULES,
416
423
  ValidationSeverity,
package/src/internal.ts CHANGED
@@ -46,6 +46,7 @@ export {
46
46
  CorruptedArchiveError,
47
47
  MissingRequiredFileError,
48
48
  FileOperationError,
49
+ ResourceLimitError,
49
50
  } from './zip/errors.js';
50
51
 
51
52
  // XML layer
@@ -67,9 +67,22 @@ export class CompatibilityUpgrader {
67
67
  const selfClosingCompat = /<w:compat\s*\/>/.exec(settingsXml);
68
68
 
69
69
  if (!compatBlockMatch && !selfClosingCompat) {
70
- // No w:compat block — insert a full modern block before </w:settings>
70
+ // No w:compat block — insert a full modern block. CT_Settings is an
71
+ // xsd:sequence where w:compat must precede rsids, mathPr, themeFontLang,
72
+ // clrSchemeMapping, shapeDefaults, decimalSymbol, and listSeparator, so
73
+ // anchor before the earliest of those; append before </w:settings> only
74
+ // when none are present.
71
75
  const modernBlock = CompatibilityUpgrader.buildModernCompatBlock(MODERN_COMPAT_SETTINGS);
72
- const xml = settingsXml.replace(/<\/w:settings>/, modernBlock + '\n</w:settings>');
76
+ const followingSibling =
77
+ /<(?:w:rsids|m:mathPr|w:themeFontLang|w:clrSchemeMapping|w:shapeDefaults|w:decimalSymbol|w:listSeparator)[\s/>]/.exec(
78
+ settingsXml
79
+ );
80
+ const xml = followingSibling
81
+ ? settingsXml.slice(0, followingSibling.index) +
82
+ modernBlock +
83
+ '\n ' +
84
+ settingsXml.slice(followingSibling.index)
85
+ : settingsXml.replace(/<\/w:settings>/, modernBlock + '\n</w:settings>');
73
86
 
74
87
  for (const s of MODERN_COMPAT_SETTINGS) {
75
88
  addedSettings.push(s.name);