docxmlater 10.1.4 → 10.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +759 -754
- package/dist/constants/legacyCompatFlags.js +1 -1
- package/dist/constants/legacyCompatFlags.js.map +1 -1
- package/dist/constants/limits.js.map +1 -1
- package/dist/core/Document.d.ts +51 -50
- package/dist/core/Document.d.ts.map +1 -1
- package/dist/core/Document.js +486 -471
- package/dist/core/Document.js.map +1 -1
- package/dist/core/DocumentContent.d.ts +9 -9
- package/dist/core/DocumentContent.d.ts.map +1 -1
- package/dist/core/DocumentContent.js +1 -1
- package/dist/core/DocumentContent.js.map +1 -1
- package/dist/core/DocumentGenerator.d.ts +11 -11
- package/dist/core/DocumentGenerator.d.ts.map +1 -1
- package/dist/core/DocumentGenerator.js +251 -251
- package/dist/core/DocumentGenerator.js.map +1 -1
- package/dist/core/DocumentIdManager.js.map +1 -1
- package/dist/core/DocumentParser.d.ts +15 -15
- package/dist/core/DocumentParser.d.ts.map +1 -1
- package/dist/core/DocumentParser.js +2123 -2155
- package/dist/core/DocumentParser.js.map +1 -1
- package/dist/core/DocumentValidator.d.ts.map +1 -1
- package/dist/core/DocumentValidator.js +2 -5
- package/dist/core/DocumentValidator.js.map +1 -1
- package/dist/core/Relationship.js.map +1 -1
- package/dist/core/RelationshipManager.d.ts.map +1 -1
- package/dist/core/RelationshipManager.js +3 -3
- package/dist/core/RelationshipManager.js.map +1 -1
- package/dist/elements/AlternateContent.js.map +1 -1
- package/dist/elements/Bookmark.d.ts.map +1 -1
- package/dist/elements/Bookmark.js +3 -1
- package/dist/elements/Bookmark.js.map +1 -1
- package/dist/elements/BookmarkManager.d.ts.map +1 -1
- package/dist/elements/BookmarkManager.js.map +1 -1
- package/dist/elements/Comment.d.ts.map +1 -1
- package/dist/elements/Comment.js +9 -6
- package/dist/elements/Comment.js.map +1 -1
- package/dist/elements/CommentManager.d.ts.map +1 -1
- package/dist/elements/CommentManager.js +18 -17
- package/dist/elements/CommentManager.js.map +1 -1
- package/dist/elements/CommonTypes.d.ts +21 -21
- package/dist/elements/CommonTypes.d.ts.map +1 -1
- package/dist/elements/CommonTypes.js +56 -56
- package/dist/elements/CommonTypes.js.map +1 -1
- package/dist/elements/CustomXml.js.map +1 -1
- package/dist/elements/Endnote.d.ts.map +1 -1
- package/dist/elements/Endnote.js +6 -6
- package/dist/elements/Endnote.js.map +1 -1
- package/dist/elements/EndnoteManager.d.ts.map +1 -1
- package/dist/elements/EndnoteManager.js +6 -7
- package/dist/elements/EndnoteManager.js.map +1 -1
- package/dist/elements/Field.d.ts.map +1 -1
- package/dist/elements/Field.js +82 -25
- package/dist/elements/Field.js.map +1 -1
- package/dist/elements/FieldHelpers.d.ts.map +1 -1
- package/dist/elements/FieldHelpers.js.map +1 -1
- package/dist/elements/FontManager.d.ts.map +1 -1
- package/dist/elements/FontManager.js +1 -1
- package/dist/elements/FontManager.js.map +1 -1
- package/dist/elements/Footer.js +2 -2
- package/dist/elements/Footer.js.map +1 -1
- package/dist/elements/Footnote.d.ts.map +1 -1
- package/dist/elements/Footnote.js +6 -6
- package/dist/elements/Footnote.js.map +1 -1
- package/dist/elements/FootnoteManager.d.ts.map +1 -1
- package/dist/elements/FootnoteManager.js +6 -7
- package/dist/elements/FootnoteManager.js.map +1 -1
- package/dist/elements/Header.js +2 -2
- package/dist/elements/Header.js.map +1 -1
- package/dist/elements/HeaderFooterManager.js.map +1 -1
- package/dist/elements/Hyperlink.d.ts +5 -3
- package/dist/elements/Hyperlink.d.ts.map +1 -1
- package/dist/elements/Hyperlink.js +134 -76
- package/dist/elements/Hyperlink.js.map +1 -1
- package/dist/elements/Image.d.ts.map +1 -1
- package/dist/elements/Image.js +238 -106
- package/dist/elements/Image.js.map +1 -1
- package/dist/elements/ImageManager.d.ts.map +1 -1
- package/dist/elements/ImageManager.js +1 -1
- package/dist/elements/ImageManager.js.map +1 -1
- package/dist/elements/ImageRun.js +1 -1
- package/dist/elements/ImageRun.js.map +1 -1
- package/dist/elements/MathElement.js.map +1 -1
- package/dist/elements/Paragraph.d.ts +24 -24
- package/dist/elements/Paragraph.d.ts.map +1 -1
- package/dist/elements/Paragraph.js +181 -188
- package/dist/elements/Paragraph.js.map +1 -1
- package/dist/elements/PreservedElement.js.map +1 -1
- package/dist/elements/PropertyChangeTypes.d.ts.map +1 -1
- package/dist/elements/PropertyChangeTypes.js +6 -6
- package/dist/elements/PropertyChangeTypes.js.map +1 -1
- package/dist/elements/RangeMarker.d.ts.map +1 -1
- package/dist/elements/RangeMarker.js.map +1 -1
- package/dist/elements/Revision.d.ts.map +1 -1
- package/dist/elements/Revision.js +4 -5
- package/dist/elements/Revision.js.map +1 -1
- package/dist/elements/RevisionContent.js.map +1 -1
- package/dist/elements/RevisionManager.d.ts.map +1 -1
- package/dist/elements/RevisionManager.js +40 -48
- package/dist/elements/RevisionManager.js.map +1 -1
- package/dist/elements/Run.d.ts +16 -16
- package/dist/elements/Run.d.ts.map +1 -1
- package/dist/elements/Run.js +256 -238
- package/dist/elements/Run.js.map +1 -1
- package/dist/elements/Section.d.ts.map +1 -1
- package/dist/elements/Section.js +36 -11
- package/dist/elements/Section.js.map +1 -1
- package/dist/elements/Shape.d.ts.map +1 -1
- package/dist/elements/Shape.js.map +1 -1
- package/dist/elements/StructuredDocumentTag.d.ts +6 -6
- package/dist/elements/StructuredDocumentTag.d.ts.map +1 -1
- package/dist/elements/StructuredDocumentTag.js +99 -104
- package/dist/elements/StructuredDocumentTag.js.map +1 -1
- package/dist/elements/Table.d.ts +11 -11
- package/dist/elements/Table.d.ts.map +1 -1
- package/dist/elements/Table.js +102 -107
- package/dist/elements/Table.js.map +1 -1
- package/dist/elements/TableCell.d.ts +10 -10
- package/dist/elements/TableCell.d.ts.map +1 -1
- package/dist/elements/TableCell.js +105 -106
- package/dist/elements/TableCell.js.map +1 -1
- package/dist/elements/TableGridChange.d.ts.map +1 -1
- package/dist/elements/TableGridChange.js.map +1 -1
- package/dist/elements/TableOfContents.d.ts.map +1 -1
- package/dist/elements/TableOfContents.js +4 -4
- package/dist/elements/TableOfContents.js.map +1 -1
- package/dist/elements/TableOfContentsElement.js.map +1 -1
- package/dist/elements/TableRow.d.ts.map +1 -1
- package/dist/elements/TableRow.js +13 -6
- package/dist/elements/TableRow.js.map +1 -1
- package/dist/elements/TextBox.d.ts.map +1 -1
- package/dist/elements/TextBox.js +3 -5
- package/dist/elements/TextBox.js.map +1 -1
- package/dist/formatting/AbstractNumbering.d.ts +4 -4
- package/dist/formatting/AbstractNumbering.d.ts.map +1 -1
- package/dist/formatting/AbstractNumbering.js +54 -49
- package/dist/formatting/AbstractNumbering.js.map +1 -1
- package/dist/formatting/NumberingInstance.d.ts.map +1 -1
- package/dist/formatting/NumberingInstance.js +1 -3
- package/dist/formatting/NumberingInstance.js.map +1 -1
- package/dist/formatting/NumberingLevel.d.ts +5 -5
- package/dist/formatting/NumberingLevel.d.ts.map +1 -1
- package/dist/formatting/NumberingLevel.js +119 -125
- package/dist/formatting/NumberingLevel.js.map +1 -1
- package/dist/formatting/NumberingManager.d.ts +1 -0
- package/dist/formatting/NumberingManager.d.ts.map +1 -1
- package/dist/formatting/NumberingManager.js +27 -9
- package/dist/formatting/NumberingManager.js.map +1 -1
- package/dist/formatting/Style.d.ts +11 -11
- package/dist/formatting/Style.d.ts.map +1 -1
- package/dist/formatting/Style.js +219 -247
- package/dist/formatting/Style.js.map +1 -1
- package/dist/formatting/StylesManager.d.ts +2 -2
- package/dist/formatting/StylesManager.d.ts.map +1 -1
- package/dist/formatting/StylesManager.js +96 -102
- package/dist/formatting/StylesManager.js.map +1 -1
- package/dist/helpers/CleanupHelper.d.ts +1 -1
- package/dist/helpers/CleanupHelper.d.ts.map +1 -1
- package/dist/helpers/CleanupHelper.js +6 -6
- package/dist/helpers/CleanupHelper.js.map +1 -1
- package/dist/images/ImageOptimizer.js +7 -7
- package/dist/images/ImageOptimizer.js.map +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/managers/DrawingManager.js.map +1 -1
- package/dist/tracking/DocumentTrackingContext.d.ts.map +1 -1
- package/dist/tracking/DocumentTrackingContext.js +23 -7
- package/dist/tracking/DocumentTrackingContext.js.map +1 -1
- package/dist/tracking/TrackingContext.d.ts.map +1 -1
- package/dist/tracking/TrackingContext.js.map +1 -1
- package/dist/types/compatibility-types.js.map +1 -1
- package/dist/types/formatting.js.map +1 -1
- package/dist/types/list-types.d.ts +6 -6
- package/dist/types/list-types.js.map +1 -1
- package/dist/types/settings-types.js.map +1 -1
- package/dist/types/styleConfig.d.ts +2 -2
- package/dist/types/styleConfig.js.map +1 -1
- package/dist/utils/ChangelogGenerator.d.ts.map +1 -1
- package/dist/utils/ChangelogGenerator.js +97 -101
- package/dist/utils/ChangelogGenerator.js.map +1 -1
- package/dist/utils/CompatibilityUpgrader.d.ts.map +1 -1
- package/dist/utils/CompatibilityUpgrader.js +1 -1
- package/dist/utils/CompatibilityUpgrader.js.map +1 -1
- package/dist/utils/InMemoryRevisionAcceptor.d.ts.map +1 -1
- package/dist/utils/InMemoryRevisionAcceptor.js +1 -6
- package/dist/utils/InMemoryRevisionAcceptor.js.map +1 -1
- package/dist/utils/MoveOperationHelper.d.ts.map +1 -1
- package/dist/utils/MoveOperationHelper.js +1 -1
- package/dist/utils/MoveOperationHelper.js.map +1 -1
- package/dist/utils/RevisionAwareProcessor.d.ts.map +1 -1
- package/dist/utils/RevisionAwareProcessor.js +2 -4
- package/dist/utils/RevisionAwareProcessor.js.map +1 -1
- package/dist/utils/RevisionWalker.d.ts.map +1 -1
- package/dist/utils/RevisionWalker.js +4 -12
- package/dist/utils/RevisionWalker.js.map +1 -1
- package/dist/utils/SelectiveRevisionAcceptor.d.ts.map +1 -1
- package/dist/utils/SelectiveRevisionAcceptor.js +2 -6
- package/dist/utils/SelectiveRevisionAcceptor.js.map +1 -1
- package/dist/utils/ShadingResolver.d.ts.map +1 -1
- package/dist/utils/ShadingResolver.js +1 -1
- package/dist/utils/ShadingResolver.js.map +1 -1
- package/dist/utils/acceptRevisions.d.ts.map +1 -1
- package/dist/utils/acceptRevisions.js +23 -12
- package/dist/utils/acceptRevisions.js.map +1 -1
- package/dist/utils/cnfStyleDecoder.d.ts +1 -1
- package/dist/utils/cnfStyleDecoder.d.ts.map +1 -1
- package/dist/utils/cnfStyleDecoder.js +40 -40
- package/dist/utils/cnfStyleDecoder.js.map +1 -1
- package/dist/utils/corruptionDetection.d.ts.map +1 -1
- package/dist/utils/corruptionDetection.js.map +1 -1
- package/dist/utils/dateFormatting.js.map +1 -1
- package/dist/utils/deepClone.js +1 -1
- package/dist/utils/deepClone.js.map +1 -1
- package/dist/utils/diagnostics.d.ts.map +1 -1
- package/dist/utils/diagnostics.js +1 -1
- package/dist/utils/diagnostics.js.map +1 -1
- package/dist/utils/errorHandling.js.map +1 -1
- package/dist/utils/formatting.d.ts.map +1 -1
- package/dist/utils/formatting.js +10 -2
- package/dist/utils/formatting.js.map +1 -1
- package/dist/utils/list-detection.d.ts +2 -2
- package/dist/utils/list-detection.d.ts.map +1 -1
- package/dist/utils/list-detection.js +21 -23
- package/dist/utils/list-detection.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +12 -7
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/parsingHelpers.js.map +1 -1
- package/dist/utils/stripTrackedChanges.d.ts.map +1 -1
- package/dist/utils/stripTrackedChanges.js +3 -3
- package/dist/utils/stripTrackedChanges.js.map +1 -1
- package/dist/utils/textDiff.d.ts +1 -1
- package/dist/utils/textDiff.js +8 -8
- package/dist/utils/textDiff.js.map +1 -1
- package/dist/utils/units.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +24 -7
- package/dist/utils/validation.js.map +1 -1
- package/dist/utils/xmlSanitization.d.ts.map +1 -1
- package/dist/utils/xmlSanitization.js +3 -3
- package/dist/utils/xmlSanitization.js.map +1 -1
- package/dist/validation/RevisionAutoFixer.d.ts.map +1 -1
- package/dist/validation/RevisionAutoFixer.js +5 -5
- package/dist/validation/RevisionAutoFixer.js.map +1 -1
- package/dist/validation/RevisionValidator.d.ts.map +1 -1
- package/dist/validation/RevisionValidator.js +7 -9
- package/dist/validation/RevisionValidator.js.map +1 -1
- package/dist/validation/ValidationRules.js +3 -3
- package/dist/validation/ValidationRules.js.map +1 -1
- package/dist/validation/index.js.map +1 -1
- package/dist/xml/XMLBuilder.d.ts +1 -1
- package/dist/xml/XMLBuilder.d.ts.map +1 -1
- package/dist/xml/XMLBuilder.js +98 -100
- package/dist/xml/XMLBuilder.js.map +1 -1
- package/dist/xml/XMLParser.d.ts.map +1 -1
- package/dist/xml/XMLParser.js +61 -66
- package/dist/xml/XMLParser.js.map +1 -1
- package/dist/zip/ZipHandler.d.ts.map +1 -1
- package/dist/zip/ZipHandler.js.map +1 -1
- package/dist/zip/ZipReader.d.ts.map +1 -1
- package/dist/zip/ZipReader.js +1 -3
- package/dist/zip/ZipReader.js.map +1 -1
- package/dist/zip/ZipWriter.d.ts +1 -1
- package/dist/zip/ZipWriter.d.ts.map +1 -1
- package/dist/zip/ZipWriter.js +28 -36
- package/dist/zip/ZipWriter.js.map +1 -1
- package/dist/zip/types.js +1 -1
- package/dist/zip/types.js.map +1 -1
- package/package.json +92 -92
- package/src/__tests__/helper-methods.test.ts +512 -512
- package/src/constants/legacyCompatFlags.ts +138 -138
- package/src/constants/limits.ts +50 -50
- package/src/core/Document.ts +1010 -1145
- package/src/core/DocumentContent.ts +461 -467
- package/src/core/DocumentGenerator.ts +1133 -1104
- package/src/core/DocumentIdManager.ts +158 -158
- package/src/core/DocumentParser.ts +2347 -2716
- package/src/core/DocumentValidator.ts +363 -372
- package/src/core/Relationship.ts +367 -367
- package/src/core/RelationshipManager.ts +429 -428
- package/src/elements/AlternateContent.ts +42 -42
- package/src/elements/Bookmark.ts +212 -210
- package/src/elements/BookmarkManager.ts +247 -250
- package/src/elements/Comment.ts +356 -359
- package/src/elements/CommentManager.ts +499 -502
- package/src/elements/CommonTypes.ts +524 -549
- package/src/elements/CustomXml.ts +36 -36
- package/src/elements/Endnote.ts +221 -217
- package/src/elements/EndnoteManager.ts +246 -249
- package/src/elements/Field.ts +1292 -1233
- package/src/elements/FieldHelpers.ts +329 -333
- package/src/elements/FontManager.ts +336 -339
- package/src/elements/Footer.ts +269 -269
- package/src/elements/Footnote.ts +221 -217
- package/src/elements/FootnoteManager.ts +246 -249
- package/src/elements/Header.ts +269 -269
- package/src/elements/HeaderFooterManager.ts +219 -219
- package/src/elements/Hyperlink.ts +1288 -1193
- package/src/elements/Image.ts +1982 -1756
- package/src/elements/ImageManager.ts +437 -432
- package/src/elements/ImageRun.ts +59 -59
- package/src/elements/MathElement.ts +65 -65
- package/src/elements/Paragraph.ts +4347 -4287
- package/src/elements/PreservedElement.ts +53 -53
- package/src/elements/PropertyChangeTypes.ts +458 -442
- package/src/elements/RangeMarker.ts +382 -400
- package/src/elements/Revision.ts +1198 -1217
- package/src/elements/RevisionContent.ts +73 -73
- package/src/elements/RevisionManager.ts +1070 -1070
- package/src/elements/Run.ts +3103 -3073
- package/src/elements/Section.ts +1521 -1421
- package/src/elements/Shape.ts +884 -873
- package/src/elements/StructuredDocumentTag.ts +1176 -1207
- package/src/elements/Table.ts +2468 -2524
- package/src/elements/TableCell.ts +1617 -1621
- package/src/elements/TableGridChange.ts +149 -151
- package/src/elements/TableOfContents.ts +701 -691
- package/src/elements/TableOfContentsElement.ts +89 -89
- package/src/elements/TableRow.ts +960 -929
- package/src/elements/TextBox.ts +766 -768
- package/src/formatting/AbstractNumbering.ts +580 -579
- package/src/formatting/NumberingInstance.ts +295 -299
- package/src/formatting/NumberingLevel.ts +981 -1040
- package/src/formatting/NumberingManager.ts +875 -827
- package/src/formatting/Style.ts +1785 -1879
- package/src/formatting/StylesManager.ts +1090 -1130
- package/src/helpers/CleanupHelper.ts +524 -524
- package/src/images/ImageOptimizer.ts +274 -274
- package/src/index.ts +559 -554
- package/src/managers/DrawingManager.ts +319 -319
- package/src/tracking/DocumentTrackingContext.ts +687 -674
- package/src/tracking/TrackingContext.ts +175 -173
- package/src/types/compatibility-types.ts +49 -49
- package/src/types/formatting.ts +210 -210
- package/src/types/list-types.ts +14 -14
- package/src/types/settings-types.ts +59 -59
- package/src/types/styleConfig.ts +189 -189
- package/src/utils/ChangelogGenerator.ts +1583 -1581
- package/src/utils/CompatibilityUpgrader.ts +235 -237
- package/src/utils/InMemoryRevisionAcceptor.ts +691 -696
- package/src/utils/MoveOperationHelper.ts +233 -238
- package/src/utils/RevisionAwareProcessor.ts +518 -526
- package/src/utils/RevisionWalker.ts +427 -457
- package/src/utils/SelectiveRevisionAcceptor.ts +662 -683
- package/src/utils/ShadingResolver.ts +105 -107
- package/src/utils/acceptRevisions.ts +723 -714
- package/src/utils/cnfStyleDecoder.ts +212 -217
- package/src/utils/corruptionDetection.ts +346 -345
- package/src/utils/dateFormatting.ts +20 -20
- package/src/utils/deepClone.ts +77 -78
- package/src/utils/diagnostics.ts +125 -129
- package/src/utils/errorHandling.ts +80 -80
- package/src/utils/formatting.ts +220 -213
- package/src/utils/list-detection.ts +32 -42
- package/src/utils/logger.ts +412 -404
- package/src/utils/parsingHelpers.ts +190 -190
- package/src/utils/stripTrackedChanges.ts +356 -353
- package/src/utils/textDiff.ts +100 -100
- package/src/utils/units.ts +421 -421
- package/src/utils/validation.ts +553 -542
- package/src/utils/xmlSanitization.ts +179 -182
- package/src/validation/RevisionAutoFixer.ts +541 -542
- package/src/validation/RevisionValidator.ts +470 -460
- package/src/validation/ValidationRules.ts +338 -338
- package/src/validation/index.ts +30 -30
- package/src/xml/XMLBuilder.ts +857 -871
- package/src/xml/XMLParser.ts +877 -919
- package/src/zip/ZipHandler.ts +629 -637
- package/src/zip/ZipReader.ts +295 -299
- package/src/zip/ZipWriter.ts +374 -390
- package/src/zip/types.ts +116 -116
package/src/core/Document.ts
CHANGED
|
@@ -3,34 +3,34 @@
|
|
|
3
3
|
* Provides a simple interface for creating DOCX files without managing ZIP and XML manually
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { AlternateContent } from
|
|
7
|
-
import { Bookmark } from
|
|
8
|
-
import { BookmarkManager } from
|
|
9
|
-
import { Comment } from
|
|
10
|
-
import { CustomXmlBlock } from
|
|
11
|
-
import { PreservedElement } from
|
|
12
|
-
import { MathParagraph } from
|
|
13
|
-
import { CommentManager } from
|
|
14
|
-
import { Endnote } from
|
|
15
|
-
import { EndnoteManager } from
|
|
16
|
-
import { Field } from
|
|
17
|
-
import { Footnote } from
|
|
18
|
-
import { FootnoteManager } from
|
|
19
|
-
import { Footer } from
|
|
20
|
-
import { Header } from
|
|
21
|
-
import { HeaderFooterManager } from
|
|
22
|
-
import { Hyperlink } from
|
|
23
|
-
import { Image } from
|
|
24
|
-
import { ImageManager } from
|
|
25
|
-
import { ImageRun } from
|
|
26
|
-
import { Paragraph, ParagraphContent, FieldLike } from
|
|
27
|
-
import { RangeMarker } from
|
|
28
|
-
import { Revision, RevisionType } from
|
|
29
|
-
import { RevisionManager } from
|
|
30
|
-
import { RevisionLocation } from
|
|
31
|
-
import { Run, RunFormatting } from
|
|
32
|
-
import { Shape } from
|
|
33
|
-
import { TextBox } from
|
|
6
|
+
import { AlternateContent } from '../elements/AlternateContent';
|
|
7
|
+
import { Bookmark } from '../elements/Bookmark';
|
|
8
|
+
import { BookmarkManager } from '../elements/BookmarkManager';
|
|
9
|
+
import { Comment } from '../elements/Comment';
|
|
10
|
+
import { CustomXmlBlock } from '../elements/CustomXml';
|
|
11
|
+
import { PreservedElement } from '../elements/PreservedElement';
|
|
12
|
+
import { MathParagraph } from '../elements/MathElement';
|
|
13
|
+
import { CommentManager } from '../elements/CommentManager';
|
|
14
|
+
import { Endnote } from '../elements/Endnote';
|
|
15
|
+
import { EndnoteManager } from '../elements/EndnoteManager';
|
|
16
|
+
import { Field } from '../elements/Field';
|
|
17
|
+
import { Footnote } from '../elements/Footnote';
|
|
18
|
+
import { FootnoteManager } from '../elements/FootnoteManager';
|
|
19
|
+
import { Footer } from '../elements/Footer';
|
|
20
|
+
import { Header } from '../elements/Header';
|
|
21
|
+
import { HeaderFooterManager } from '../elements/HeaderFooterManager';
|
|
22
|
+
import { Hyperlink } from '../elements/Hyperlink';
|
|
23
|
+
import { Image } from '../elements/Image';
|
|
24
|
+
import { ImageManager } from '../elements/ImageManager';
|
|
25
|
+
import { ImageRun } from '../elements/ImageRun';
|
|
26
|
+
import { Paragraph, ParagraphContent, FieldLike } from '../elements/Paragraph';
|
|
27
|
+
import { RangeMarker } from '../elements/RangeMarker';
|
|
28
|
+
import { Revision, RevisionType } from '../elements/Revision';
|
|
29
|
+
import { RevisionManager } from '../elements/RevisionManager';
|
|
30
|
+
import { RevisionLocation } from '../elements/PropertyChangeTypes';
|
|
31
|
+
import { Run, RunFormatting } from '../elements/Run';
|
|
32
|
+
import { Shape } from '../elements/Shape';
|
|
33
|
+
import { TextBox } from '../elements/TextBox';
|
|
34
34
|
import {
|
|
35
35
|
RevisionValidator,
|
|
36
36
|
RevisionAutoFixer,
|
|
@@ -38,21 +38,25 @@ import {
|
|
|
38
38
|
AutoFixOptions,
|
|
39
39
|
ValidationResult,
|
|
40
40
|
AutoFixResult,
|
|
41
|
-
} from
|
|
42
|
-
import { Section } from
|
|
43
|
-
import { StructuredDocumentTag } from
|
|
44
|
-
import { Table, TableBorder } from
|
|
45
|
-
import { TableCell } from
|
|
46
|
-
import { TableOfContentsElement } from
|
|
47
|
-
import { resolveCellShading } from
|
|
48
|
-
import {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
import {
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
41
|
+
} from '../validation';
|
|
42
|
+
import { Section } from '../elements/Section';
|
|
43
|
+
import { StructuredDocumentTag } from '../elements/StructuredDocumentTag';
|
|
44
|
+
import { Table, TableBorder } from '../elements/Table';
|
|
45
|
+
import { TableCell } from '../elements/TableCell';
|
|
46
|
+
import { TableOfContentsElement } from '../elements/TableOfContentsElement';
|
|
47
|
+
import { resolveCellShading } from '../utils/ShadingResolver';
|
|
48
|
+
import {
|
|
49
|
+
NumberingManager,
|
|
50
|
+
NumberingConsolidationOptions,
|
|
51
|
+
NumberingConsolidationResult,
|
|
52
|
+
} from '../formatting/NumberingManager';
|
|
53
|
+
import { Style, StyleProperties } from '../formatting/Style';
|
|
54
|
+
import { StylesManager } from '../formatting/StylesManager';
|
|
55
|
+
import { FormatOptions, StyleApplyOptions } from '../types/formatting';
|
|
56
|
+
import { CompatibilityMode, CompatibilityInfo, CompatSetting } from '../types/compatibility-types';
|
|
57
|
+
import { DocumentProtection, RevisionViewSettings, WebSettingsInfo } from '../types/settings-types';
|
|
58
|
+
import { CompatibilityUpgrader, UpgradeReport } from '../utils/CompatibilityUpgrader';
|
|
59
|
+
import { MODERN_COMPAT_SETTINGS } from '../constants/legacyCompatFlags';
|
|
56
60
|
// ListNormalizationOptions and ListNormalizationReport removed - normalizeTableLists moved to consumer
|
|
57
61
|
import {
|
|
58
62
|
ApplyStylesOptions,
|
|
@@ -60,10 +64,10 @@ import {
|
|
|
60
64
|
StyleConfig,
|
|
61
65
|
StyleRunFormatting,
|
|
62
66
|
StyleParagraphFormatting,
|
|
63
|
-
} from
|
|
67
|
+
} from '../types/styleConfig';
|
|
64
68
|
// ListNormalizer import removed - moved to consumer
|
|
65
|
-
import { defaultLogger, ILogger, getGlobalLogger, createScopedLogger } from
|
|
66
|
-
import { UNITS } from
|
|
69
|
+
import { defaultLogger, ILogger, getGlobalLogger, createScopedLogger } from '../utils/logger';
|
|
70
|
+
import { UNITS } from '../utils/units';
|
|
67
71
|
|
|
68
72
|
// Create scoped logger for Document operations
|
|
69
73
|
function getLogger(): ILogger {
|
|
@@ -71,25 +75,25 @@ function getLogger(): ILogger {
|
|
|
71
75
|
}
|
|
72
76
|
// Raw XML revision acceptance - used at load time BEFORE parsing
|
|
73
77
|
// cleanupRevisionMetadata - cleanup metadata files after in-memory acceptance
|
|
74
|
-
import { acceptAllRevisions, cleanupRevisionMetadata } from
|
|
78
|
+
import { acceptAllRevisions, cleanupRevisionMetadata } from '../utils/acceptRevisions';
|
|
75
79
|
// In-memory revision acceptance - used AFTER parsing, allows subsequent modifications
|
|
76
|
-
import { acceptRevisionsInMemory, AcceptRevisionsResult } from
|
|
77
|
-
import { stripTrackedChanges } from
|
|
78
|
-
import { diffText, diffHasUnchangedParts } from
|
|
79
|
-
import { XMLBuilder } from
|
|
80
|
-
import { XMLParser } from
|
|
81
|
-
import { DocumentTrackingContext } from
|
|
82
|
-
import type { TrackingContext } from
|
|
83
|
-
import { ZipHandler } from
|
|
84
|
-
import { DOCX_PATHS } from
|
|
85
|
-
import { DocumentGenerator } from
|
|
86
|
-
import { DocumentIdManager } from
|
|
87
|
-
import { DocumentParser } from
|
|
88
|
-
import { DocumentValidator } from
|
|
89
|
-
import { RelationshipManager } from
|
|
90
|
-
import { RelationshipType } from
|
|
91
|
-
import { BodyElement } from
|
|
92
|
-
import { optimizeImage, ImageOptimizationResult } from
|
|
80
|
+
import { acceptRevisionsInMemory, AcceptRevisionsResult } from '../utils/InMemoryRevisionAcceptor';
|
|
81
|
+
import { stripTrackedChanges } from '../utils/stripTrackedChanges';
|
|
82
|
+
import { diffText, diffHasUnchangedParts } from '../utils/textDiff';
|
|
83
|
+
import { XMLBuilder } from '../xml/XMLBuilder';
|
|
84
|
+
import { XMLParser } from '../xml/XMLParser';
|
|
85
|
+
import { DocumentTrackingContext } from '../tracking/DocumentTrackingContext';
|
|
86
|
+
import type { TrackingContext } from '../tracking/TrackingContext';
|
|
87
|
+
import { ZipHandler } from '../zip/ZipHandler';
|
|
88
|
+
import { DOCX_PATHS } from '../zip/types';
|
|
89
|
+
import { DocumentGenerator } from './DocumentGenerator';
|
|
90
|
+
import { DocumentIdManager } from './DocumentIdManager';
|
|
91
|
+
import { DocumentParser } from './DocumentParser';
|
|
92
|
+
import { DocumentValidator } from './DocumentValidator';
|
|
93
|
+
import { RelationshipManager } from './RelationshipManager';
|
|
94
|
+
import { RelationshipType } from './Relationship';
|
|
95
|
+
import { BodyElement } from './DocumentContent';
|
|
96
|
+
import { optimizeImage, ImageOptimizationResult } from '../images/ImageOptimizer';
|
|
93
97
|
|
|
94
98
|
/**
|
|
95
99
|
* Document properties (core and extended)
|
|
@@ -249,7 +253,9 @@ export class Document {
|
|
|
249
253
|
this.numberingManager = NumberingManager.create();
|
|
250
254
|
this.section = new Section();
|
|
251
255
|
this.imageManager = ImageManager.create();
|
|
252
|
-
this.relationshipManager = initDefaults
|
|
256
|
+
this.relationshipManager = initDefaults
|
|
257
|
+
? RelationshipManager.createForDocument()
|
|
258
|
+
: RelationshipManager.create();
|
|
253
259
|
this.headerFooterManager = HeaderFooterManager.create();
|
|
254
260
|
this.bookmarkManager = BookmarkManager.create();
|
|
255
261
|
this.revisionManager = RevisionManager.create();
|
|
@@ -326,7 +332,7 @@ export class Document {
|
|
|
326
332
|
|
|
327
333
|
// TOC auto-population setting
|
|
328
334
|
private autoPopulateTOCs = false;
|
|
329
|
-
|
|
335
|
+
|
|
330
336
|
// TOC field instruction sync setting (default: OFF to preserve original instructions)
|
|
331
337
|
private autoSyncTOCStyles = false;
|
|
332
338
|
|
|
@@ -404,7 +410,12 @@ export class Document {
|
|
|
404
410
|
private documentProtection?: DocumentProtection;
|
|
405
411
|
|
|
406
412
|
/** Document background (w:background) per ECMA-376 Part 1 §17.2.1 */
|
|
407
|
-
private _documentBackground?: {
|
|
413
|
+
private _documentBackground?: {
|
|
414
|
+
color?: string;
|
|
415
|
+
themeColor?: string;
|
|
416
|
+
themeTint?: string;
|
|
417
|
+
themeShade?: string;
|
|
418
|
+
};
|
|
408
419
|
|
|
409
420
|
/** Even and odd headers setting (w:evenAndOddHeaders) per ECMA-376 Part 1 §17.15.1.28 */
|
|
410
421
|
private _evenAndOddHeaders?: boolean;
|
|
@@ -579,10 +590,7 @@ export class Document {
|
|
|
579
590
|
* }
|
|
580
591
|
* ```
|
|
581
592
|
*/
|
|
582
|
-
static async load(
|
|
583
|
-
filePath: string,
|
|
584
|
-
options?: DocumentLoadOptions
|
|
585
|
-
): Promise<Document> {
|
|
593
|
+
static async load(filePath: string, options?: DocumentLoadOptions): Promise<Document> {
|
|
586
594
|
const logger = getLogger();
|
|
587
595
|
logger.info('Loading document from file', { path: filePath });
|
|
588
596
|
|
|
@@ -620,10 +628,7 @@ export class Document {
|
|
|
620
628
|
* const doc = await Document.loadFromBuffer(buffer);
|
|
621
629
|
* ```
|
|
622
630
|
*/
|
|
623
|
-
static async loadFromBuffer(
|
|
624
|
-
buffer: Buffer,
|
|
625
|
-
options?: DocumentLoadOptions
|
|
626
|
-
): Promise<Document> {
|
|
631
|
+
static async loadFromBuffer(buffer: Buffer, options?: DocumentLoadOptions): Promise<Document> {
|
|
627
632
|
const logger = getLogger();
|
|
628
633
|
logger.info('Loading document from buffer', { bufferSize: buffer.length });
|
|
629
634
|
|
|
@@ -648,7 +653,7 @@ export class Document {
|
|
|
648
653
|
// so they can be accepted using in-memory transformation after parsing
|
|
649
654
|
const useInMemoryAccept = options?.acceptRevisions === true;
|
|
650
655
|
const revisionHandling = useInMemoryAccept
|
|
651
|
-
? 'preserve'
|
|
656
|
+
? 'preserve' // Force preserve so revisions are parsed into model
|
|
652
657
|
: (options?.revisionHandling ?? 'accept'); // Default to accept
|
|
653
658
|
|
|
654
659
|
// Handle tracked changes BEFORE parsing (unless using in-memory accept)
|
|
@@ -662,8 +667,13 @@ export class Document {
|
|
|
662
667
|
// Check if document has tracked changes and warn (unless intentionally accepting later)
|
|
663
668
|
if (!useInMemoryAccept) {
|
|
664
669
|
const documentXml = zipHandler.getFileAsString('word/document.xml');
|
|
665
|
-
if (
|
|
666
|
-
|
|
670
|
+
if (
|
|
671
|
+
documentXml &&
|
|
672
|
+
(documentXml.includes('<w:ins') ||
|
|
673
|
+
documentXml.includes('<w:del') ||
|
|
674
|
+
documentXml.includes('<w:moveFrom') ||
|
|
675
|
+
documentXml.includes('<w:moveTo'))
|
|
676
|
+
) {
|
|
667
677
|
logger.warn('Document contains tracked changes in preserve mode');
|
|
668
678
|
}
|
|
669
679
|
}
|
|
@@ -717,7 +727,7 @@ export class Document {
|
|
|
717
727
|
if (parseWarnings.length > 0) {
|
|
718
728
|
logger.warn('Document loaded with parse warnings', {
|
|
719
729
|
warningCount: parseWarnings.length,
|
|
720
|
-
elements: parseWarnings.map(w => w.element).join(', '),
|
|
730
|
+
elements: parseWarnings.map((w) => w.element).join(', '),
|
|
721
731
|
});
|
|
722
732
|
}
|
|
723
733
|
|
|
@@ -815,11 +825,16 @@ export class Document {
|
|
|
815
825
|
const hashMatch = /w:hash\s*=\s*"([^"]*)"/.exec(attrs);
|
|
816
826
|
const saltMatch = /w:salt\s*=\s*"([^"]*)"/.exec(attrs);
|
|
817
827
|
|
|
818
|
-
if (cryptProviderMatch?.[1])
|
|
819
|
-
|
|
820
|
-
if (
|
|
821
|
-
|
|
822
|
-
if (
|
|
828
|
+
if (cryptProviderMatch?.[1])
|
|
829
|
+
this.documentProtection.cryptProviderType = cryptProviderMatch[1];
|
|
830
|
+
if (cryptAlgClassMatch?.[1])
|
|
831
|
+
this.documentProtection.cryptAlgorithmClass = cryptAlgClassMatch[1];
|
|
832
|
+
if (cryptAlgTypeMatch?.[1])
|
|
833
|
+
this.documentProtection.cryptAlgorithmType = cryptAlgTypeMatch[1];
|
|
834
|
+
if (cryptAlgSidMatch?.[1])
|
|
835
|
+
this.documentProtection.cryptAlgorithmSid = parseInt(cryptAlgSidMatch[1], 10);
|
|
836
|
+
if (cryptSpinMatch?.[1])
|
|
837
|
+
this.documentProtection.cryptSpinCount = parseInt(cryptSpinMatch[1], 10);
|
|
823
838
|
if (hashMatch?.[1]) this.documentProtection.hash = hashMatch[1];
|
|
824
839
|
if (saltMatch?.[1]) this.documentProtection.salt = saltMatch[1];
|
|
825
840
|
}
|
|
@@ -960,8 +975,10 @@ export class Document {
|
|
|
960
975
|
compatSettings.push(setting);
|
|
961
976
|
|
|
962
977
|
// Extract compatibility mode value
|
|
963
|
-
if (
|
|
964
|
-
|
|
978
|
+
if (
|
|
979
|
+
setting.name === 'compatibilityMode' &&
|
|
980
|
+
setting.uri === 'http://schemas.microsoft.com/office/word'
|
|
981
|
+
) {
|
|
965
982
|
const modeVal = parseInt(setting.val, 10);
|
|
966
983
|
if (!isNaN(modeVal)) {
|
|
967
984
|
mode = modeVal as CompatibilityMode;
|
|
@@ -1064,15 +1081,12 @@ export class Document {
|
|
|
1064
1081
|
}
|
|
1065
1082
|
} catch (headerFooterError) {
|
|
1066
1083
|
const logger = getLogger();
|
|
1067
|
-
logger.warn(
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
headerFooterError
|
|
1072
|
-
|
|
1073
|
-
: String(headerFooterError),
|
|
1074
|
-
}
|
|
1075
|
-
);
|
|
1084
|
+
logger.warn('Failed to parse headers/footers - document will load without them', {
|
|
1085
|
+
error:
|
|
1086
|
+
headerFooterError instanceof Error
|
|
1087
|
+
? headerFooterError.message
|
|
1088
|
+
: String(headerFooterError),
|
|
1089
|
+
});
|
|
1076
1090
|
// Continue loading - headers/footers are not critical for document structure
|
|
1077
1091
|
// User should be aware that headers/footers may be missing
|
|
1078
1092
|
}
|
|
@@ -1125,7 +1139,10 @@ export class Document {
|
|
|
1125
1139
|
// Initialize the centralized DocumentIdManager from document XML
|
|
1126
1140
|
// This scans ALL w:id attributes and sets nextId to globalMax + 1
|
|
1127
1141
|
// All managers (Bookmark, Revision, Comment) use this shared counter via callbacks
|
|
1128
|
-
this.documentIdManager.initializeFromDocument(
|
|
1142
|
+
this.documentIdManager.initializeFromDocument(
|
|
1143
|
+
documentXml || undefined,
|
|
1144
|
+
commentsXml || undefined
|
|
1145
|
+
);
|
|
1129
1146
|
}
|
|
1130
1147
|
|
|
1131
1148
|
/**
|
|
@@ -1143,7 +1160,11 @@ export class Document {
|
|
|
1143
1160
|
this._originalCommentsXml = commentsXml;
|
|
1144
1161
|
|
|
1145
1162
|
// Also preserve companion files from the ZIP
|
|
1146
|
-
for (const path of [
|
|
1163
|
+
for (const path of [
|
|
1164
|
+
DOCX_PATHS.COMMENTS_EXTENDED,
|
|
1165
|
+
DOCX_PATHS.COMMENTS_IDS,
|
|
1166
|
+
DOCX_PATHS.COMMENTS_EXTENSIBLE,
|
|
1167
|
+
]) {
|
|
1147
1168
|
const content = this.zipHandler.getFileAsString(path);
|
|
1148
1169
|
if (content) {
|
|
1149
1170
|
this._originalCommentCompanionFiles.set(path, content);
|
|
@@ -1267,10 +1288,7 @@ export class Document {
|
|
|
1267
1288
|
*/
|
|
1268
1289
|
private initializeRequiredFiles(): void {
|
|
1269
1290
|
// [Content_Types].xml
|
|
1270
|
-
this.zipHandler.addFile(
|
|
1271
|
-
DOCX_PATHS.CONTENT_TYPES,
|
|
1272
|
-
this.generator.generateContentTypes()
|
|
1273
|
-
);
|
|
1291
|
+
this.zipHandler.addFile(DOCX_PATHS.CONTENT_TYPES, this.generator.generateContentTypes());
|
|
1274
1292
|
|
|
1275
1293
|
// _rels/.rels
|
|
1276
1294
|
this.zipHandler.addFile(DOCX_PATHS.RELS, this.generator.generateRels());
|
|
@@ -1287,34 +1305,19 @@ export class Document {
|
|
|
1287
1305
|
);
|
|
1288
1306
|
|
|
1289
1307
|
// word/_rels/document.xml.rels
|
|
1290
|
-
this.zipHandler.addFile(
|
|
1291
|
-
"word/_rels/document.xml.rels",
|
|
1292
|
-
this.relationshipManager.generateXml()
|
|
1293
|
-
);
|
|
1308
|
+
this.zipHandler.addFile('word/_rels/document.xml.rels', this.relationshipManager.generateXml());
|
|
1294
1309
|
|
|
1295
1310
|
// word/styles.xml
|
|
1296
|
-
this.zipHandler.addFile(
|
|
1297
|
-
DOCX_PATHS.STYLES,
|
|
1298
|
-
this.stylesManager.generateStylesXml()
|
|
1299
|
-
);
|
|
1311
|
+
this.zipHandler.addFile(DOCX_PATHS.STYLES, this.stylesManager.generateStylesXml());
|
|
1300
1312
|
|
|
1301
1313
|
// word/numbering.xml
|
|
1302
|
-
this.zipHandler.addFile(
|
|
1303
|
-
DOCX_PATHS.NUMBERING,
|
|
1304
|
-
this.numberingManager.generateNumberingXml()
|
|
1305
|
-
);
|
|
1314
|
+
this.zipHandler.addFile(DOCX_PATHS.NUMBERING, this.numberingManager.generateNumberingXml());
|
|
1306
1315
|
|
|
1307
1316
|
// word/fontTable.xml (REQUIRED for DOCX compliance)
|
|
1308
|
-
this.zipHandler.addFile(
|
|
1309
|
-
"word/fontTable.xml",
|
|
1310
|
-
this.generator.generateFontTable()
|
|
1311
|
-
);
|
|
1317
|
+
this.zipHandler.addFile('word/fontTable.xml', this.generator.generateFontTable());
|
|
1312
1318
|
|
|
1313
1319
|
// word/webSettings.xml
|
|
1314
|
-
this.zipHandler.addFile(
|
|
1315
|
-
DOCX_PATHS.WEB_SETTINGS,
|
|
1316
|
-
this.generator.generateWebSettings()
|
|
1317
|
-
);
|
|
1320
|
+
this.zipHandler.addFile(DOCX_PATHS.WEB_SETTINGS, this.generator.generateWebSettings());
|
|
1318
1321
|
|
|
1319
1322
|
// word/settings.xml (REQUIRED for DOCX compliance)
|
|
1320
1323
|
this.zipHandler.addFile(
|
|
@@ -1335,19 +1338,32 @@ export class Document {
|
|
|
1335
1338
|
isLegacyMode: false,
|
|
1336
1339
|
compatSettings: [
|
|
1337
1340
|
{ name: 'compatibilityMode', uri: 'http://schemas.microsoft.com/office/word', val: '15' },
|
|
1338
|
-
{
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1341
|
+
{
|
|
1342
|
+
name: 'overrideTableStyleFontSizeAndJustification',
|
|
1343
|
+
uri: 'http://schemas.microsoft.com/office/word',
|
|
1344
|
+
val: '1',
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
name: 'enableOpenTypeFeatures',
|
|
1348
|
+
uri: 'http://schemas.microsoft.com/office/word',
|
|
1349
|
+
val: '1',
|
|
1350
|
+
},
|
|
1351
|
+
{
|
|
1352
|
+
name: 'doNotFlipMirrorIndents',
|
|
1353
|
+
uri: 'http://schemas.microsoft.com/office/word',
|
|
1354
|
+
val: '1',
|
|
1355
|
+
},
|
|
1356
|
+
{
|
|
1357
|
+
name: 'differentiateMultirowTableHeaders',
|
|
1358
|
+
uri: 'http://schemas.microsoft.com/office/word',
|
|
1359
|
+
val: '1',
|
|
1360
|
+
},
|
|
1342
1361
|
],
|
|
1343
1362
|
legacyFlags: [],
|
|
1344
1363
|
};
|
|
1345
1364
|
|
|
1346
1365
|
// word/theme/theme1.xml (REQUIRED for DOCX compliance)
|
|
1347
|
-
this.zipHandler.addFile(
|
|
1348
|
-
"word/theme/theme1.xml",
|
|
1349
|
-
this.generator.generateTheme()
|
|
1350
|
-
);
|
|
1366
|
+
this.zipHandler.addFile('word/theme/theme1.xml', this.generator.generateTheme());
|
|
1351
1367
|
|
|
1352
1368
|
// docProps/core.xml
|
|
1353
1369
|
this.zipHandler.addFile(
|
|
@@ -1356,15 +1372,11 @@ export class Document {
|
|
|
1356
1372
|
);
|
|
1357
1373
|
|
|
1358
1374
|
// docProps/app.xml
|
|
1359
|
-
this.zipHandler.addFile(
|
|
1360
|
-
DOCX_PATHS.APP_PROPS,
|
|
1361
|
-
this.generator.generateAppProps(this.properties)
|
|
1362
|
-
);
|
|
1375
|
+
this.zipHandler.addFile(DOCX_PATHS.APP_PROPS, this.generator.generateAppProps(this.properties));
|
|
1363
1376
|
|
|
1364
1377
|
// Note: docProps/custom.xml is added during save() if custom properties exist
|
|
1365
1378
|
}
|
|
1366
1379
|
|
|
1367
|
-
|
|
1368
1380
|
/**
|
|
1369
1381
|
* Adds an existing paragraph to the document body
|
|
1370
1382
|
*
|
|
@@ -1654,9 +1666,7 @@ export class Document {
|
|
|
1654
1666
|
* @returns The paragraph at that index, or undefined if out of bounds
|
|
1655
1667
|
*/
|
|
1656
1668
|
getParagraphAt(index: number): Paragraph | undefined {
|
|
1657
|
-
const paragraphs = this.bodyElements.filter(
|
|
1658
|
-
(el): el is Paragraph => el instanceof Paragraph
|
|
1659
|
-
);
|
|
1669
|
+
const paragraphs = this.bodyElements.filter((el): el is Paragraph => el instanceof Paragraph);
|
|
1660
1670
|
return paragraphs[index];
|
|
1661
1671
|
}
|
|
1662
1672
|
|
|
@@ -1666,9 +1676,7 @@ export class Document {
|
|
|
1666
1676
|
* @returns The table at that index, or undefined if out of bounds
|
|
1667
1677
|
*/
|
|
1668
1678
|
getTableAt(index: number): Table | undefined {
|
|
1669
|
-
const tables = this.bodyElements.filter(
|
|
1670
|
-
(el): el is Table => el instanceof Table
|
|
1671
|
-
);
|
|
1679
|
+
const tables = this.bodyElements.filter((el): el is Table => el instanceof Table);
|
|
1672
1680
|
return tables[index];
|
|
1673
1681
|
}
|
|
1674
1682
|
|
|
@@ -1687,9 +1695,7 @@ export class Document {
|
|
|
1687
1695
|
* @returns The index of the paragraph, or -1 if not found
|
|
1688
1696
|
*/
|
|
1689
1697
|
getParagraphIndex(paragraph: Paragraph): number {
|
|
1690
|
-
const paragraphs = this.bodyElements.filter(
|
|
1691
|
-
(el): el is Paragraph => el instanceof Paragraph
|
|
1692
|
-
);
|
|
1698
|
+
const paragraphs = this.bodyElements.filter((el): el is Paragraph => el instanceof Paragraph);
|
|
1693
1699
|
return paragraphs.indexOf(paragraph);
|
|
1694
1700
|
}
|
|
1695
1701
|
|
|
@@ -1699,9 +1705,7 @@ export class Document {
|
|
|
1699
1705
|
* @returns The index of the table, or -1 if not found
|
|
1700
1706
|
*/
|
|
1701
1707
|
getTableIndex(table: Table): number {
|
|
1702
|
-
const tables = this.bodyElements.filter(
|
|
1703
|
-
(el): el is Table => el instanceof Table
|
|
1704
|
-
);
|
|
1708
|
+
const tables = this.bodyElements.filter((el): el is Table => el instanceof Table);
|
|
1705
1709
|
return tables.indexOf(table);
|
|
1706
1710
|
}
|
|
1707
1711
|
|
|
@@ -1711,13 +1715,9 @@ export class Document {
|
|
|
1711
1715
|
* @returns The next paragraph, or undefined if none exists
|
|
1712
1716
|
*/
|
|
1713
1717
|
getNextParagraph(paragraph: Paragraph): Paragraph | undefined {
|
|
1714
|
-
const paragraphs = this.bodyElements.filter(
|
|
1715
|
-
(el): el is Paragraph => el instanceof Paragraph
|
|
1716
|
-
);
|
|
1718
|
+
const paragraphs = this.bodyElements.filter((el): el is Paragraph => el instanceof Paragraph);
|
|
1717
1719
|
const index = paragraphs.indexOf(paragraph);
|
|
1718
|
-
return index >= 0 && index < paragraphs.length - 1
|
|
1719
|
-
? paragraphs[index + 1]
|
|
1720
|
-
: undefined;
|
|
1720
|
+
return index >= 0 && index < paragraphs.length - 1 ? paragraphs[index + 1] : undefined;
|
|
1721
1721
|
}
|
|
1722
1722
|
|
|
1723
1723
|
/**
|
|
@@ -1726,9 +1726,7 @@ export class Document {
|
|
|
1726
1726
|
* @returns The previous paragraph, or undefined if none exists
|
|
1727
1727
|
*/
|
|
1728
1728
|
getPreviousParagraph(paragraph: Paragraph): Paragraph | undefined {
|
|
1729
|
-
const paragraphs = this.bodyElements.filter(
|
|
1730
|
-
(el): el is Paragraph => el instanceof Paragraph
|
|
1731
|
-
);
|
|
1729
|
+
const paragraphs = this.bodyElements.filter((el): el is Paragraph => el instanceof Paragraph);
|
|
1732
1730
|
const index = paragraphs.indexOf(paragraph);
|
|
1733
1731
|
return index > 0 ? paragraphs[index - 1] : undefined;
|
|
1734
1732
|
}
|
|
@@ -1872,8 +1870,11 @@ export class Document {
|
|
|
1872
1870
|
* @param value - Property value
|
|
1873
1871
|
* @returns This document for chaining
|
|
1874
1872
|
*/
|
|
1875
|
-
setProperty(
|
|
1876
|
-
|
|
1873
|
+
setProperty(
|
|
1874
|
+
key: keyof DocumentProperties,
|
|
1875
|
+
value: DocumentProperties[keyof DocumentProperties]
|
|
1876
|
+
): this {
|
|
1877
|
+
this.properties[key] = value as never;
|
|
1877
1878
|
return this;
|
|
1878
1879
|
}
|
|
1879
1880
|
|
|
@@ -2011,10 +2012,7 @@ export class Document {
|
|
|
2011
2012
|
* @param value - Property value (string, number, boolean, or Date)
|
|
2012
2013
|
* @returns This document for chaining
|
|
2013
2014
|
*/
|
|
2014
|
-
setCustomProperty(
|
|
2015
|
-
name: string,
|
|
2016
|
-
value: string | number | boolean | Date
|
|
2017
|
-
): this {
|
|
2015
|
+
setCustomProperty(name: string, value: string | number | boolean | Date): this {
|
|
2018
2016
|
if (!this.properties.customProperties) {
|
|
2019
2017
|
this.properties.customProperties = {};
|
|
2020
2018
|
}
|
|
@@ -2027,9 +2025,7 @@ export class Document {
|
|
|
2027
2025
|
* @param properties - Object containing custom properties
|
|
2028
2026
|
* @returns This document for chaining
|
|
2029
2027
|
*/
|
|
2030
|
-
setCustomProperties(
|
|
2031
|
-
properties: Record<string, string | number | boolean | Date>
|
|
2032
|
-
): this {
|
|
2028
|
+
setCustomProperties(properties: Record<string, string | number | boolean | Date>): this {
|
|
2033
2029
|
this.properties.customProperties = { ...properties };
|
|
2034
2030
|
return this;
|
|
2035
2031
|
}
|
|
@@ -2039,9 +2035,7 @@ export class Document {
|
|
|
2039
2035
|
* @param name - Property name
|
|
2040
2036
|
* @returns Property value or undefined
|
|
2041
2037
|
*/
|
|
2042
|
-
getCustomProperty(
|
|
2043
|
-
name: string
|
|
2044
|
-
): string | number | boolean | Date | undefined {
|
|
2038
|
+
getCustomProperty(name: string): string | number | boolean | Date | undefined {
|
|
2045
2039
|
return this.properties.customProperties?.[name];
|
|
2046
2040
|
}
|
|
2047
2041
|
|
|
@@ -2120,10 +2114,7 @@ export class Document {
|
|
|
2120
2114
|
this.validator.checkMemoryThreshold();
|
|
2121
2115
|
|
|
2122
2116
|
// Check document size and warn if too large
|
|
2123
|
-
const sizeInfo = this.validator.estimateSize(
|
|
2124
|
-
this.bodyElements,
|
|
2125
|
-
this.imageManager
|
|
2126
|
-
);
|
|
2117
|
+
const sizeInfo = this.validator.estimateSize(this.bodyElements, this.imageManager);
|
|
2127
2118
|
if (sizeInfo.warning) {
|
|
2128
2119
|
this.logger.warn(sizeInfo.warning, {
|
|
2129
2120
|
totalMB: sizeInfo.totalEstimatedMB,
|
|
@@ -2174,7 +2165,7 @@ export class Document {
|
|
|
2174
2165
|
if (this.skipDocumentXmlRegeneration) {
|
|
2175
2166
|
this.logger.warn(
|
|
2176
2167
|
'skipDocumentXmlRegeneration is set: in-memory content modifications will NOT be saved. ' +
|
|
2177
|
-
|
|
2168
|
+
'Use acceptAllRevisions() instead of acceptAllRevisionsRawXml() if you need to modify the document after accepting revisions.'
|
|
2178
2169
|
);
|
|
2179
2170
|
} else {
|
|
2180
2171
|
this.updateDocumentXml();
|
|
@@ -2229,7 +2220,7 @@ export class Document {
|
|
|
2229
2220
|
}
|
|
2230
2221
|
|
|
2231
2222
|
// Atomic rename - only if save succeeded
|
|
2232
|
-
const { promises: fs } = await import(
|
|
2223
|
+
const { promises: fs } = await import('fs');
|
|
2233
2224
|
await fs.rename(tempPath, filePath);
|
|
2234
2225
|
|
|
2235
2226
|
// Mark save as successful - image data can now be released safely
|
|
@@ -2242,7 +2233,7 @@ export class Document {
|
|
|
2242
2233
|
|
|
2243
2234
|
// Cleanup temporary file on error
|
|
2244
2235
|
try {
|
|
2245
|
-
const { promises: fs } = await import(
|
|
2236
|
+
const { promises: fs } = await import('fs');
|
|
2246
2237
|
await fs.unlink(tempPath);
|
|
2247
2238
|
} catch (cleanupErr) {
|
|
2248
2239
|
logger.debug('Failed to clean up temp file', { tempPath, error: String(cleanupErr) });
|
|
@@ -2300,11 +2291,11 @@ export class Document {
|
|
|
2300
2291
|
|
|
2301
2292
|
// Auto-populate TOCs if enabled
|
|
2302
2293
|
if (this.autoPopulateTOCs) {
|
|
2303
|
-
const docXml = this.zipHandler.getFileAsString(
|
|
2294
|
+
const docXml = this.zipHandler.getFileAsString('word/document.xml');
|
|
2304
2295
|
if (docXml) {
|
|
2305
2296
|
const populatedXml = this.populateAllTOCsInXML(docXml);
|
|
2306
2297
|
if (populatedXml !== docXml) {
|
|
2307
|
-
this.zipHandler.updateFile(
|
|
2298
|
+
this.zipHandler.updateFile('word/document.xml', populatedXml);
|
|
2308
2299
|
}
|
|
2309
2300
|
}
|
|
2310
2301
|
}
|
|
@@ -2374,7 +2365,11 @@ export class Document {
|
|
|
2374
2365
|
* @private
|
|
2375
2366
|
*/
|
|
2376
2367
|
private _postProcessDocumentXml(): void {
|
|
2377
|
-
if (
|
|
2368
|
+
if (
|
|
2369
|
+
!this._flattenIncludePictureFields &&
|
|
2370
|
+
!this._stripOrphanRSIDs &&
|
|
2371
|
+
!this._clearDirectSpacingStyles
|
|
2372
|
+
) {
|
|
2378
2373
|
return;
|
|
2379
2374
|
}
|
|
2380
2375
|
|
|
@@ -2453,7 +2448,9 @@ export class Document {
|
|
|
2453
2448
|
const runContent = xml.substring(runStart, runEnd);
|
|
2454
2449
|
|
|
2455
2450
|
// Check for fldChar
|
|
2456
|
-
const fldCharMatch = /<w:fldChar\s+w:fldCharType\s*=\s*"(begin|separate|end)"/.exec(
|
|
2451
|
+
const fldCharMatch = /<w:fldChar\s+w:fldCharType\s*=\s*"(begin|separate|end)"/.exec(
|
|
2452
|
+
runContent
|
|
2453
|
+
);
|
|
2457
2454
|
if (fldCharMatch) {
|
|
2458
2455
|
fieldTokens.push({
|
|
2459
2456
|
type: fldCharMatch[1] as 'begin' | 'separate' | 'end',
|
|
@@ -2498,7 +2495,11 @@ export class Document {
|
|
|
2498
2495
|
|
|
2499
2496
|
// Collect any instrText runs between this begin and the next fldChar
|
|
2500
2497
|
const nextFldCharIdx = i + 1 < fieldTokens.length ? fieldTokens[i + 1]!.runStart : Infinity;
|
|
2501
|
-
while (
|
|
2498
|
+
while (
|
|
2499
|
+
instrTokenIdx < instrTokens.length &&
|
|
2500
|
+
instrTokens[instrTokenIdx]!.runStart < nextFldCharIdx &&
|
|
2501
|
+
instrTokens[instrTokenIdx]!.runStart > token.runStart
|
|
2502
|
+
) {
|
|
2502
2503
|
const instr = instrTokens[instrTokenIdx]!;
|
|
2503
2504
|
stackTop.instrRuns.push({ start: instr.runStart, end: instr.runEnd });
|
|
2504
2505
|
|
|
@@ -2516,9 +2517,11 @@ export class Document {
|
|
|
2516
2517
|
// Also collect instrText runs between begin and separate that we may have missed
|
|
2517
2518
|
// (when instrText spans multiple runs between begin and separate)
|
|
2518
2519
|
const beginToken = fieldTokens[current.tokenIndex]!;
|
|
2519
|
-
while (
|
|
2520
|
-
|
|
2521
|
-
|
|
2520
|
+
while (
|
|
2521
|
+
instrTokenIdx < instrTokens.length &&
|
|
2522
|
+
instrTokens[instrTokenIdx]!.runStart > beginToken.runStart &&
|
|
2523
|
+
instrTokens[instrTokenIdx]!.runStart < token.runStart
|
|
2524
|
+
) {
|
|
2522
2525
|
const instr = instrTokens[instrTokenIdx]!;
|
|
2523
2526
|
current.instrRuns.push({ start: instr.runStart, end: instr.runEnd });
|
|
2524
2527
|
if (/^\s*INCLUDEPICTURE\b/i.test(instr.text)) {
|
|
@@ -2556,7 +2559,7 @@ export class Document {
|
|
|
2556
2559
|
// Deduplicate overlapping ranges
|
|
2557
2560
|
const uniqueRuns: { start: number; end: number }[] = [];
|
|
2558
2561
|
for (const run of runsToRemove) {
|
|
2559
|
-
const isDuplicate = uniqueRuns.some(u => u.start === run.start && u.end === run.end);
|
|
2562
|
+
const isDuplicate = uniqueRuns.some((u) => u.start === run.start && u.end === run.end);
|
|
2560
2563
|
if (!isDuplicate) {
|
|
2561
2564
|
uniqueRuns.push(run);
|
|
2562
2565
|
}
|
|
@@ -2657,20 +2660,14 @@ export class Document {
|
|
|
2657
2660
|
const protectedBlocks: string[] = [];
|
|
2658
2661
|
// Protect pPrChange and rPr blocks separately to prevent cross-tag
|
|
2659
2662
|
// mismatch (e.g., <w:rPr> matching </w:pPrChange> in the alternation).
|
|
2660
|
-
let safeInner = inner.replace(
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
/<w:rPr\b[\s\S]*?<\/w:rPr>/g,
|
|
2669
|
-
(block) => {
|
|
2670
|
-
protectedBlocks.push(block);
|
|
2671
|
-
return `\x00PROTECTED_${protectedBlocks.length - 1}\x00`;
|
|
2672
|
-
}
|
|
2673
|
-
);
|
|
2663
|
+
let safeInner = inner.replace(/<w:pPrChange\b[\s\S]*?<\/w:pPrChange>/g, (block) => {
|
|
2664
|
+
protectedBlocks.push(block);
|
|
2665
|
+
return `\x00PROTECTED_${protectedBlocks.length - 1}\x00`;
|
|
2666
|
+
});
|
|
2667
|
+
safeInner = safeInner.replace(/<w:rPr\b[\s\S]*?<\/w:rPr>/g, (block) => {
|
|
2668
|
+
protectedBlocks.push(block);
|
|
2669
|
+
return `\x00PROTECTED_${protectedBlocks.length - 1}\x00`;
|
|
2670
|
+
});
|
|
2674
2671
|
|
|
2675
2672
|
// Check if this pPr contains a pStyle matching our list
|
|
2676
2673
|
// (checked against safeInner to avoid matching styles inside pPrChange)
|
|
@@ -2756,7 +2753,11 @@ export class Document {
|
|
|
2756
2753
|
* Updates the core properties with current values
|
|
2757
2754
|
*/
|
|
2758
2755
|
private updateCoreProps(): void {
|
|
2759
|
-
if (
|
|
2756
|
+
if (
|
|
2757
|
+
this._removedParts.has(DOCX_PATHS.CORE_PROPS) ||
|
|
2758
|
+
this._removedParts.has('docProps/core.xml')
|
|
2759
|
+
)
|
|
2760
|
+
return;
|
|
2760
2761
|
const xml = this.generator.generateCoreProps(this.properties);
|
|
2761
2762
|
this.zipHandler.updateFile(DOCX_PATHS.CORE_PROPS, xml);
|
|
2762
2763
|
}
|
|
@@ -2766,7 +2767,8 @@ export class Document {
|
|
|
2766
2767
|
* Uses preservation strategy to maintain original metadata when unmodified
|
|
2767
2768
|
*/
|
|
2768
2769
|
private updateAppProps(): void {
|
|
2769
|
-
if (this._removedParts.has(DOCX_PATHS.APP_PROPS) || this._removedParts.has('docProps/app.xml'))
|
|
2770
|
+
if (this._removedParts.has(DOCX_PATHS.APP_PROPS) || this._removedParts.has('docProps/app.xml'))
|
|
2771
|
+
return;
|
|
2770
2772
|
if (this._originalAppPropsXml && !this._appPropsModified) {
|
|
2771
2773
|
// Preserve original as-is — no changes to app properties
|
|
2772
2774
|
return;
|
|
@@ -2808,7 +2810,10 @@ export class Document {
|
|
|
2808
2810
|
if (this.properties.application !== undefined) {
|
|
2809
2811
|
const escaped = XMLBuilder.sanitizeXmlContent(this.properties.application);
|
|
2810
2812
|
if (xml.includes('<Application>')) {
|
|
2811
|
-
xml = xml.replace(
|
|
2813
|
+
xml = xml.replace(
|
|
2814
|
+
/<Application>[^<]*<\/Application>/,
|
|
2815
|
+
`<Application>${escaped}</Application>`
|
|
2816
|
+
);
|
|
2812
2817
|
}
|
|
2813
2818
|
}
|
|
2814
2819
|
|
|
@@ -3028,10 +3033,7 @@ export class Document {
|
|
|
3028
3033
|
resultXml = resultXml.replace(stylePattern, newStyleXml);
|
|
3029
3034
|
} else {
|
|
3030
3035
|
// Style doesn't exist in original - append before </w:styles>
|
|
3031
|
-
resultXml = resultXml.replace(
|
|
3032
|
-
'</w:styles>',
|
|
3033
|
-
`${newStyleXml}\n</w:styles>`
|
|
3034
|
-
);
|
|
3036
|
+
resultXml = resultXml.replace('</w:styles>', `${newStyleXml}\n</w:styles>`);
|
|
3035
3037
|
}
|
|
3036
3038
|
}
|
|
3037
3039
|
|
|
@@ -3097,7 +3099,10 @@ export class Document {
|
|
|
3097
3099
|
}
|
|
3098
3100
|
|
|
3099
3101
|
for (const entry of entriesToRemove) {
|
|
3100
|
-
cleanedRels = cleanedRels.replace(
|
|
3102
|
+
cleanedRels = cleanedRels.replace(
|
|
3103
|
+
new RegExp(`\\s*${entry.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`),
|
|
3104
|
+
''
|
|
3105
|
+
);
|
|
3101
3106
|
}
|
|
3102
3107
|
|
|
3103
3108
|
if (entriesToRemove.length > 0) {
|
|
@@ -3132,8 +3137,12 @@ export class Document {
|
|
|
3132
3137
|
const removedNumIds = this.numberingManager.getRemovedNumIds();
|
|
3133
3138
|
|
|
3134
3139
|
// If nothing was modified or removed, return original as-is
|
|
3135
|
-
if (
|
|
3136
|
-
|
|
3140
|
+
if (
|
|
3141
|
+
modifiedAbstractNumIds.size === 0 &&
|
|
3142
|
+
modifiedNumIds.size === 0 &&
|
|
3143
|
+
removedAbstractNumIds.size === 0 &&
|
|
3144
|
+
removedNumIds.size === 0
|
|
3145
|
+
) {
|
|
3137
3146
|
return this._originalNumberingXml;
|
|
3138
3147
|
}
|
|
3139
3148
|
|
|
@@ -3445,9 +3454,12 @@ export class Document {
|
|
|
3445
3454
|
const esc = XMLBuilder.escapeXmlAttribute;
|
|
3446
3455
|
let protXml = `\n <w:documentProtection w:edit="${esc(prot.edit)}" w:enforcement="${prot.enforcement ? '1' : '0'}"`;
|
|
3447
3456
|
if (prot.cryptProviderType) protXml += ` w:cryptProviderType="${esc(prot.cryptProviderType)}"`;
|
|
3448
|
-
if (prot.cryptAlgorithmClass)
|
|
3449
|
-
|
|
3450
|
-
if (prot.
|
|
3457
|
+
if (prot.cryptAlgorithmClass)
|
|
3458
|
+
protXml += ` w:cryptAlgorithmClass="${esc(prot.cryptAlgorithmClass)}"`;
|
|
3459
|
+
if (prot.cryptAlgorithmType)
|
|
3460
|
+
protXml += ` w:cryptAlgorithmType="${esc(prot.cryptAlgorithmType)}"`;
|
|
3461
|
+
if (prot.cryptAlgorithmSid)
|
|
3462
|
+
protXml += ` w:cryptAlgorithmSid="${esc(String(prot.cryptAlgorithmSid))}"`;
|
|
3451
3463
|
if (prot.cryptSpinCount) protXml += ` w:cryptSpinCount="${esc(String(prot.cryptSpinCount))}"`;
|
|
3452
3464
|
if (prot.hash) protXml += ` w:hash="${esc(prot.hash)}"`;
|
|
3453
3465
|
if (prot.salt) protXml += ` w:salt="${esc(prot.salt)}"`;
|
|
@@ -3561,7 +3573,7 @@ export class Document {
|
|
|
3561
3573
|
|
|
3562
3574
|
// Insert the block before defaultTabStop (schema order preserved since array is ordered)
|
|
3563
3575
|
if (preElements.length > 0) {
|
|
3564
|
-
const preBlock = preElements.map(e => '\n ' + e).join('');
|
|
3576
|
+
const preBlock = preElements.map((e) => '\n ' + e).join('');
|
|
3565
3577
|
if (/<w:defaultTabStop\b/.test(xml)) {
|
|
3566
3578
|
xml = xml.replace(/<w:defaultTabStop\b/, preBlock + '\n <w:defaultTabStop');
|
|
3567
3579
|
} else {
|
|
@@ -3575,9 +3587,15 @@ export class Document {
|
|
|
3575
3587
|
if (mod.has('defaultTabStop') && this._defaultTabStop !== undefined) {
|
|
3576
3588
|
const hasDTS = /<w:defaultTabStop\b[^>]*\/?>/.test(xml);
|
|
3577
3589
|
if (hasDTS) {
|
|
3578
|
-
xml = xml.replace(
|
|
3590
|
+
xml = xml.replace(
|
|
3591
|
+
/<w:defaultTabStop\b[^>]*\/?>/,
|
|
3592
|
+
`<w:defaultTabStop w:val="${this._defaultTabStop}"/>`
|
|
3593
|
+
);
|
|
3579
3594
|
} else {
|
|
3580
|
-
xml = xml.replace(
|
|
3595
|
+
xml = xml.replace(
|
|
3596
|
+
/<\/w:settings>/,
|
|
3597
|
+
` <w:defaultTabStop w:val="${this._defaultTabStop}"/>\n</w:settings>`
|
|
3598
|
+
);
|
|
3581
3599
|
}
|
|
3582
3600
|
}
|
|
3583
3601
|
|
|
@@ -3598,9 +3616,12 @@ export class Document {
|
|
|
3598
3616
|
}
|
|
3599
3617
|
|
|
3600
3618
|
if (postElements.length > 0) {
|
|
3601
|
-
const postBlock = postElements.map(e => '\n ' + e).join('');
|
|
3619
|
+
const postBlock = postElements.map((e) => '\n ' + e).join('');
|
|
3602
3620
|
if (/<w:characterSpacingControl\b/.test(xml)) {
|
|
3603
|
-
xml = xml.replace(
|
|
3621
|
+
xml = xml.replace(
|
|
3622
|
+
/<w:characterSpacingControl\b/,
|
|
3623
|
+
postBlock + '\n <w:characterSpacingControl'
|
|
3624
|
+
);
|
|
3604
3625
|
} else {
|
|
3605
3626
|
xml = xml.replace(/<\/w:settings>/, postBlock + '\n</w:settings>');
|
|
3606
3627
|
}
|
|
@@ -3676,7 +3697,11 @@ export class Document {
|
|
|
3676
3697
|
* @param col - Column index (0-based)
|
|
3677
3698
|
* @returns The resolved ShadingConfig, or undefined if no shading applies
|
|
3678
3699
|
*/
|
|
3679
|
-
getComputedCellShading(
|
|
3700
|
+
getComputedCellShading(
|
|
3701
|
+
table: Table,
|
|
3702
|
+
row: number,
|
|
3703
|
+
col: number
|
|
3704
|
+
): import('../elements/CommonTypes').ShadingConfig | undefined {
|
|
3680
3705
|
const tableRow = table.getRow(row);
|
|
3681
3706
|
if (!tableRow) return undefined;
|
|
3682
3707
|
const cell = tableRow.getCell(col);
|
|
@@ -3852,12 +3877,7 @@ export class Document {
|
|
|
3852
3877
|
* console.log(`Updated ${count} elements`);
|
|
3853
3878
|
* ```
|
|
3854
3879
|
*/
|
|
3855
|
-
applyStyleToAll(
|
|
3856
|
-
styleId: string,
|
|
3857
|
-
predicate: (
|
|
3858
|
-
element: BodyElement
|
|
3859
|
-
) => boolean
|
|
3860
|
-
): number {
|
|
3880
|
+
applyStyleToAll(styleId: string, predicate: (element: BodyElement) => boolean): number {
|
|
3861
3881
|
let count = 0;
|
|
3862
3882
|
|
|
3863
3883
|
for (const element of this.bodyElements) {
|
|
@@ -3947,10 +3967,7 @@ export class Document {
|
|
|
3947
3967
|
* console.log(`Updated ${count} paragraphs`);
|
|
3948
3968
|
* ```
|
|
3949
3969
|
*/
|
|
3950
|
-
applyStyleToAllParagraphsWithStyle(
|
|
3951
|
-
currentStyleId: string,
|
|
3952
|
-
newStyleId: string
|
|
3953
|
-
): number {
|
|
3970
|
+
applyStyleToAllParagraphsWithStyle(currentStyleId: string, newStyleId: string): number {
|
|
3954
3971
|
let count = 0;
|
|
3955
3972
|
|
|
3956
3973
|
// Check body paragraphs
|
|
@@ -4104,7 +4121,7 @@ export class Document {
|
|
|
4104
4121
|
* console.log(`Updated ${count} tables`);
|
|
4105
4122
|
* ```
|
|
4106
4123
|
*/
|
|
4107
|
-
setAllTablesLayout(layout:
|
|
4124
|
+
setAllTablesLayout(layout: 'auto' | 'fixed'): number {
|
|
4108
4125
|
const tables = this.getTables();
|
|
4109
4126
|
|
|
4110
4127
|
for (const table of tables) {
|
|
@@ -4211,17 +4228,17 @@ export class Document {
|
|
|
4211
4228
|
fixTODHyperlinks(): number {
|
|
4212
4229
|
console.warn(
|
|
4213
4230
|
'DEPRECATION WARNING: fixTODHyperlinks() is deprecated. ' +
|
|
4214
|
-
|
|
4231
|
+
'Use Template_UI WordDocumentProcessor.fixExistingTopHyperlinks() instead.'
|
|
4215
4232
|
);
|
|
4216
4233
|
let count = 0;
|
|
4217
4234
|
|
|
4218
4235
|
// Ensure _top bookmark exists at document start
|
|
4219
|
-
if (!this.hasBookmark(
|
|
4236
|
+
if (!this.hasBookmark('_top')) {
|
|
4220
4237
|
const paragraphs = this.getAllParagraphs();
|
|
4221
4238
|
if (paragraphs.length > 0) {
|
|
4222
4239
|
const firstPara = paragraphs[0];
|
|
4223
4240
|
if (firstPara) {
|
|
4224
|
-
const bookmark = new Bookmark({ name:
|
|
4241
|
+
const bookmark = new Bookmark({ name: '_top' });
|
|
4225
4242
|
const registered = this.bookmarkManager.register(bookmark);
|
|
4226
4243
|
firstPara.addBookmark(registered);
|
|
4227
4244
|
}
|
|
@@ -4235,23 +4252,23 @@ export class Document {
|
|
|
4235
4252
|
const text = hyperlink.getText().toLowerCase();
|
|
4236
4253
|
|
|
4237
4254
|
// Match variations: "top of document", "top of the document", etc.
|
|
4238
|
-
if (text.includes(
|
|
4255
|
+
if (text.includes('top') && text.includes('document')) {
|
|
4239
4256
|
// Update text
|
|
4240
|
-
hyperlink.setText(
|
|
4257
|
+
hyperlink.setText('Top of the Document');
|
|
4241
4258
|
|
|
4242
4259
|
// Update formatting
|
|
4243
4260
|
hyperlink.setFormatting({
|
|
4244
|
-
font:
|
|
4261
|
+
font: 'Verdana',
|
|
4245
4262
|
size: 12,
|
|
4246
|
-
underline:
|
|
4247
|
-
color:
|
|
4263
|
+
underline: 'single',
|
|
4264
|
+
color: '0000FF',
|
|
4248
4265
|
});
|
|
4249
4266
|
|
|
4250
4267
|
// Update anchor to _top
|
|
4251
|
-
hyperlink.setAnchor(
|
|
4268
|
+
hyperlink.setAnchor('_top');
|
|
4252
4269
|
|
|
4253
4270
|
// Set paragraph alignment to right
|
|
4254
|
-
paragraph.setAlignment(
|
|
4271
|
+
paragraph.setAlignment('right');
|
|
4255
4272
|
|
|
4256
4273
|
count++;
|
|
4257
4274
|
}
|
|
@@ -4294,7 +4311,7 @@ export class Document {
|
|
|
4294
4311
|
const firstCell = cells[0];
|
|
4295
4312
|
if (firstCell) {
|
|
4296
4313
|
const text = firstCell.getText().toLowerCase();
|
|
4297
|
-
if (text.includes(
|
|
4314
|
+
if (text.includes('if')) {
|
|
4298
4315
|
hasIfColumn = true;
|
|
4299
4316
|
break;
|
|
4300
4317
|
}
|
|
@@ -4382,7 +4399,7 @@ export class Document {
|
|
|
4382
4399
|
/** Header row text formatting */
|
|
4383
4400
|
headerRowFormatting?: {
|
|
4384
4401
|
bold?: boolean;
|
|
4385
|
-
alignment?:
|
|
4402
|
+
alignment?: 'left' | 'center' | 'right' | 'justify';
|
|
4386
4403
|
font?: string;
|
|
4387
4404
|
size?: number;
|
|
4388
4405
|
color?: string;
|
|
@@ -4407,23 +4424,25 @@ export class Document {
|
|
|
4407
4424
|
singleCellTablesShaded: number;
|
|
4408
4425
|
} {
|
|
4409
4426
|
// Handle different parameter combinations
|
|
4410
|
-
let options:
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
+
let options:
|
|
4428
|
+
| {
|
|
4429
|
+
autofitToWindow?: boolean;
|
|
4430
|
+
singleCellShading?: string;
|
|
4431
|
+
headerRowShading?: string;
|
|
4432
|
+
headerRowFormatting?: {
|
|
4433
|
+
bold?: boolean;
|
|
4434
|
+
alignment?: 'left' | 'center' | 'right' | 'justify';
|
|
4435
|
+
font?: string;
|
|
4436
|
+
size?: number;
|
|
4437
|
+
color?: string;
|
|
4438
|
+
spacingBefore?: number;
|
|
4439
|
+
spacingAfter?: number;
|
|
4440
|
+
};
|
|
4441
|
+
cellMargins?: { top?: number; bottom?: number; left?: number; right?: number };
|
|
4442
|
+
skipSingleCellTables?: boolean;
|
|
4443
|
+
}
|
|
4444
|
+
| undefined;
|
|
4445
|
+
if (typeof colorOrOptions === 'string') {
|
|
4427
4446
|
if (multiCellColor) {
|
|
4428
4447
|
// Two colors provided: applyStandardTableFormatting('BFBFBF', 'E9E9E9')
|
|
4429
4448
|
options = {
|
|
@@ -4440,15 +4459,13 @@ export class Document {
|
|
|
4440
4459
|
|
|
4441
4460
|
// Default values
|
|
4442
4461
|
const singleCellShading = options?.singleCellShading?.toUpperCase();
|
|
4443
|
-
const headerRowShading = (
|
|
4444
|
-
options?.headerRowShading || "E9E9E9"
|
|
4445
|
-
).toUpperCase();
|
|
4462
|
+
const headerRowShading = (options?.headerRowShading || 'E9E9E9').toUpperCase();
|
|
4446
4463
|
const headerRowFormatting = {
|
|
4447
4464
|
bold: options?.headerRowFormatting?.bold !== false,
|
|
4448
|
-
alignment: options?.headerRowFormatting?.alignment || (
|
|
4449
|
-
font: options?.headerRowFormatting?.font ||
|
|
4465
|
+
alignment: options?.headerRowFormatting?.alignment || ('center' as const),
|
|
4466
|
+
font: options?.headerRowFormatting?.font || 'Verdana',
|
|
4450
4467
|
size: options?.headerRowFormatting?.size || 12,
|
|
4451
|
-
color: options?.headerRowFormatting?.color ||
|
|
4468
|
+
color: options?.headerRowFormatting?.color || '000000',
|
|
4452
4469
|
spacingBefore: options?.headerRowFormatting?.spacingBefore ?? 60,
|
|
4453
4470
|
spacingAfter: options?.headerRowFormatting?.spacingAfter ?? 60,
|
|
4454
4471
|
};
|
|
@@ -4458,8 +4475,7 @@ export class Document {
|
|
|
4458
4475
|
left: options?.cellMargins?.left ?? 115, // 0.08 inches
|
|
4459
4476
|
right: options?.cellMargins?.right ?? 115, // 0.08 inches
|
|
4460
4477
|
};
|
|
4461
|
-
const skipSingleCellTables =
|
|
4462
|
-
options?.skipSingleCellTables !== false && !singleCellShading;
|
|
4478
|
+
const skipSingleCellTables = options?.skipSingleCellTables !== false && !singleCellShading;
|
|
4463
4479
|
|
|
4464
4480
|
// Statistics
|
|
4465
4481
|
let tablesProcessed = 0;
|
|
@@ -4476,14 +4492,14 @@ export class Document {
|
|
|
4476
4492
|
|
|
4477
4493
|
// Apply borders to all cells (always applied to all tables)
|
|
4478
4494
|
table.setAllBorders({
|
|
4479
|
-
style:
|
|
4495
|
+
style: 'single',
|
|
4480
4496
|
size: 4,
|
|
4481
|
-
color:
|
|
4497
|
+
color: '000000',
|
|
4482
4498
|
});
|
|
4483
4499
|
|
|
4484
4500
|
// Set table width to autofit to window (always applied to all tables)
|
|
4485
|
-
table.setLayout(
|
|
4486
|
-
table.setWidthType(
|
|
4501
|
+
table.setLayout('auto');
|
|
4502
|
+
table.setWidthType('pct');
|
|
4487
4503
|
table.setWidth(5000);
|
|
4488
4504
|
|
|
4489
4505
|
// Handle 1x1 (single-cell) tables separately
|
|
@@ -4516,10 +4532,7 @@ export class Document {
|
|
|
4516
4532
|
for (const para of cell.getParagraphs()) {
|
|
4517
4533
|
// Skip paragraphs that are part of numbered or bulleted lists
|
|
4518
4534
|
const numPr = para.getFormatting().numbering;
|
|
4519
|
-
if (
|
|
4520
|
-
numPr &&
|
|
4521
|
-
(numPr.level !== undefined || numPr.numId !== undefined)
|
|
4522
|
-
) {
|
|
4535
|
+
if (numPr && (numPr.level !== undefined || numPr.numId !== undefined)) {
|
|
4523
4536
|
continue; // Preserve list formatting
|
|
4524
4537
|
}
|
|
4525
4538
|
|
|
@@ -4557,17 +4570,16 @@ export class Document {
|
|
|
4557
4570
|
const currentPattern = currentShading?.pattern?.toLowerCase();
|
|
4558
4571
|
|
|
4559
4572
|
// Check if color is a valid 6-character hex code (not 'auto' or other special values)
|
|
4560
|
-
const isValidHexColor = /^[0-9A-F]{6}$/i.test(currentColor ||
|
|
4561
|
-
const hasHexFillShading =
|
|
4562
|
-
currentColor && currentColor !== "FFFFFF" && isValidHexColor;
|
|
4573
|
+
const isValidHexColor = /^[0-9A-F]{6}$/i.test(currentColor || '');
|
|
4574
|
+
const hasHexFillShading = currentColor && currentColor !== 'FFFFFF' && isValidHexColor;
|
|
4563
4575
|
|
|
4564
4576
|
// Check if cell has pattern-based shading (like pct10, pct20, etc.)
|
|
4565
4577
|
// Patterns like 'clear' or 'nil' don't count as shading
|
|
4566
4578
|
const hasPatternShading =
|
|
4567
4579
|
currentPattern &&
|
|
4568
|
-
currentPattern !==
|
|
4569
|
-
currentPattern !==
|
|
4570
|
-
currentPattern !==
|
|
4580
|
+
currentPattern !== 'clear' &&
|
|
4581
|
+
currentPattern !== 'nil' &&
|
|
4582
|
+
currentPattern !== 'auto';
|
|
4571
4583
|
|
|
4572
4584
|
if (hasHexFillShading || hasPatternShading) {
|
|
4573
4585
|
// Apply the color passed to the method
|
|
@@ -4578,14 +4590,11 @@ export class Document {
|
|
|
4578
4590
|
for (const para of cell.getParagraphs()) {
|
|
4579
4591
|
// Skip paragraphs that are part of numbered or bulleted lists
|
|
4580
4592
|
const numPr = para.getFormatting().numbering;
|
|
4581
|
-
if (
|
|
4582
|
-
numPr &&
|
|
4583
|
-
(numPr.level !== undefined || numPr.numId !== undefined)
|
|
4584
|
-
) {
|
|
4593
|
+
if (numPr && (numPr.level !== undefined || numPr.numId !== undefined)) {
|
|
4585
4594
|
continue; // Preserve list formatting
|
|
4586
4595
|
}
|
|
4587
4596
|
|
|
4588
|
-
para.setAlignment(
|
|
4597
|
+
para.setAlignment('center');
|
|
4589
4598
|
para.setSpaceBefore(60); // 3pt
|
|
4590
4599
|
para.setSpaceAfter(60); // 3pt
|
|
4591
4600
|
|
|
@@ -4595,11 +4604,11 @@ export class Document {
|
|
|
4595
4604
|
continue;
|
|
4596
4605
|
}
|
|
4597
4606
|
run.setBold(true);
|
|
4598
|
-
run.setFont(
|
|
4607
|
+
run.setFont('Verdana', 12);
|
|
4599
4608
|
// Preserve white font - don't change color if run is white (FFFFFF)
|
|
4600
4609
|
const currentColor = run.getColor()?.toUpperCase();
|
|
4601
4610
|
if (currentColor !== 'FFFFFF') {
|
|
4602
|
-
run.setColor(
|
|
4611
|
+
run.setColor('000000');
|
|
4603
4612
|
}
|
|
4604
4613
|
}
|
|
4605
4614
|
}
|
|
@@ -4676,7 +4685,7 @@ export class Document {
|
|
|
4676
4685
|
if (relId && largeImageIds.has(relId)) {
|
|
4677
4686
|
// Remove indentation before centering
|
|
4678
4687
|
paragraph.formatting.indentation = undefined;
|
|
4679
|
-
paragraph.setAlignment(
|
|
4688
|
+
paragraph.setAlignment('center');
|
|
4680
4689
|
count++;
|
|
4681
4690
|
break; // Only count paragraph once
|
|
4682
4691
|
}
|
|
@@ -4778,7 +4787,7 @@ export class Document {
|
|
|
4778
4787
|
if (hasLargeImage) {
|
|
4779
4788
|
// Remove indentation before centering
|
|
4780
4789
|
paragraph.formatting.indentation = undefined;
|
|
4781
|
-
paragraph.setAlignment(
|
|
4790
|
+
paragraph.setAlignment('center');
|
|
4782
4791
|
count++;
|
|
4783
4792
|
}
|
|
4784
4793
|
}
|
|
@@ -4805,7 +4814,7 @@ export class Document {
|
|
|
4805
4814
|
|
|
4806
4815
|
if (numbering) {
|
|
4807
4816
|
// Has numbering - it's a list item
|
|
4808
|
-
paragraph.setLineSpacing(spacingTwips,
|
|
4817
|
+
paragraph.setLineSpacing(spacingTwips, 'auto');
|
|
4809
4818
|
count++;
|
|
4810
4819
|
}
|
|
4811
4820
|
}
|
|
@@ -4834,9 +4843,9 @@ export class Document {
|
|
|
4834
4843
|
|
|
4835
4844
|
// Create a standard numbered list
|
|
4836
4845
|
const standardNumId = this.numberingManager.createNumberedList(3, [
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4846
|
+
'decimal',
|
|
4847
|
+
'lowerLetter',
|
|
4848
|
+
'lowerRoman',
|
|
4840
4849
|
]);
|
|
4841
4850
|
|
|
4842
4851
|
// Collect all paragraphs with numbering and identify numbered lists
|
|
@@ -4851,9 +4860,7 @@ export class Document {
|
|
|
4851
4860
|
const instance = this.numberingManager.getInstance(numbering.numId);
|
|
4852
4861
|
if (!instance) continue;
|
|
4853
4862
|
|
|
4854
|
-
const abstractNum = this.numberingManager.getAbstractNumbering(
|
|
4855
|
-
instance.getAbstractNumId()
|
|
4856
|
-
);
|
|
4863
|
+
const abstractNum = this.numberingManager.getAbstractNumbering(instance.getAbstractNumId());
|
|
4857
4864
|
if (!abstractNum) continue;
|
|
4858
4865
|
|
|
4859
4866
|
// Check if level 0 is a numbered format (not bullet)
|
|
@@ -4862,7 +4869,7 @@ export class Document {
|
|
|
4862
4869
|
|
|
4863
4870
|
const format = level0.getFormat();
|
|
4864
4871
|
// Numbered formats: decimal, lowerRoman, upperRoman, lowerLetter, upperLetter, etc.
|
|
4865
|
-
if (format !==
|
|
4872
|
+
if (format !== 'bullet') {
|
|
4866
4873
|
numberedParas.push({ para, level: numbering.level });
|
|
4867
4874
|
}
|
|
4868
4875
|
}
|
|
@@ -4899,11 +4906,7 @@ export class Document {
|
|
|
4899
4906
|
let count = 0;
|
|
4900
4907
|
|
|
4901
4908
|
// Create a standard bullet list with custom bullets
|
|
4902
|
-
const standardNumId = this.numberingManager.createBulletList(3, [
|
|
4903
|
-
"•",
|
|
4904
|
-
"○",
|
|
4905
|
-
"■",
|
|
4906
|
-
]);
|
|
4909
|
+
const standardNumId = this.numberingManager.createBulletList(3, ['•', '○', '■']);
|
|
4907
4910
|
|
|
4908
4911
|
// Collect all paragraphs with numbering and identify bullet lists
|
|
4909
4912
|
const paragraphs = this.getAllParagraphs();
|
|
@@ -4917,9 +4920,7 @@ export class Document {
|
|
|
4917
4920
|
const instance = this.numberingManager.getInstance(numbering.numId);
|
|
4918
4921
|
if (!instance) continue;
|
|
4919
4922
|
|
|
4920
|
-
const abstractNum = this.numberingManager.getAbstractNumbering(
|
|
4921
|
-
instance.getAbstractNumId()
|
|
4922
|
-
);
|
|
4923
|
+
const abstractNum = this.numberingManager.getAbstractNumbering(instance.getAbstractNumId());
|
|
4923
4924
|
if (!abstractNum) continue;
|
|
4924
4925
|
|
|
4925
4926
|
// Check if level 0 is a bullet format
|
|
@@ -4927,7 +4928,7 @@ export class Document {
|
|
|
4927
4928
|
if (!level0) continue;
|
|
4928
4929
|
|
|
4929
4930
|
const format = level0.getFormat();
|
|
4930
|
-
if (format ===
|
|
4931
|
+
if (format === 'bullet') {
|
|
4931
4932
|
bulletParas.push({ para, level: numbering.level });
|
|
4932
4933
|
}
|
|
4933
4934
|
}
|
|
@@ -5015,11 +5016,11 @@ export class Document {
|
|
|
5015
5016
|
|
|
5016
5017
|
// 3. Scan headers and footers for numId references
|
|
5017
5018
|
this.collectNumIdsFromElements(
|
|
5018
|
-
this.headerFooterManager.getAllHeaders().flatMap(entry => entry.header.getElements()),
|
|
5019
|
+
this.headerFooterManager.getAllHeaders().flatMap((entry) => entry.header.getElements()),
|
|
5019
5020
|
usedNumIds
|
|
5020
5021
|
);
|
|
5021
5022
|
this.collectNumIdsFromElements(
|
|
5022
|
-
this.headerFooterManager.getAllFooters().flatMap(entry => entry.footer.getElements()),
|
|
5023
|
+
this.headerFooterManager.getAllFooters().flatMap((entry) => entry.footer.getElements()),
|
|
5023
5024
|
usedNumIds
|
|
5024
5025
|
);
|
|
5025
5026
|
|
|
@@ -5223,7 +5224,9 @@ export class Document {
|
|
|
5223
5224
|
// Add synthetic bookmarkEnd to the last paragraph in the document body
|
|
5224
5225
|
const lastPara = this.getLastParagraph();
|
|
5225
5226
|
if (lastPara) {
|
|
5226
|
-
lastPara.addBookmarkEnd(
|
|
5227
|
+
lastPara.addBookmarkEnd(
|
|
5228
|
+
new Bookmark({ id, name: `_repair_${id}`, skipNormalization: true })
|
|
5229
|
+
);
|
|
5227
5230
|
this.logger.warn(`Bookmark validation: added missing bookmarkEnd for ID ${id}`);
|
|
5228
5231
|
repairs++;
|
|
5229
5232
|
}
|
|
@@ -5374,7 +5377,10 @@ export class Document {
|
|
|
5374
5377
|
* Handles both paragraph numbering and raw nested content in tables.
|
|
5375
5378
|
* @private
|
|
5376
5379
|
*/
|
|
5377
|
-
private collectNumIdsFromElements(
|
|
5380
|
+
private collectNumIdsFromElements(
|
|
5381
|
+
elements: (Paragraph | Table)[],
|
|
5382
|
+
usedNumIds: Set<number>
|
|
5383
|
+
): void {
|
|
5378
5384
|
for (const element of elements) {
|
|
5379
5385
|
if (element instanceof Paragraph) {
|
|
5380
5386
|
this.collectNumIdsFromParagraphs([element], usedNumIds);
|
|
@@ -5472,7 +5478,7 @@ export class Document {
|
|
|
5472
5478
|
if (!alreadyHasBlank) {
|
|
5473
5479
|
// Insert blank paragraph with Normal style after this list item
|
|
5474
5480
|
const blankPara = new Paragraph();
|
|
5475
|
-
blankPara.setStyle(
|
|
5481
|
+
blankPara.setStyle('Normal');
|
|
5476
5482
|
|
|
5477
5483
|
// Insert at position i+1 (after current element)
|
|
5478
5484
|
this.bodyElements.splice(i + 1, 0, blankPara);
|
|
@@ -5509,10 +5515,10 @@ export class Document {
|
|
|
5509
5515
|
|
|
5510
5516
|
// Step 1: Remove relationship entries for headers and footers
|
|
5511
5517
|
const headerRels = this.relationshipManager.getRelationshipsByType(
|
|
5512
|
-
|
|
5518
|
+
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/header'
|
|
5513
5519
|
);
|
|
5514
5520
|
const footerRels = this.relationshipManager.getRelationshipsByType(
|
|
5515
|
-
|
|
5521
|
+
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer'
|
|
5516
5522
|
);
|
|
5517
5523
|
|
|
5518
5524
|
for (const rel of [...headerRels, ...footerRels]) {
|
|
@@ -5723,10 +5729,7 @@ export class Document {
|
|
|
5723
5729
|
* doc.createParagraph('Letter').setNumbering(listId, 1); // a.
|
|
5724
5730
|
* ```
|
|
5725
5731
|
*/
|
|
5726
|
-
createNumberedList(
|
|
5727
|
-
levels = 3,
|
|
5728
|
-
formats?: ("decimal" | "lowerLetter" | "lowerRoman")[]
|
|
5729
|
-
): number {
|
|
5732
|
+
createNumberedList(levels = 3, formats?: ('decimal' | 'lowerLetter' | 'lowerRoman')[]): number {
|
|
5730
5733
|
return this.numberingManager.createNumberedList(levels, formats);
|
|
5731
5734
|
}
|
|
5732
5735
|
|
|
@@ -5738,6 +5741,31 @@ export class Document {
|
|
|
5738
5741
|
return this.numberingManager.createMultiLevelList();
|
|
5739
5742
|
}
|
|
5740
5743
|
|
|
5744
|
+
/**
|
|
5745
|
+
* Creates a new numbering instance that restarts numbering for an existing list
|
|
5746
|
+
*
|
|
5747
|
+
* This creates a new `<w:num>` referencing the same abstract numbering as the
|
|
5748
|
+
* given numId, but with a level override to restart counting at the specified value.
|
|
5749
|
+
*
|
|
5750
|
+
* @param numId The existing numbering instance ID to base the restart on
|
|
5751
|
+
* @param level The level to restart (0-8, default: 0)
|
|
5752
|
+
* @param startValue The value to restart from (>= 1, default: 1)
|
|
5753
|
+
* @returns The new numId to use with paragraph.setNumbering()
|
|
5754
|
+
*
|
|
5755
|
+
* @example
|
|
5756
|
+
* ```typescript
|
|
5757
|
+
* const listId = doc.createNumberedList();
|
|
5758
|
+
* doc.createParagraph('First list item 1').setNumbering(listId, 0);
|
|
5759
|
+
* doc.createParagraph('First list item 2').setNumbering(listId, 0);
|
|
5760
|
+
*
|
|
5761
|
+
* const restartId = doc.restartNumbering(listId);
|
|
5762
|
+
* doc.createParagraph('Second list item 1').setNumbering(restartId, 0);
|
|
5763
|
+
* ```
|
|
5764
|
+
*/
|
|
5765
|
+
restartNumbering(numId: number, level?: number, startValue?: number): number {
|
|
5766
|
+
return this.numberingManager.restartNumbering(numId, level, startValue);
|
|
5767
|
+
}
|
|
5768
|
+
|
|
5741
5769
|
/**
|
|
5742
5770
|
* Gets the framework's standard indentation for a list level
|
|
5743
5771
|
*
|
|
@@ -5783,12 +5811,7 @@ export class Document {
|
|
|
5783
5811
|
leftIndent: number,
|
|
5784
5812
|
hangingIndent?: number
|
|
5785
5813
|
): this {
|
|
5786
|
-
this.numberingManager.setListIndentation(
|
|
5787
|
-
numId,
|
|
5788
|
-
level,
|
|
5789
|
-
leftIndent,
|
|
5790
|
-
hangingIndent
|
|
5791
|
-
);
|
|
5814
|
+
this.numberingManager.setListIndentation(numId, level, leftIndent, hangingIndent);
|
|
5792
5815
|
return this;
|
|
5793
5816
|
}
|
|
5794
5817
|
|
|
@@ -5838,14 +5861,13 @@ export class Document {
|
|
|
5838
5861
|
|
|
5839
5862
|
for (const instance of instances) {
|
|
5840
5863
|
const abstractNumId = instance.getAbstractNumId();
|
|
5841
|
-
const abstractNum =
|
|
5842
|
-
this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
5864
|
+
const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
5843
5865
|
|
|
5844
5866
|
if (!abstractNum) continue;
|
|
5845
5867
|
|
|
5846
5868
|
// Only process bullet lists (skip numbered lists)
|
|
5847
5869
|
const level0 = abstractNum.getLevel(0);
|
|
5848
|
-
if (level0?.getFormat() !==
|
|
5870
|
+
if (level0?.getFormat() !== 'bullet') continue;
|
|
5849
5871
|
|
|
5850
5872
|
// Update all 9 levels (0-8) with standard formatting
|
|
5851
5873
|
for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
|
|
@@ -5853,11 +5875,11 @@ export class Document {
|
|
|
5853
5875
|
if (!numLevel) continue;
|
|
5854
5876
|
|
|
5855
5877
|
// Alternate bullets: even levels = solid (•), odd levels = open (○)
|
|
5856
|
-
const bullet = levelIndex % 2 === 0 ?
|
|
5878
|
+
const bullet = levelIndex % 2 === 0 ? '•' : '○';
|
|
5857
5879
|
numLevel.setText(bullet);
|
|
5858
5880
|
|
|
5859
5881
|
// Set bullet font to Arial (Unicode bullets require a regular font, not Symbol)
|
|
5860
|
-
numLevel.setFont(
|
|
5882
|
+
numLevel.setFont('Arial');
|
|
5861
5883
|
|
|
5862
5884
|
// Set bullet size to 12pt (24 half-points)
|
|
5863
5885
|
numLevel.setFontSize(24);
|
|
@@ -5908,14 +5930,13 @@ export class Document {
|
|
|
5908
5930
|
|
|
5909
5931
|
for (const instance of instances) {
|
|
5910
5932
|
const abstractNumId = instance.getAbstractNumId();
|
|
5911
|
-
const abstractNum =
|
|
5912
|
-
this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
5933
|
+
const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
5913
5934
|
|
|
5914
5935
|
if (!abstractNum) continue;
|
|
5915
5936
|
|
|
5916
5937
|
// Only process numbered lists (skip bullet lists)
|
|
5917
5938
|
const level0 = abstractNum.getLevel(0);
|
|
5918
|
-
if (!level0 || level0.getFormat() ===
|
|
5939
|
+
if (!level0 || level0.getFormat() === 'bullet') continue;
|
|
5919
5940
|
|
|
5920
5941
|
// Update all 9 levels (0-8) with standard formatting
|
|
5921
5942
|
for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
|
|
@@ -5923,7 +5944,7 @@ export class Document {
|
|
|
5923
5944
|
if (!numLevel) continue;
|
|
5924
5945
|
|
|
5925
5946
|
// Set number font to Verdana 12pt
|
|
5926
|
-
numLevel.setFont(
|
|
5947
|
+
numLevel.setFont('Verdana');
|
|
5927
5948
|
numLevel.setFontSize(24); // 12pt = 24 half-points
|
|
5928
5949
|
|
|
5929
5950
|
// Indentation: 0.5" per level (720 twips)
|
|
@@ -5934,7 +5955,7 @@ export class Document {
|
|
|
5934
5955
|
numLevel.setHangingIndent(360);
|
|
5935
5956
|
|
|
5936
5957
|
// Set alignment to left
|
|
5937
|
-
numLevel.setAlignment(
|
|
5958
|
+
numLevel.setAlignment('left');
|
|
5938
5959
|
}
|
|
5939
5960
|
|
|
5940
5961
|
// Apply paragraph formatting to all paragraphs using this list
|
|
@@ -5960,7 +5981,7 @@ export class Document {
|
|
|
5960
5981
|
// Apply font to all runs in the paragraph
|
|
5961
5982
|
const runs = para.getRuns();
|
|
5962
5983
|
for (const run of runs) {
|
|
5963
|
-
run.setFont(
|
|
5984
|
+
run.setFont('Verdana', 12);
|
|
5964
5985
|
}
|
|
5965
5986
|
|
|
5966
5987
|
// Apply paragraph spacing
|
|
@@ -6024,7 +6045,7 @@ export class Document {
|
|
|
6024
6045
|
// Find the paragraph index in bodyElements
|
|
6025
6046
|
const paraIndex = this.bodyElements.indexOf(para);
|
|
6026
6047
|
if (paraIndex === -1) {
|
|
6027
|
-
throw new Error(
|
|
6048
|
+
throw new Error('Paragraph not found in document body elements');
|
|
6028
6049
|
}
|
|
6029
6050
|
|
|
6030
6051
|
// Create 1x1 table
|
|
@@ -6032,7 +6053,7 @@ export class Document {
|
|
|
6032
6053
|
const cell = table.getCell(0, 0);
|
|
6033
6054
|
|
|
6034
6055
|
if (!cell) {
|
|
6035
|
-
throw new Error(
|
|
6056
|
+
throw new Error('Failed to get cell from newly created table');
|
|
6036
6057
|
}
|
|
6037
6058
|
|
|
6038
6059
|
// Move paragraph to cell
|
|
@@ -6064,7 +6085,7 @@ export class Document {
|
|
|
6064
6085
|
// Set table width (percentage of page width)
|
|
6065
6086
|
if (options.tableWidthPercent !== undefined) {
|
|
6066
6087
|
table.setWidth(options.tableWidthPercent);
|
|
6067
|
-
table.setWidthType(
|
|
6088
|
+
table.setWidthType('pct');
|
|
6068
6089
|
}
|
|
6069
6090
|
|
|
6070
6091
|
// Insert table where paragraph was
|
|
@@ -6076,30 +6097,30 @@ export class Document {
|
|
|
6076
6097
|
// Default style configurations for applyStyles()
|
|
6077
6098
|
private static readonly DEFAULT_HEADING1_CONFIG: StyleConfig = {
|
|
6078
6099
|
run: {
|
|
6079
|
-
font:
|
|
6100
|
+
font: 'Verdana',
|
|
6080
6101
|
size: 18,
|
|
6081
6102
|
bold: true,
|
|
6082
|
-
color:
|
|
6103
|
+
color: '000000',
|
|
6083
6104
|
},
|
|
6084
6105
|
paragraph: {
|
|
6085
|
-
alignment:
|
|
6086
|
-
spacing: { before: 0, after: 240, line: 240, lineRule:
|
|
6106
|
+
alignment: 'left',
|
|
6107
|
+
spacing: { before: 0, after: 240, line: 240, lineRule: 'auto' },
|
|
6087
6108
|
},
|
|
6088
6109
|
};
|
|
6089
6110
|
|
|
6090
6111
|
private static readonly DEFAULT_HEADING2_CONFIG: Heading2Config = {
|
|
6091
6112
|
run: {
|
|
6092
|
-
font:
|
|
6113
|
+
font: 'Verdana',
|
|
6093
6114
|
size: 14,
|
|
6094
6115
|
bold: true,
|
|
6095
|
-
color:
|
|
6116
|
+
color: '000000',
|
|
6096
6117
|
},
|
|
6097
6118
|
paragraph: {
|
|
6098
|
-
alignment:
|
|
6099
|
-
spacing: { before: 120, after: 120, line: 240, lineRule:
|
|
6119
|
+
alignment: 'left',
|
|
6120
|
+
spacing: { before: 120, after: 120, line: 240, lineRule: 'auto' },
|
|
6100
6121
|
},
|
|
6101
6122
|
tableOptions: {
|
|
6102
|
-
shading:
|
|
6123
|
+
shading: 'BFBFBF',
|
|
6103
6124
|
marginTop: 0,
|
|
6104
6125
|
marginBottom: 0,
|
|
6105
6126
|
marginLeft: 115,
|
|
@@ -6110,38 +6131,38 @@ export class Document {
|
|
|
6110
6131
|
|
|
6111
6132
|
private static readonly DEFAULT_HEADING3_CONFIG: StyleConfig = {
|
|
6112
6133
|
run: {
|
|
6113
|
-
font:
|
|
6134
|
+
font: 'Verdana',
|
|
6114
6135
|
size: 12,
|
|
6115
6136
|
bold: true,
|
|
6116
|
-
color:
|
|
6137
|
+
color: '000000',
|
|
6117
6138
|
},
|
|
6118
6139
|
paragraph: {
|
|
6119
|
-
alignment:
|
|
6120
|
-
spacing: { before: 60, after: 60, line: 240, lineRule:
|
|
6140
|
+
alignment: 'left',
|
|
6141
|
+
spacing: { before: 60, after: 60, line: 240, lineRule: 'auto' },
|
|
6121
6142
|
},
|
|
6122
6143
|
};
|
|
6123
6144
|
|
|
6124
6145
|
private static readonly DEFAULT_NORMAL_CONFIG: StyleConfig = {
|
|
6125
6146
|
run: {
|
|
6126
|
-
font:
|
|
6147
|
+
font: 'Verdana',
|
|
6127
6148
|
size: 12,
|
|
6128
|
-
color:
|
|
6149
|
+
color: '000000',
|
|
6129
6150
|
},
|
|
6130
6151
|
paragraph: {
|
|
6131
|
-
alignment:
|
|
6132
|
-
spacing: { before: 60, after: 60, line: 240, lineRule:
|
|
6152
|
+
alignment: 'left',
|
|
6153
|
+
spacing: { before: 60, after: 60, line: 240, lineRule: 'auto' },
|
|
6133
6154
|
},
|
|
6134
6155
|
};
|
|
6135
6156
|
|
|
6136
6157
|
private static readonly DEFAULT_LIST_PARAGRAPH_CONFIG: StyleConfig = {
|
|
6137
6158
|
run: {
|
|
6138
|
-
font:
|
|
6159
|
+
font: 'Verdana',
|
|
6139
6160
|
size: 12,
|
|
6140
|
-
color:
|
|
6161
|
+
color: '000000',
|
|
6141
6162
|
},
|
|
6142
6163
|
paragraph: {
|
|
6143
|
-
alignment:
|
|
6144
|
-
spacing: { before: 0, after: 60, line: 240, lineRule:
|
|
6164
|
+
alignment: 'left',
|
|
6165
|
+
spacing: { before: 0, after: 60, line: 240, lineRule: 'auto' },
|
|
6145
6166
|
indentation: { left: 360, hanging: 360 },
|
|
6146
6167
|
contextualSpacing: true,
|
|
6147
6168
|
},
|
|
@@ -6260,35 +6281,31 @@ export class Document {
|
|
|
6260
6281
|
};
|
|
6261
6282
|
|
|
6262
6283
|
// Get existing styles from StylesManager
|
|
6263
|
-
const heading1 = this.stylesManager.getStyle(
|
|
6264
|
-
const heading2 = this.stylesManager.getStyle(
|
|
6265
|
-
const heading3 = this.stylesManager.getStyle(
|
|
6266
|
-
const normal = this.stylesManager.getStyle(
|
|
6267
|
-
const listParagraph = this.stylesManager.getStyle(
|
|
6284
|
+
const heading1 = this.stylesManager.getStyle('Heading1');
|
|
6285
|
+
const heading2 = this.stylesManager.getStyle('Heading2');
|
|
6286
|
+
const heading3 = this.stylesManager.getStyle('Heading3');
|
|
6287
|
+
const normal = this.stylesManager.getStyle('Normal');
|
|
6288
|
+
const listParagraph = this.stylesManager.getStyle('ListParagraph');
|
|
6268
6289
|
|
|
6269
6290
|
// Merge provided options with ACTUAL current style values (not hardcoded defaults)
|
|
6270
6291
|
// This allows users to only specify properties they want to change
|
|
6271
6292
|
const h1Config = {
|
|
6272
6293
|
run: {
|
|
6273
|
-
...(heading1?.getRunFormatting() ||
|
|
6274
|
-
Document.DEFAULT_HEADING1_CONFIG.run),
|
|
6294
|
+
...(heading1?.getRunFormatting() || Document.DEFAULT_HEADING1_CONFIG.run),
|
|
6275
6295
|
...options?.heading1?.run,
|
|
6276
6296
|
},
|
|
6277
6297
|
paragraph: {
|
|
6278
|
-
...(heading1?.getParagraphFormatting() ||
|
|
6279
|
-
Document.DEFAULT_HEADING1_CONFIG.paragraph),
|
|
6298
|
+
...(heading1?.getParagraphFormatting() || Document.DEFAULT_HEADING1_CONFIG.paragraph),
|
|
6280
6299
|
...options?.heading1?.paragraph,
|
|
6281
6300
|
},
|
|
6282
6301
|
};
|
|
6283
6302
|
const h2Config = {
|
|
6284
6303
|
run: {
|
|
6285
|
-
...(heading2?.getRunFormatting() ||
|
|
6286
|
-
Document.DEFAULT_HEADING2_CONFIG.run),
|
|
6304
|
+
...(heading2?.getRunFormatting() || Document.DEFAULT_HEADING2_CONFIG.run),
|
|
6287
6305
|
...options?.heading2?.run,
|
|
6288
6306
|
},
|
|
6289
6307
|
paragraph: {
|
|
6290
|
-
...(heading2?.getParagraphFormatting() ||
|
|
6291
|
-
Document.DEFAULT_HEADING2_CONFIG.paragraph),
|
|
6308
|
+
...(heading2?.getParagraphFormatting() || Document.DEFAULT_HEADING2_CONFIG.paragraph),
|
|
6292
6309
|
...options?.heading2?.paragraph,
|
|
6293
6310
|
},
|
|
6294
6311
|
tableOptions: {
|
|
@@ -6298,13 +6315,11 @@ export class Document {
|
|
|
6298
6315
|
};
|
|
6299
6316
|
const h3Config = {
|
|
6300
6317
|
run: {
|
|
6301
|
-
...(heading3?.getRunFormatting() ||
|
|
6302
|
-
Document.DEFAULT_HEADING3_CONFIG.run),
|
|
6318
|
+
...(heading3?.getRunFormatting() || Document.DEFAULT_HEADING3_CONFIG.run),
|
|
6303
6319
|
...options?.heading3?.run,
|
|
6304
6320
|
},
|
|
6305
6321
|
paragraph: {
|
|
6306
|
-
...(heading3?.getParagraphFormatting() ||
|
|
6307
|
-
Document.DEFAULT_HEADING3_CONFIG.paragraph),
|
|
6322
|
+
...(heading3?.getParagraphFormatting() || Document.DEFAULT_HEADING3_CONFIG.paragraph),
|
|
6308
6323
|
...options?.heading3?.paragraph,
|
|
6309
6324
|
},
|
|
6310
6325
|
};
|
|
@@ -6314,15 +6329,13 @@ export class Document {
|
|
|
6314
6329
|
...options?.normal?.run,
|
|
6315
6330
|
},
|
|
6316
6331
|
paragraph: {
|
|
6317
|
-
...(normal?.getParagraphFormatting() ||
|
|
6318
|
-
Document.DEFAULT_NORMAL_CONFIG.paragraph),
|
|
6332
|
+
...(normal?.getParagraphFormatting() || Document.DEFAULT_NORMAL_CONFIG.paragraph),
|
|
6319
6333
|
...options?.normal?.paragraph,
|
|
6320
6334
|
},
|
|
6321
6335
|
};
|
|
6322
6336
|
const listParaConfig = {
|
|
6323
6337
|
run: {
|
|
6324
|
-
...(listParagraph?.getRunFormatting() ||
|
|
6325
|
-
Document.DEFAULT_LIST_PARAGRAPH_CONFIG.run),
|
|
6338
|
+
...(listParagraph?.getRunFormatting() || Document.DEFAULT_LIST_PARAGRAPH_CONFIG.run),
|
|
6326
6339
|
...options?.listParagraph?.run,
|
|
6327
6340
|
},
|
|
6328
6341
|
paragraph: {
|
|
@@ -6333,14 +6346,12 @@ export class Document {
|
|
|
6333
6346
|
};
|
|
6334
6347
|
|
|
6335
6348
|
// Extract preserve blank lines option (defaults to true)
|
|
6336
|
-
const preserveBlankLines =
|
|
6337
|
-
options?.preserveBlankLinesAfterHeading2Tables ?? true;
|
|
6349
|
+
const preserveBlankLines = options?.preserveBlankLinesAfterHeading2Tables ?? true;
|
|
6338
6350
|
|
|
6339
6351
|
// Modify Heading1 definition
|
|
6340
6352
|
if (heading1 && h1Config.run && h1Config.paragraph) {
|
|
6341
6353
|
if (h1Config.run) heading1.setRunFormatting(h1Config.run);
|
|
6342
|
-
if (h1Config.paragraph)
|
|
6343
|
-
heading1.setParagraphFormatting(h1Config.paragraph);
|
|
6354
|
+
if (h1Config.paragraph) heading1.setParagraphFormatting(h1Config.paragraph);
|
|
6344
6355
|
// Mark style as modified so it gets included in mergeStylesWithOriginal()
|
|
6345
6356
|
this.addStyle(heading1);
|
|
6346
6357
|
results.heading1 = true;
|
|
@@ -6349,8 +6360,7 @@ export class Document {
|
|
|
6349
6360
|
// Modify Heading2 definition
|
|
6350
6361
|
if (heading2 && h2Config.run && h2Config.paragraph) {
|
|
6351
6362
|
if (h2Config.run) heading2.setRunFormatting(h2Config.run);
|
|
6352
|
-
if (h2Config.paragraph)
|
|
6353
|
-
heading2.setParagraphFormatting(h2Config.paragraph);
|
|
6363
|
+
if (h2Config.paragraph) heading2.setParagraphFormatting(h2Config.paragraph);
|
|
6354
6364
|
// Mark style as modified so it gets included in mergeStylesWithOriginal()
|
|
6355
6365
|
this.addStyle(heading2);
|
|
6356
6366
|
results.heading2 = true;
|
|
@@ -6359,8 +6369,7 @@ export class Document {
|
|
|
6359
6369
|
// Modify Heading3 definition
|
|
6360
6370
|
if (heading3 && h3Config.run && h3Config.paragraph) {
|
|
6361
6371
|
if (h3Config.run) heading3.setRunFormatting(h3Config.run);
|
|
6362
|
-
if (h3Config.paragraph)
|
|
6363
|
-
heading3.setParagraphFormatting(h3Config.paragraph);
|
|
6372
|
+
if (h3Config.paragraph) heading3.setParagraphFormatting(h3Config.paragraph);
|
|
6364
6373
|
// Mark style as modified so it gets included in mergeStylesWithOriginal()
|
|
6365
6374
|
this.addStyle(heading3);
|
|
6366
6375
|
results.heading3 = true;
|
|
@@ -6369,8 +6378,7 @@ export class Document {
|
|
|
6369
6378
|
// Modify Normal definition
|
|
6370
6379
|
if (normal && normalConfig.run && normalConfig.paragraph) {
|
|
6371
6380
|
if (normalConfig.run) normal.setRunFormatting(normalConfig.run);
|
|
6372
|
-
if (normalConfig.paragraph)
|
|
6373
|
-
normal.setParagraphFormatting(normalConfig.paragraph);
|
|
6381
|
+
if (normalConfig.paragraph) normal.setParagraphFormatting(normalConfig.paragraph);
|
|
6374
6382
|
// Mark style as modified so it gets included in mergeStylesWithOriginal()
|
|
6375
6383
|
this.addStyle(normal);
|
|
6376
6384
|
|
|
@@ -6378,12 +6386,11 @@ export class Document {
|
|
|
6378
6386
|
// Default is true - changes to Normal automatically apply to NormalWeb
|
|
6379
6387
|
const shouldLinkNormalWeb = options?.linkNormalWebToNormal !== false;
|
|
6380
6388
|
if (shouldLinkNormalWeb) {
|
|
6381
|
-
const normalWeb = this.stylesManager.getStyle(
|
|
6389
|
+
const normalWeb = this.stylesManager.getStyle('NormalWeb');
|
|
6382
6390
|
if (normalWeb) {
|
|
6383
6391
|
// Apply same formatting to NormalWeb
|
|
6384
6392
|
if (normalConfig.run) normalWeb.setRunFormatting(normalConfig.run);
|
|
6385
|
-
if (normalConfig.paragraph)
|
|
6386
|
-
normalWeb.setParagraphFormatting(normalConfig.paragraph);
|
|
6393
|
+
if (normalConfig.paragraph) normalWeb.setParagraphFormatting(normalConfig.paragraph);
|
|
6387
6394
|
// Mark as modified for selective merging during save
|
|
6388
6395
|
this.addStyle(normalWeb);
|
|
6389
6396
|
}
|
|
@@ -6394,8 +6401,7 @@ export class Document {
|
|
|
6394
6401
|
|
|
6395
6402
|
// Modify List Paragraph definition
|
|
6396
6403
|
if (listParagraph && listParaConfig.run && listParaConfig.paragraph) {
|
|
6397
|
-
if (listParaConfig.run)
|
|
6398
|
-
listParagraph.setRunFormatting(listParaConfig.run);
|
|
6404
|
+
if (listParaConfig.run) listParagraph.setRunFormatting(listParaConfig.run);
|
|
6399
6405
|
if (listParaConfig.paragraph) {
|
|
6400
6406
|
// Validate indentation: hanging must not exceed left to prevent negative bullet position
|
|
6401
6407
|
const indent = listParaConfig.paragraph.indentation;
|
|
@@ -6404,7 +6410,7 @@ export class Document {
|
|
|
6404
6410
|
const logger = getGlobalLogger();
|
|
6405
6411
|
logger.warn(
|
|
6406
6412
|
`[Document] ListParagraph indentation: hanging (${indent.hanging}) > left (${indent.left}). ` +
|
|
6407
|
-
|
|
6413
|
+
`Capping hanging to left to prevent negative bullet position.`
|
|
6408
6414
|
);
|
|
6409
6415
|
indent.hanging = indent.left;
|
|
6410
6416
|
}
|
|
@@ -6459,12 +6465,12 @@ export class Document {
|
|
|
6459
6465
|
const styleId = para.getStyle();
|
|
6460
6466
|
|
|
6461
6467
|
// Process Heading1 paragraphs
|
|
6462
|
-
if (styleId ===
|
|
6468
|
+
if (styleId === 'Heading1' && heading1) {
|
|
6463
6469
|
// Save white font status BEFORE clearing (clearDirectFormattingConflicts clears color)
|
|
6464
6470
|
const allRuns = this.getAllRunsFromParagraph(para);
|
|
6465
6471
|
const whiteFontRuns = new Set(
|
|
6466
6472
|
options?.preserveWhiteFont
|
|
6467
|
-
? allRuns.filter(run => run.getColor()?.toUpperCase() === 'FFFFFF')
|
|
6473
|
+
? allRuns.filter((run) => run.getColor()?.toUpperCase() === 'FFFFFF')
|
|
6468
6474
|
: []
|
|
6469
6475
|
);
|
|
6470
6476
|
|
|
@@ -6483,7 +6489,7 @@ export class Document {
|
|
|
6483
6489
|
run.setItalic(h1Config.run?.italic ?? false);
|
|
6484
6490
|
}
|
|
6485
6491
|
if (!h1Preserve.underline) {
|
|
6486
|
-
run.setUnderline(h1Config.run?.underline ?
|
|
6492
|
+
run.setUnderline(h1Config.run?.underline ? 'single' : false);
|
|
6487
6493
|
}
|
|
6488
6494
|
// Apply font, color, and size - skip color if run was white font
|
|
6489
6495
|
if (h1Config.run?.font) {
|
|
@@ -6505,25 +6511,13 @@ export class Document {
|
|
|
6505
6511
|
// Update paragraph mark properties to match configuration
|
|
6506
6512
|
if (para.formatting.paragraphMarkRunProperties) {
|
|
6507
6513
|
const markProps = para.formatting.paragraphMarkRunProperties;
|
|
6508
|
-
if (
|
|
6509
|
-
!h1Preserve.bold &&
|
|
6510
|
-
h1Config.run?.bold === false &&
|
|
6511
|
-
markProps.bold
|
|
6512
|
-
) {
|
|
6514
|
+
if (!h1Preserve.bold && h1Config.run?.bold === false && markProps.bold) {
|
|
6513
6515
|
delete markProps.bold;
|
|
6514
6516
|
}
|
|
6515
|
-
if (
|
|
6516
|
-
!h1Preserve.italic &&
|
|
6517
|
-
h1Config.run?.italic === false &&
|
|
6518
|
-
markProps.italic
|
|
6519
|
-
) {
|
|
6517
|
+
if (!h1Preserve.italic && h1Config.run?.italic === false && markProps.italic) {
|
|
6520
6518
|
delete markProps.italic;
|
|
6521
6519
|
}
|
|
6522
|
-
if (
|
|
6523
|
-
!h1Preserve.underline &&
|
|
6524
|
-
h1Config.run?.underline === false &&
|
|
6525
|
-
markProps.underline
|
|
6526
|
-
) {
|
|
6520
|
+
if (!h1Preserve.underline && h1Config.run?.underline === false && markProps.underline) {
|
|
6527
6521
|
delete markProps.underline;
|
|
6528
6522
|
}
|
|
6529
6523
|
// Update paragraph mark font, color, size
|
|
@@ -6542,10 +6536,11 @@ export class Document {
|
|
|
6542
6536
|
}
|
|
6543
6537
|
|
|
6544
6538
|
// Process Heading2 paragraphs
|
|
6545
|
-
else if (styleId ===
|
|
6539
|
+
else if (styleId === 'Heading2' && heading2) {
|
|
6546
6540
|
// Check if paragraph has actual text content (skip empty paragraphs)
|
|
6547
|
-
const hasContent = this.getAllRunsFromParagraph(para)
|
|
6548
|
-
|
|
6541
|
+
const hasContent = this.getAllRunsFromParagraph(para).some(
|
|
6542
|
+
(run) => run.getText().trim().length > 0
|
|
6543
|
+
);
|
|
6549
6544
|
|
|
6550
6545
|
if (!hasContent) {
|
|
6551
6546
|
// Skip empty Heading2 paragraphs - don't wrap them in tables
|
|
@@ -6578,8 +6573,7 @@ export class Document {
|
|
|
6578
6573
|
const isMultiCellTable = !(rowCount === 1 && colCount === 1);
|
|
6579
6574
|
const cellFormatting = cell.getFormatting();
|
|
6580
6575
|
const cellHasShading = !!(
|
|
6581
|
-
cellFormatting?.shading?.fill ||
|
|
6582
|
-
cellFormatting?.shading?.pattern
|
|
6576
|
+
cellFormatting?.shading?.fill || cellFormatting?.shading?.pattern
|
|
6583
6577
|
);
|
|
6584
6578
|
|
|
6585
6579
|
if (isMultiCellTable && cellHasShading && para.formatting.alignment) {
|
|
@@ -6592,7 +6586,7 @@ export class Document {
|
|
|
6592
6586
|
const allRuns = this.getAllRunsFromParagraph(para);
|
|
6593
6587
|
const whiteFontRuns = new Set(
|
|
6594
6588
|
options?.preserveWhiteFont
|
|
6595
|
-
? allRuns.filter(run => run.getColor()?.toUpperCase() === 'FFFFFF')
|
|
6589
|
+
? allRuns.filter((run) => run.getColor()?.toUpperCase() === 'FFFFFF')
|
|
6596
6590
|
: []
|
|
6597
6591
|
);
|
|
6598
6592
|
|
|
@@ -6617,7 +6611,7 @@ export class Document {
|
|
|
6617
6611
|
run.setItalic(h2Config.run?.italic ?? false);
|
|
6618
6612
|
}
|
|
6619
6613
|
if (!h2Preserve.underline) {
|
|
6620
|
-
run.setUnderline(h2Config.run?.underline ?
|
|
6614
|
+
run.setUnderline(h2Config.run?.underline ? 'single' : false);
|
|
6621
6615
|
}
|
|
6622
6616
|
// Apply font, color, and size - skip color if run was white font
|
|
6623
6617
|
if (h2Config.run?.font) {
|
|
@@ -6639,25 +6633,13 @@ export class Document {
|
|
|
6639
6633
|
// Update paragraph mark properties to match configuration
|
|
6640
6634
|
if (para.formatting.paragraphMarkRunProperties) {
|
|
6641
6635
|
const markProps = para.formatting.paragraphMarkRunProperties;
|
|
6642
|
-
if (
|
|
6643
|
-
!h2Preserve.bold &&
|
|
6644
|
-
h2Config.run?.bold === false &&
|
|
6645
|
-
markProps.bold
|
|
6646
|
-
) {
|
|
6636
|
+
if (!h2Preserve.bold && h2Config.run?.bold === false && markProps.bold) {
|
|
6647
6637
|
delete markProps.bold;
|
|
6648
6638
|
}
|
|
6649
|
-
if (
|
|
6650
|
-
!h2Preserve.italic &&
|
|
6651
|
-
h2Config.run?.italic === false &&
|
|
6652
|
-
markProps.italic
|
|
6653
|
-
) {
|
|
6639
|
+
if (!h2Preserve.italic && h2Config.run?.italic === false && markProps.italic) {
|
|
6654
6640
|
delete markProps.italic;
|
|
6655
6641
|
}
|
|
6656
|
-
if (
|
|
6657
|
-
!h2Preserve.underline &&
|
|
6658
|
-
h2Config.run?.underline === false &&
|
|
6659
|
-
markProps.underline
|
|
6660
|
-
) {
|
|
6642
|
+
if (!h2Preserve.underline && h2Config.run?.underline === false && markProps.underline) {
|
|
6661
6643
|
delete markProps.underline;
|
|
6662
6644
|
}
|
|
6663
6645
|
// Update paragraph mark font, color, size
|
|
@@ -6703,13 +6685,13 @@ export class Document {
|
|
|
6703
6685
|
});
|
|
6704
6686
|
if (table) {
|
|
6705
6687
|
table.setWidth(h2Config.tableOptions.tableWidthPercent);
|
|
6706
|
-
table.setWidthType(
|
|
6688
|
+
table.setWidthType('pct');
|
|
6707
6689
|
}
|
|
6708
6690
|
}
|
|
6709
6691
|
} else {
|
|
6710
6692
|
// Paragraph is not in a table - wrap it using config
|
|
6711
6693
|
const table = this.wrapParagraphInTable(para, {
|
|
6712
|
-
shading: h2Config.tableOptions?.shading ??
|
|
6694
|
+
shading: h2Config.tableOptions?.shading ?? 'BFBFBF',
|
|
6713
6695
|
marginTop: h2Config.tableOptions?.marginTop ?? 0,
|
|
6714
6696
|
marginBottom: h2Config.tableOptions?.marginBottom ?? 0,
|
|
6715
6697
|
marginLeft: h2Config.tableOptions?.marginLeft ?? 115,
|
|
@@ -6740,7 +6722,7 @@ export class Document {
|
|
|
6740
6722
|
// Runs with text count as content
|
|
6741
6723
|
if ((item as any).getText) {
|
|
6742
6724
|
const text = (item as any).getText().trim();
|
|
6743
|
-
if (text !==
|
|
6725
|
+
if (text !== '') return false;
|
|
6744
6726
|
}
|
|
6745
6727
|
}
|
|
6746
6728
|
|
|
@@ -6765,12 +6747,12 @@ export class Document {
|
|
|
6765
6747
|
}
|
|
6766
6748
|
|
|
6767
6749
|
// Process Heading3 paragraphs
|
|
6768
|
-
else if (styleId ===
|
|
6750
|
+
else if (styleId === 'Heading3' && heading3) {
|
|
6769
6751
|
// Save white font status BEFORE clearing (clearDirectFormattingConflicts clears color)
|
|
6770
6752
|
const allRuns = this.getAllRunsFromParagraph(para);
|
|
6771
6753
|
const whiteFontRuns = new Set(
|
|
6772
6754
|
options?.preserveWhiteFont
|
|
6773
|
-
? allRuns.filter(run => run.getColor()?.toUpperCase() === 'FFFFFF')
|
|
6755
|
+
? allRuns.filter((run) => run.getColor()?.toUpperCase() === 'FFFFFF')
|
|
6774
6756
|
: []
|
|
6775
6757
|
);
|
|
6776
6758
|
|
|
@@ -6789,7 +6771,7 @@ export class Document {
|
|
|
6789
6771
|
run.setItalic(h3Config.run?.italic ?? false);
|
|
6790
6772
|
}
|
|
6791
6773
|
if (!h3Preserve.underline) {
|
|
6792
|
-
run.setUnderline(h3Config.run?.underline ?
|
|
6774
|
+
run.setUnderline(h3Config.run?.underline ? 'single' : false);
|
|
6793
6775
|
}
|
|
6794
6776
|
// Apply font, color, and size - skip color if run was white font
|
|
6795
6777
|
if (h3Config.run?.font) {
|
|
@@ -6811,25 +6793,13 @@ export class Document {
|
|
|
6811
6793
|
// Update paragraph mark properties to match configuration
|
|
6812
6794
|
if (para.formatting.paragraphMarkRunProperties) {
|
|
6813
6795
|
const markProps = para.formatting.paragraphMarkRunProperties;
|
|
6814
|
-
if (
|
|
6815
|
-
!h3Preserve.bold &&
|
|
6816
|
-
h3Config.run?.bold === false &&
|
|
6817
|
-
markProps.bold
|
|
6818
|
-
) {
|
|
6796
|
+
if (!h3Preserve.bold && h3Config.run?.bold === false && markProps.bold) {
|
|
6819
6797
|
delete markProps.bold;
|
|
6820
6798
|
}
|
|
6821
|
-
if (
|
|
6822
|
-
!h3Preserve.italic &&
|
|
6823
|
-
h3Config.run?.italic === false &&
|
|
6824
|
-
markProps.italic
|
|
6825
|
-
) {
|
|
6799
|
+
if (!h3Preserve.italic && h3Config.run?.italic === false && markProps.italic) {
|
|
6826
6800
|
delete markProps.italic;
|
|
6827
6801
|
}
|
|
6828
|
-
if (
|
|
6829
|
-
!h3Preserve.underline &&
|
|
6830
|
-
h3Config.run?.underline === false &&
|
|
6831
|
-
markProps.underline
|
|
6832
|
-
) {
|
|
6802
|
+
if (!h3Preserve.underline && h3Config.run?.underline === false && markProps.underline) {
|
|
6833
6803
|
delete markProps.underline;
|
|
6834
6804
|
}
|
|
6835
6805
|
// Update paragraph mark font, color, size
|
|
@@ -6848,7 +6818,7 @@ export class Document {
|
|
|
6848
6818
|
}
|
|
6849
6819
|
|
|
6850
6820
|
// Process List Paragraph paragraphs
|
|
6851
|
-
else if (styleId ===
|
|
6821
|
+
else if (styleId === 'ListParagraph' && listParagraph) {
|
|
6852
6822
|
// Check for mis-styled paragraphs: ListParagraph + left:0 + no numbering
|
|
6853
6823
|
// These are not actual list items - change them to Normal style
|
|
6854
6824
|
const paraIndentation = para.getFormatting().indentation;
|
|
@@ -6856,7 +6826,7 @@ export class Document {
|
|
|
6856
6826
|
if (paraIndentation?.left === 0 && !hasNumbering) {
|
|
6857
6827
|
// This paragraph has ListParagraph style but explicitly overrides indent to 0
|
|
6858
6828
|
// and has no numbering - it should be Normal style, not ListParagraph
|
|
6859
|
-
para.setStyle(
|
|
6829
|
+
para.setStyle('Normal');
|
|
6860
6830
|
|
|
6861
6831
|
// Preserve existing bold formatting and white font before applying Normal style
|
|
6862
6832
|
const allRuns = this.getAllRunsFromParagraph(para);
|
|
@@ -6942,7 +6912,7 @@ export class Document {
|
|
|
6942
6912
|
run.setItalic(listParaConfig.run?.italic ?? false);
|
|
6943
6913
|
}
|
|
6944
6914
|
if (!listParaPreserve.underline) {
|
|
6945
|
-
run.setUnderline(listParaConfig.run?.underline ?
|
|
6915
|
+
run.setUnderline(listParaConfig.run?.underline ? 'single' : false);
|
|
6946
6916
|
}
|
|
6947
6917
|
// Apply font, color, and size - skip color if run was white font
|
|
6948
6918
|
if (listParaConfig.run?.font) {
|
|
@@ -6964,11 +6934,7 @@ export class Document {
|
|
|
6964
6934
|
// Update paragraph mark properties to match configuration
|
|
6965
6935
|
if (para.formatting.paragraphMarkRunProperties) {
|
|
6966
6936
|
const markProps = para.formatting.paragraphMarkRunProperties;
|
|
6967
|
-
if (
|
|
6968
|
-
!listParaPreserve.bold &&
|
|
6969
|
-
listParaConfig.run?.bold === false &&
|
|
6970
|
-
markProps.bold
|
|
6971
|
-
) {
|
|
6937
|
+
if (!listParaPreserve.bold && listParaConfig.run?.bold === false && markProps.bold) {
|
|
6972
6938
|
delete markProps.bold;
|
|
6973
6939
|
}
|
|
6974
6940
|
if (
|
|
@@ -7002,7 +6968,12 @@ export class Document {
|
|
|
7002
6968
|
|
|
7003
6969
|
// Process Normal paragraphs (including undefined style which defaults to Normal)
|
|
7004
6970
|
// Also process NormalWeb paragraphs when linkNormalWebToNormal is enabled (default: true)
|
|
7005
|
-
else if (
|
|
6971
|
+
else if (
|
|
6972
|
+
(styleId === 'Normal' ||
|
|
6973
|
+
styleId === undefined ||
|
|
6974
|
+
(styleId === 'NormalWeb' && options?.linkNormalWebToNormal !== false)) &&
|
|
6975
|
+
normal
|
|
6976
|
+
) {
|
|
7006
6977
|
// Save formatting that should be preserved BEFORE clearing
|
|
7007
6978
|
const allRuns = this.getAllRunsFromParagraph(para);
|
|
7008
6979
|
const preservedFormatting = allRuns.map((run) => {
|
|
@@ -7017,8 +6988,8 @@ export class Document {
|
|
|
7017
6988
|
});
|
|
7018
6989
|
|
|
7019
6990
|
// Save center alignment BEFORE clearing if preserveCenterAlignment is set
|
|
7020
|
-
const savedCenterAlignment =
|
|
7021
|
-
para.getAlignment() === 'center';
|
|
6991
|
+
const savedCenterAlignment =
|
|
6992
|
+
options?.normal?.preserveCenterAlignment && para.getAlignment() === 'center';
|
|
7022
6993
|
|
|
7023
6994
|
para.clearDirectFormattingConflicts(normal);
|
|
7024
6995
|
|
|
@@ -7054,7 +7025,7 @@ export class Document {
|
|
|
7054
7025
|
run.setItalic(normalConfig.run?.italic ?? false);
|
|
7055
7026
|
}
|
|
7056
7027
|
if (!normalPreserve.underline) {
|
|
7057
|
-
run.setUnderline(normalConfig.run?.underline ?
|
|
7028
|
+
run.setUnderline(normalConfig.run?.underline ? 'single' : false);
|
|
7058
7029
|
}
|
|
7059
7030
|
// Apply font, color, and size - skip color if run was white font
|
|
7060
7031
|
if (normalConfig.run?.font) {
|
|
@@ -7077,18 +7048,10 @@ export class Document {
|
|
|
7077
7048
|
// Update paragraph mark properties to match configuration
|
|
7078
7049
|
if (para.formatting.paragraphMarkRunProperties) {
|
|
7079
7050
|
const markProps = para.formatting.paragraphMarkRunProperties;
|
|
7080
|
-
if (
|
|
7081
|
-
!normalPreserve.bold &&
|
|
7082
|
-
normalConfig.run?.bold === false &&
|
|
7083
|
-
markProps.bold
|
|
7084
|
-
) {
|
|
7051
|
+
if (!normalPreserve.bold && normalConfig.run?.bold === false && markProps.bold) {
|
|
7085
7052
|
delete markProps.bold;
|
|
7086
7053
|
}
|
|
7087
|
-
if (
|
|
7088
|
-
!normalPreserve.italic &&
|
|
7089
|
-
normalConfig.run?.italic === false &&
|
|
7090
|
-
markProps.italic
|
|
7091
|
-
) {
|
|
7054
|
+
if (!normalPreserve.italic && normalConfig.run?.italic === false && markProps.italic) {
|
|
7092
7055
|
delete markProps.italic;
|
|
7093
7056
|
}
|
|
7094
7057
|
if (
|
|
@@ -7174,8 +7137,9 @@ export class Document {
|
|
|
7174
7137
|
indentPerLevel?: number;
|
|
7175
7138
|
}): { formatted: number[] } {
|
|
7176
7139
|
// Filter valid levels, deduplicate, and sort
|
|
7177
|
-
const validLevels = (options.levels ?? [1, 2, 3, 4, 5, 6, 7, 8, 9])
|
|
7178
|
-
|
|
7140
|
+
const validLevels = (options.levels ?? [1, 2, 3, 4, 5, 6, 7, 8, 9]).filter(
|
|
7141
|
+
(l) => l >= 1 && l <= 9
|
|
7142
|
+
);
|
|
7179
7143
|
const levels = [...new Set(validLevels)].sort((a, b) => a - b);
|
|
7180
7144
|
const formatted: number[] = [];
|
|
7181
7145
|
|
|
@@ -7277,14 +7241,14 @@ export class Document {
|
|
|
7277
7241
|
const styleId = style.getStyleId();
|
|
7278
7242
|
|
|
7279
7243
|
switch (styleId) {
|
|
7280
|
-
case
|
|
7244
|
+
case 'Heading1':
|
|
7281
7245
|
options.heading1 = {
|
|
7282
7246
|
run: style.getRunFormatting() as any,
|
|
7283
7247
|
paragraph: style.getParagraphFormatting() as any,
|
|
7284
7248
|
};
|
|
7285
7249
|
break;
|
|
7286
7250
|
|
|
7287
|
-
case
|
|
7251
|
+
case 'Heading2':
|
|
7288
7252
|
options.heading2 = {
|
|
7289
7253
|
run: style.getRunFormatting() as any,
|
|
7290
7254
|
paragraph: style.getParagraphFormatting() as any,
|
|
@@ -7292,21 +7256,21 @@ export class Document {
|
|
|
7292
7256
|
};
|
|
7293
7257
|
break;
|
|
7294
7258
|
|
|
7295
|
-
case
|
|
7259
|
+
case 'Heading3':
|
|
7296
7260
|
options.heading3 = {
|
|
7297
7261
|
run: style.getRunFormatting() as any,
|
|
7298
7262
|
paragraph: style.getParagraphFormatting() as any,
|
|
7299
7263
|
};
|
|
7300
7264
|
break;
|
|
7301
7265
|
|
|
7302
|
-
case
|
|
7266
|
+
case 'Normal':
|
|
7303
7267
|
options.normal = {
|
|
7304
7268
|
run: style.getRunFormatting() as any,
|
|
7305
7269
|
paragraph: style.getParagraphFormatting() as any,
|
|
7306
7270
|
};
|
|
7307
7271
|
break;
|
|
7308
7272
|
|
|
7309
|
-
case
|
|
7273
|
+
case 'ListParagraph':
|
|
7310
7274
|
options.listParagraph = {
|
|
7311
7275
|
run: style.getRunFormatting() as any,
|
|
7312
7276
|
paragraph: style.getParagraphFormatting() as any,
|
|
@@ -7346,8 +7310,8 @@ export class Document {
|
|
|
7346
7310
|
*/
|
|
7347
7311
|
public updateTableStyleShading(oldColor: string, newColor: string): number {
|
|
7348
7312
|
// Normalize colors (uppercase, no #)
|
|
7349
|
-
const normalizedOld = oldColor.replace(
|
|
7350
|
-
const normalizedNew = newColor.replace(
|
|
7313
|
+
const normalizedOld = oldColor.replace('#', '').toUpperCase();
|
|
7314
|
+
const normalizedNew = newColor.replace('#', '').toUpperCase();
|
|
7351
7315
|
|
|
7352
7316
|
if (normalizedOld === normalizedNew) {
|
|
7353
7317
|
return 0; // No change needed
|
|
@@ -7363,10 +7327,7 @@ export class Document {
|
|
|
7363
7327
|
|
|
7364
7328
|
// Pattern to match shading elements with fill attribute containing the old color
|
|
7365
7329
|
// Matches: w:fill="A5A5A5" (case-insensitive)
|
|
7366
|
-
const fillPattern = new RegExp(
|
|
7367
|
-
`(w:fill=["'])${normalizedOld}(["'])`,
|
|
7368
|
-
"gi"
|
|
7369
|
-
);
|
|
7330
|
+
const fillPattern = new RegExp(`(w:fill=["'])${normalizedOld}(["'])`, 'gi');
|
|
7370
7331
|
|
|
7371
7332
|
// Replace all occurrences
|
|
7372
7333
|
stylesXml = stylesXml.replace(fillPattern, (match, prefix, suffix) => {
|
|
@@ -7376,10 +7337,7 @@ export class Document {
|
|
|
7376
7337
|
|
|
7377
7338
|
// Also handle color attribute in shading elements
|
|
7378
7339
|
// Matches: w:color="A5A5A5" within shd elements
|
|
7379
|
-
const colorPattern = new RegExp(
|
|
7380
|
-
`(<w:shd[^>]*w:color=["'])${normalizedOld}(["'])`,
|
|
7381
|
-
"gi"
|
|
7382
|
-
);
|
|
7340
|
+
const colorPattern = new RegExp(`(<w:shd[^>]*w:color=["'])${normalizedOld}(["'])`, 'gi');
|
|
7383
7341
|
|
|
7384
7342
|
stylesXml = stylesXml.replace(colorPattern, (match, prefix, suffix) => {
|
|
7385
7343
|
updateCount++;
|
|
@@ -7425,28 +7383,22 @@ export class Document {
|
|
|
7425
7383
|
let totalUpdated = 0;
|
|
7426
7384
|
|
|
7427
7385
|
// Default colors commonly used in table styles
|
|
7428
|
-
const defaultReplaceColors = [
|
|
7386
|
+
const defaultReplaceColors = ['A5A5A5', 'C0C0C0', 'D9D9D9', 'E7E6E6'];
|
|
7429
7387
|
const colorsToReplace = settings.replaceColors || defaultReplaceColors;
|
|
7430
7388
|
|
|
7431
7389
|
// Replace header-type colors with header shading
|
|
7432
7390
|
if (settings.headerShading) {
|
|
7433
7391
|
for (const oldColor of colorsToReplace) {
|
|
7434
|
-
totalUpdated += this.updateTableStyleShading(
|
|
7435
|
-
oldColor,
|
|
7436
|
-
settings.headerShading
|
|
7437
|
-
);
|
|
7392
|
+
totalUpdated += this.updateTableStyleShading(oldColor, settings.headerShading);
|
|
7438
7393
|
}
|
|
7439
7394
|
}
|
|
7440
7395
|
|
|
7441
7396
|
// If data shading differs from header, apply it to lighter colors
|
|
7442
7397
|
if (settings.dataShading && settings.dataShading !== settings.headerShading) {
|
|
7443
7398
|
// Typically data cells use lighter shading
|
|
7444
|
-
const dataColors = [
|
|
7399
|
+
const dataColors = ['D9D9D9', 'E7E6E6', 'F2F2F2'];
|
|
7445
7400
|
for (const oldColor of dataColors) {
|
|
7446
|
-
totalUpdated += this.updateTableStyleShading(
|
|
7447
|
-
oldColor,
|
|
7448
|
-
settings.dataShading
|
|
7449
|
-
);
|
|
7401
|
+
totalUpdated += this.updateTableStyleShading(oldColor, settings.dataShading);
|
|
7450
7402
|
}
|
|
7451
7403
|
}
|
|
7452
7404
|
|
|
@@ -7518,8 +7470,8 @@ export class Document {
|
|
|
7518
7470
|
const {
|
|
7519
7471
|
bold = false,
|
|
7520
7472
|
fontSize = 24, // 12pt
|
|
7521
|
-
color =
|
|
7522
|
-
font =
|
|
7473
|
+
color = '000000',
|
|
7474
|
+
font = 'Arial',
|
|
7523
7475
|
} = options || {};
|
|
7524
7476
|
|
|
7525
7477
|
let listsUpdated = 0;
|
|
@@ -7529,14 +7481,13 @@ export class Document {
|
|
|
7529
7481
|
|
|
7530
7482
|
for (const instance of instances) {
|
|
7531
7483
|
const abstractNumId = instance.getAbstractNumId();
|
|
7532
|
-
const abstractNum =
|
|
7533
|
-
this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
7484
|
+
const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
7534
7485
|
|
|
7535
7486
|
if (!abstractNum) continue;
|
|
7536
7487
|
|
|
7537
7488
|
// Only process bullet lists (skip numbered lists)
|
|
7538
7489
|
const level0 = abstractNum.getLevel(0);
|
|
7539
|
-
if (level0?.getFormat() !==
|
|
7490
|
+
if (level0?.getFormat() !== 'bullet') continue;
|
|
7540
7491
|
|
|
7541
7492
|
// Update all 9 levels (0-8) with formatting only (preserve existing symbols)
|
|
7542
7493
|
for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
|
|
@@ -7597,8 +7548,8 @@ export class Document {
|
|
|
7597
7548
|
const {
|
|
7598
7549
|
bold = false,
|
|
7599
7550
|
fontSize = 24, // 12pt
|
|
7600
|
-
color =
|
|
7601
|
-
font =
|
|
7551
|
+
color = '000000',
|
|
7552
|
+
font = 'Verdana',
|
|
7602
7553
|
} = options || {};
|
|
7603
7554
|
|
|
7604
7555
|
let listsUpdated = 0;
|
|
@@ -7608,14 +7559,13 @@ export class Document {
|
|
|
7608
7559
|
|
|
7609
7560
|
for (const instance of instances) {
|
|
7610
7561
|
const abstractNumId = instance.getAbstractNumId();
|
|
7611
|
-
const abstractNum =
|
|
7612
|
-
this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
7562
|
+
const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
7613
7563
|
|
|
7614
7564
|
if (!abstractNum) continue;
|
|
7615
7565
|
|
|
7616
7566
|
// Only process numbered lists (skip bullet lists)
|
|
7617
7567
|
const level0 = abstractNum.getLevel(0);
|
|
7618
|
-
if (!level0 || level0.getFormat() ===
|
|
7568
|
+
if (!level0 || level0.getFormat() === 'bullet') continue;
|
|
7619
7569
|
|
|
7620
7570
|
// Update all 9 levels (0-8)
|
|
7621
7571
|
for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
|
|
@@ -7666,12 +7616,7 @@ export class Document {
|
|
|
7666
7616
|
color?: string;
|
|
7667
7617
|
underline?: boolean;
|
|
7668
7618
|
}): number {
|
|
7669
|
-
const {
|
|
7670
|
-
font = "Verdana",
|
|
7671
|
-
size = 12,
|
|
7672
|
-
color = "0000FF",
|
|
7673
|
-
underline = true,
|
|
7674
|
-
} = options || {};
|
|
7619
|
+
const { font = 'Verdana', size = 12, color = '0000FF', underline = true } = options || {};
|
|
7675
7620
|
|
|
7676
7621
|
const hyperlinks = this.getHyperlinks();
|
|
7677
7622
|
|
|
@@ -7680,14 +7625,13 @@ export class Document {
|
|
|
7680
7625
|
font: font,
|
|
7681
7626
|
size: size,
|
|
7682
7627
|
color: color,
|
|
7683
|
-
underline: underline ?
|
|
7628
|
+
underline: underline ? 'single' : false,
|
|
7684
7629
|
});
|
|
7685
7630
|
}
|
|
7686
7631
|
|
|
7687
7632
|
return hyperlinks.length;
|
|
7688
7633
|
}
|
|
7689
7634
|
|
|
7690
|
-
|
|
7691
7635
|
/**
|
|
7692
7636
|
* Applies Heading 1 style to paragraphs with H1-like style names
|
|
7693
7637
|
* @param options Optional style application options
|
|
@@ -7710,7 +7654,7 @@ export class Document {
|
|
|
7710
7654
|
* ```
|
|
7711
7655
|
*/
|
|
7712
7656
|
public applyH1(options?: StyleApplyOptions): number {
|
|
7713
|
-
return this.applyStyleToMatching(
|
|
7657
|
+
return this.applyStyleToMatching('Heading1', options, (style) =>
|
|
7714
7658
|
/^(heading\s*1|header\s*1|h1)$/i.test(style)
|
|
7715
7659
|
);
|
|
7716
7660
|
}
|
|
@@ -7727,7 +7671,7 @@ export class Document {
|
|
|
7727
7671
|
* ```
|
|
7728
7672
|
*/
|
|
7729
7673
|
public applyH2(options?: StyleApplyOptions): number {
|
|
7730
|
-
return this.applyStyleToMatching(
|
|
7674
|
+
return this.applyStyleToMatching('Heading2', options, (style) =>
|
|
7731
7675
|
/^(heading\s*2|header\s*2|h2)$/i.test(style)
|
|
7732
7676
|
);
|
|
7733
7677
|
}
|
|
@@ -7744,7 +7688,7 @@ export class Document {
|
|
|
7744
7688
|
* ```
|
|
7745
7689
|
*/
|
|
7746
7690
|
public applyH3(options?: StyleApplyOptions): number {
|
|
7747
|
-
return this.applyStyleToMatching(
|
|
7691
|
+
return this.applyStyleToMatching('Heading3', options, (style) =>
|
|
7748
7692
|
/^(heading\s*3|header\s*3|h3)$/i.test(style)
|
|
7749
7693
|
);
|
|
7750
7694
|
}
|
|
@@ -7759,16 +7703,13 @@ export class Document {
|
|
|
7759
7703
|
options?.paragraphs ||
|
|
7760
7704
|
this.getAllParagraphs().filter((p) => {
|
|
7761
7705
|
const style = p.getStyle();
|
|
7762
|
-
return (
|
|
7763
|
-
!style ||
|
|
7764
|
-
!/^(heading|header|h\d|list|toc|tod|caution|table)/i.test(style)
|
|
7765
|
-
);
|
|
7706
|
+
return !style || !/^(heading|header|h\d|list|toc|tod|caution|table)/i.test(style);
|
|
7766
7707
|
});
|
|
7767
7708
|
|
|
7768
7709
|
let count = 0;
|
|
7769
7710
|
for (const para of targets) {
|
|
7770
7711
|
if (para.isPreserved()) continue;
|
|
7771
|
-
para.setStyle(
|
|
7712
|
+
para.setStyle('Normal');
|
|
7772
7713
|
|
|
7773
7714
|
if (options?.keepProperties && options.keepProperties.length > 0) {
|
|
7774
7715
|
this.clearFormattingExcept(para, options.keepProperties);
|
|
@@ -7791,7 +7732,7 @@ export class Document {
|
|
|
7791
7732
|
* @returns Number of paragraphs updated
|
|
7792
7733
|
*/
|
|
7793
7734
|
public applyNumList(options?: StyleApplyOptions): number {
|
|
7794
|
-
return this.applyStyleToMatching(
|
|
7735
|
+
return this.applyStyleToMatching('ListParagraph', options, (style) =>
|
|
7795
7736
|
/^(list\s*number|numbered\s*list|list\s*paragraph)$/i.test(style)
|
|
7796
7737
|
);
|
|
7797
7738
|
}
|
|
@@ -7802,7 +7743,7 @@ export class Document {
|
|
|
7802
7743
|
* @returns Number of paragraphs updated
|
|
7803
7744
|
*/
|
|
7804
7745
|
public applyBulletList(options?: StyleApplyOptions): number {
|
|
7805
|
-
return this.applyStyleToMatching(
|
|
7746
|
+
return this.applyStyleToMatching('ListParagraph', options, (style) =>
|
|
7806
7747
|
/^(list\s*bullet|bullet\s*list|list\s*paragraph)$/i.test(style)
|
|
7807
7748
|
);
|
|
7808
7749
|
}
|
|
@@ -7813,7 +7754,7 @@ export class Document {
|
|
|
7813
7754
|
* @returns Number of paragraphs updated
|
|
7814
7755
|
*/
|
|
7815
7756
|
public applyTOC(options?: StyleApplyOptions): number {
|
|
7816
|
-
return this.applyStyleToMatching(
|
|
7757
|
+
return this.applyStyleToMatching('TOC', options, (style) =>
|
|
7817
7758
|
/^(toc|table\s*of\s*contents|toc\s*heading)$/i.test(style)
|
|
7818
7759
|
);
|
|
7819
7760
|
}
|
|
@@ -7824,7 +7765,7 @@ export class Document {
|
|
|
7824
7765
|
* @returns Number of paragraphs updated
|
|
7825
7766
|
*/
|
|
7826
7767
|
public applyTOD(options?: StyleApplyOptions): number {
|
|
7827
|
-
return this.applyStyleToMatching(
|
|
7768
|
+
return this.applyStyleToMatching('TopOfDocument', options, (style) =>
|
|
7828
7769
|
/^(tod|top\s*of\s*document|document\s*top)$/i.test(style)
|
|
7829
7770
|
);
|
|
7830
7771
|
}
|
|
@@ -7835,7 +7776,7 @@ export class Document {
|
|
|
7835
7776
|
* @returns Number of paragraphs updated
|
|
7836
7777
|
*/
|
|
7837
7778
|
public applyCaution(options?: StyleApplyOptions): number {
|
|
7838
|
-
return this.applyStyleToMatching(
|
|
7779
|
+
return this.applyStyleToMatching('Caution', options, (style) =>
|
|
7839
7780
|
/^(caution|warning|important|alert)$/i.test(style)
|
|
7840
7781
|
);
|
|
7841
7782
|
}
|
|
@@ -7856,7 +7797,7 @@ export class Document {
|
|
|
7856
7797
|
for (const cell of firstRow.getCells()) {
|
|
7857
7798
|
for (const para of cell.getParagraphs()) {
|
|
7858
7799
|
if (para.isPreserved()) continue;
|
|
7859
|
-
para.setStyle(
|
|
7800
|
+
para.setStyle('TableHeader');
|
|
7860
7801
|
|
|
7861
7802
|
if (options?.keepProperties && options.keepProperties.length > 0) {
|
|
7862
7803
|
this.clearFormattingExcept(para, options.keepProperties);
|
|
@@ -7905,9 +7846,9 @@ export class Document {
|
|
|
7905
7846
|
if (options.color) run.setColor(options.color);
|
|
7906
7847
|
if (options.emphasis) {
|
|
7907
7848
|
options.emphasis.forEach((emp) => {
|
|
7908
|
-
if (emp ===
|
|
7909
|
-
if (emp ===
|
|
7910
|
-
if (emp ===
|
|
7849
|
+
if (emp === 'bold') run.setBold(true);
|
|
7850
|
+
if (emp === 'italic') run.setItalic(true);
|
|
7851
|
+
if (emp === 'underline') run.setUnderline('single');
|
|
7911
7852
|
});
|
|
7912
7853
|
}
|
|
7913
7854
|
}
|
|
@@ -7960,10 +7901,7 @@ export class Document {
|
|
|
7960
7901
|
* Helper method to selectively clear formatting while preserving specific properties
|
|
7961
7902
|
* @private
|
|
7962
7903
|
*/
|
|
7963
|
-
private clearFormattingExcept(
|
|
7964
|
-
para: Paragraph,
|
|
7965
|
-
keepProperties: string[]
|
|
7966
|
-
): void {
|
|
7904
|
+
private clearFormattingExcept(para: Paragraph, keepProperties: string[]): void {
|
|
7967
7905
|
// Save properties to keep
|
|
7968
7906
|
const savedProps: Record<string, unknown> = {};
|
|
7969
7907
|
const formatting = para.formatting as Record<string, unknown>;
|
|
@@ -8000,21 +7938,15 @@ export class Document {
|
|
|
8000
7938
|
|
|
8001
7939
|
// Restore saved properties using appropriate setters
|
|
8002
7940
|
if (runSavedProps.bold !== undefined) run.setBold(runSavedProps.bold);
|
|
8003
|
-
if (runSavedProps.italic !== undefined)
|
|
8004
|
-
|
|
8005
|
-
if (runSavedProps.underline !== undefined)
|
|
8006
|
-
run.setUnderline(runSavedProps.underline);
|
|
7941
|
+
if (runSavedProps.italic !== undefined) run.setItalic(runSavedProps.italic);
|
|
7942
|
+
if (runSavedProps.underline !== undefined) run.setUnderline(runSavedProps.underline);
|
|
8007
7943
|
if (runSavedProps.color !== undefined) run.setColor(runSavedProps.color);
|
|
8008
7944
|
if (runSavedProps.font !== undefined) run.setFont(runSavedProps.font);
|
|
8009
7945
|
if (runSavedProps.size !== undefined) run.setSize(runSavedProps.size);
|
|
8010
|
-
if (runSavedProps.highlight !== undefined)
|
|
8011
|
-
|
|
8012
|
-
if (runSavedProps.
|
|
8013
|
-
|
|
8014
|
-
if (runSavedProps.subscript !== undefined)
|
|
8015
|
-
run.setSubscript(runSavedProps.subscript);
|
|
8016
|
-
if (runSavedProps.superscript !== undefined)
|
|
8017
|
-
run.setSuperscript(runSavedProps.superscript);
|
|
7946
|
+
if (runSavedProps.highlight !== undefined) run.setHighlight(runSavedProps.highlight);
|
|
7947
|
+
if (runSavedProps.strike !== undefined) run.setStrike(runSavedProps.strike);
|
|
7948
|
+
if (runSavedProps.subscript !== undefined) run.setSubscript(runSavedProps.subscript);
|
|
7949
|
+
if (runSavedProps.superscript !== undefined) run.setSuperscript(runSavedProps.superscript);
|
|
8018
7950
|
}
|
|
8019
7951
|
}
|
|
8020
7952
|
|
|
@@ -8130,7 +8062,7 @@ export class Document {
|
|
|
8130
8062
|
|
|
8131
8063
|
for (const match of tMatches) {
|
|
8132
8064
|
hasTableSwitch = true;
|
|
8133
|
-
const content = (match[1] ||
|
|
8065
|
+
const content = (match[1] || '').trim();
|
|
8134
8066
|
if (!content) continue;
|
|
8135
8067
|
|
|
8136
8068
|
// --- Case 1: Range format "X-Y" ---
|
|
@@ -8147,7 +8079,7 @@ export class Document {
|
|
|
8147
8079
|
// --- Case 2: Style format "StyleName,Level," (e.g., "Heading 2,2,") ---
|
|
8148
8080
|
// Split by comma, expect pattern: styleName, level, [optional trailing comma]
|
|
8149
8081
|
const parts = content
|
|
8150
|
-
.split(
|
|
8082
|
+
.split(',')
|
|
8151
8083
|
.map((p) => p.trim())
|
|
8152
8084
|
.filter(Boolean);
|
|
8153
8085
|
for (let i = 0; i < parts.length; i += 2) {
|
|
@@ -8207,14 +8139,14 @@ export class Document {
|
|
|
8207
8139
|
|
|
8208
8140
|
// Decode XML entities in instruction
|
|
8209
8141
|
const fieldInstruction = match[1]
|
|
8210
|
-
.replace(/&/g,
|
|
8211
|
-
.replace(/</g,
|
|
8212
|
-
.replace(/>/g,
|
|
8142
|
+
.replace(/&/g, '&')
|
|
8143
|
+
.replace(/</g, '<')
|
|
8144
|
+
.replace(/>/g, '>')
|
|
8213
8145
|
.replace(/"/g, '"')
|
|
8214
8146
|
.replace(/'/g, "'");
|
|
8215
8147
|
|
|
8216
8148
|
// Check if instruction contains \t switches (style-specific TOC)
|
|
8217
|
-
if (!fieldInstruction.includes(
|
|
8149
|
+
if (!fieldInstruction.includes('\\t')) {
|
|
8218
8150
|
continue; // Outline-based TOC, no style names to sync
|
|
8219
8151
|
}
|
|
8220
8152
|
|
|
@@ -8225,11 +8157,11 @@ export class Document {
|
|
|
8225
8157
|
if (updatedInstruction !== fieldInstruction) {
|
|
8226
8158
|
// Re-encode for XML
|
|
8227
8159
|
const encodedInstruction = updatedInstruction
|
|
8228
|
-
.replace(/&/g,
|
|
8229
|
-
.replace(/</g,
|
|
8230
|
-
.replace(/>/g,
|
|
8231
|
-
.replace(/"/g,
|
|
8232
|
-
.replace(/'/g,
|
|
8160
|
+
.replace(/&/g, '&')
|
|
8161
|
+
.replace(/</g, '<')
|
|
8162
|
+
.replace(/>/g, '>')
|
|
8163
|
+
.replace(/"/g, '"')
|
|
8164
|
+
.replace(/'/g, ''');
|
|
8233
8165
|
|
|
8234
8166
|
// Replace the instruction
|
|
8235
8167
|
const updatedInstrXml = `<w:instrText xml:space="preserve">${encodedInstruction}</w:instrText>`;
|
|
@@ -8250,7 +8182,7 @@ export class Document {
|
|
|
8250
8182
|
} catch (error: unknown) {
|
|
8251
8183
|
// Log error but don't fail the save
|
|
8252
8184
|
this.logger.error(
|
|
8253
|
-
|
|
8185
|
+
'Error syncing TOC field instructions - document will save with original instructions',
|
|
8254
8186
|
error instanceof Error
|
|
8255
8187
|
? { message: error.message, stack: error.stack }
|
|
8256
8188
|
: { error: String(error) }
|
|
@@ -8263,9 +8195,7 @@ export class Document {
|
|
|
8263
8195
|
* Helper to sync TOC instruction within an XML fragment
|
|
8264
8196
|
* @private
|
|
8265
8197
|
*/
|
|
8266
|
-
private syncTOCInstructionInXml(
|
|
8267
|
-
xml: string
|
|
8268
|
-
): { xml: string; changed: boolean } {
|
|
8198
|
+
private syncTOCInstructionInXml(xml: string): { xml: string; changed: boolean } {
|
|
8269
8199
|
const instrMatch = /<w:instrText[^>]*>([\s\S]*?)<\/w:instrText>/.exec(xml);
|
|
8270
8200
|
if (!instrMatch?.[1]) {
|
|
8271
8201
|
return { xml, changed: false };
|
|
@@ -8273,14 +8203,14 @@ export class Document {
|
|
|
8273
8203
|
|
|
8274
8204
|
// Decode XML entities in instruction
|
|
8275
8205
|
const fieldInstruction = instrMatch[1]
|
|
8276
|
-
.replace(/&/g,
|
|
8277
|
-
.replace(/</g,
|
|
8278
|
-
.replace(/>/g,
|
|
8206
|
+
.replace(/&/g, '&')
|
|
8207
|
+
.replace(/</g, '<')
|
|
8208
|
+
.replace(/>/g, '>')
|
|
8279
8209
|
.replace(/"/g, '"')
|
|
8280
8210
|
.replace(/'/g, "'");
|
|
8281
8211
|
|
|
8282
8212
|
// Check if instruction contains \t switches (style-specific TOC)
|
|
8283
|
-
if (!fieldInstruction.includes(
|
|
8213
|
+
if (!fieldInstruction.includes('\\t')) {
|
|
8284
8214
|
return { xml, changed: false };
|
|
8285
8215
|
}
|
|
8286
8216
|
|
|
@@ -8293,11 +8223,11 @@ export class Document {
|
|
|
8293
8223
|
|
|
8294
8224
|
// Re-encode for XML
|
|
8295
8225
|
const encodedInstruction = updatedInstruction
|
|
8296
|
-
.replace(/&/g,
|
|
8297
|
-
.replace(/</g,
|
|
8298
|
-
.replace(/>/g,
|
|
8299
|
-
.replace(/"/g,
|
|
8300
|
-
.replace(/'/g,
|
|
8226
|
+
.replace(/&/g, '&')
|
|
8227
|
+
.replace(/</g, '<')
|
|
8228
|
+
.replace(/>/g, '>')
|
|
8229
|
+
.replace(/"/g, '"')
|
|
8230
|
+
.replace(/'/g, ''');
|
|
8301
8231
|
|
|
8302
8232
|
const updatedXml = xml.replace(
|
|
8303
8233
|
/<w:instrText[^>]*>[\s\S]*?<\/w:instrText>/,
|
|
@@ -8331,7 +8261,10 @@ export class Document {
|
|
|
8331
8261
|
|
|
8332
8262
|
// Parse style names and levels from the switch content
|
|
8333
8263
|
// Format: "StyleName,Level," or "StyleName,Level,StyleName2,Level2,..."
|
|
8334
|
-
const parts = content
|
|
8264
|
+
const parts = content
|
|
8265
|
+
.split(',')
|
|
8266
|
+
.map((p) => p.trim())
|
|
8267
|
+
.filter(Boolean);
|
|
8335
8268
|
const updatedParts: string[] = [];
|
|
8336
8269
|
|
|
8337
8270
|
for (let i = 0; i < parts.length; i += 2) {
|
|
@@ -8496,31 +8429,30 @@ export class Document {
|
|
|
8496
8429
|
docXml: string,
|
|
8497
8430
|
levels: number[]
|
|
8498
8431
|
): { level: number; text: string; bookmark: string }[] {
|
|
8499
|
-
const headings: { level: number; text: string; bookmark: string }[] =
|
|
8500
|
-
[];
|
|
8432
|
+
const headings: { level: number; text: string; bookmark: string }[] = [];
|
|
8501
8433
|
const levelSet = new Set(levels);
|
|
8502
8434
|
|
|
8503
8435
|
try {
|
|
8504
8436
|
// Parse document.xml to object structure
|
|
8505
8437
|
const parsed = XMLParser.parseToObject(docXml, { trimValues: false });
|
|
8506
|
-
const document = parsed[
|
|
8438
|
+
const document = parsed['w:document'];
|
|
8507
8439
|
if (!document) {
|
|
8508
8440
|
return headings;
|
|
8509
8441
|
}
|
|
8510
8442
|
|
|
8511
|
-
const body = (document as any)[
|
|
8443
|
+
const body = (document as any)['w:body'];
|
|
8512
8444
|
if (!body) {
|
|
8513
8445
|
return headings;
|
|
8514
8446
|
}
|
|
8515
8447
|
|
|
8516
8448
|
// Helper function to extract heading info from a parsed paragraph object
|
|
8517
8449
|
const extractHeading = (para: any): void => {
|
|
8518
|
-
const pPr = para[
|
|
8519
|
-
if (!pPr?.[
|
|
8450
|
+
const pPr = para['w:pPr'];
|
|
8451
|
+
if (!pPr?.['w:pStyle']) {
|
|
8520
8452
|
return;
|
|
8521
8453
|
}
|
|
8522
8454
|
|
|
8523
|
-
const styleVal = pPr[
|
|
8455
|
+
const styleVal = pPr['w:pStyle']['@_w:val'];
|
|
8524
8456
|
if (!styleVal) {
|
|
8525
8457
|
return;
|
|
8526
8458
|
}
|
|
@@ -8539,26 +8471,27 @@ export class Document {
|
|
|
8539
8471
|
}
|
|
8540
8472
|
|
|
8541
8473
|
// Extract bookmark (use any existing bookmark, prioritize "_heading" or "_Toc")
|
|
8542
|
-
let bookmark =
|
|
8543
|
-
const bookmarkStart = para[
|
|
8474
|
+
let bookmark = '';
|
|
8475
|
+
const bookmarkStart = para['w:bookmarkStart'];
|
|
8544
8476
|
if (bookmarkStart) {
|
|
8545
|
-
const bookmarkArray = Array.isArray(bookmarkStart)
|
|
8546
|
-
|
|
8547
|
-
: [bookmarkStart];
|
|
8548
|
-
|
|
8477
|
+
const bookmarkArray = Array.isArray(bookmarkStart) ? bookmarkStart : [bookmarkStart];
|
|
8478
|
+
|
|
8549
8479
|
// First try to find preferred bookmark types
|
|
8550
8480
|
for (const bm of bookmarkArray) {
|
|
8551
|
-
const bmName = bm[
|
|
8552
|
-
if (
|
|
8481
|
+
const bmName = bm['@_w:name'];
|
|
8482
|
+
if (
|
|
8483
|
+
bmName &&
|
|
8484
|
+
(bmName.toLowerCase().includes('_heading') || bmName.toLowerCase().includes('_toc'))
|
|
8485
|
+
) {
|
|
8553
8486
|
bookmark = bmName;
|
|
8554
8487
|
break;
|
|
8555
8488
|
}
|
|
8556
8489
|
}
|
|
8557
|
-
|
|
8490
|
+
|
|
8558
8491
|
// If no preferred bookmark found, use the first available bookmark
|
|
8559
8492
|
if (!bookmark && bookmarkArray.length > 0) {
|
|
8560
8493
|
const firstBm = bookmarkArray[0];
|
|
8561
|
-
const bmName = firstBm[
|
|
8494
|
+
const bmName = firstBm['@_w:name'];
|
|
8562
8495
|
if (bmName) {
|
|
8563
8496
|
bookmark = bmName;
|
|
8564
8497
|
}
|
|
@@ -8566,17 +8499,17 @@ export class Document {
|
|
|
8566
8499
|
}
|
|
8567
8500
|
|
|
8568
8501
|
// Extract text from runs
|
|
8569
|
-
let text =
|
|
8570
|
-
const runs = para[
|
|
8502
|
+
let text = '';
|
|
8503
|
+
const runs = para['w:r'];
|
|
8571
8504
|
if (runs) {
|
|
8572
8505
|
const runArray = Array.isArray(runs) ? runs : [runs];
|
|
8573
8506
|
for (const run of runArray) {
|
|
8574
|
-
const textElement = run[
|
|
8507
|
+
const textElement = run['w:t'];
|
|
8575
8508
|
if (textElement) {
|
|
8576
|
-
if (typeof textElement ===
|
|
8509
|
+
if (typeof textElement === 'string') {
|
|
8577
8510
|
text += textElement;
|
|
8578
|
-
} else if (textElement[
|
|
8579
|
-
text += textElement[
|
|
8511
|
+
} else if (textElement['#text']) {
|
|
8512
|
+
text += textElement['#text'];
|
|
8580
8513
|
}
|
|
8581
8514
|
}
|
|
8582
8515
|
}
|
|
@@ -8601,7 +8534,7 @@ export class Document {
|
|
|
8601
8534
|
};
|
|
8602
8535
|
|
|
8603
8536
|
// Search in direct paragraphs
|
|
8604
|
-
const paragraphs = body[
|
|
8537
|
+
const paragraphs = body['w:p'];
|
|
8605
8538
|
if (paragraphs) {
|
|
8606
8539
|
const paraArray = Array.isArray(paragraphs) ? paragraphs : [paragraphs];
|
|
8607
8540
|
for (const para of paraArray) {
|
|
@@ -8610,26 +8543,24 @@ export class Document {
|
|
|
8610
8543
|
}
|
|
8611
8544
|
|
|
8612
8545
|
// Search in tables (this is critical - many documents have headings in tables)
|
|
8613
|
-
const tables = body[
|
|
8546
|
+
const tables = body['w:tbl'];
|
|
8614
8547
|
if (tables) {
|
|
8615
8548
|
const tableArray = Array.isArray(tables) ? tables : [tables];
|
|
8616
8549
|
for (const table of tableArray) {
|
|
8617
|
-
const rows = table[
|
|
8550
|
+
const rows = table['w:tr'];
|
|
8618
8551
|
if (!rows) continue;
|
|
8619
8552
|
|
|
8620
8553
|
const rowArray = Array.isArray(rows) ? rows : [rows];
|
|
8621
8554
|
for (const row of rowArray) {
|
|
8622
|
-
const cells = row[
|
|
8555
|
+
const cells = row['w:tc'];
|
|
8623
8556
|
if (!cells) continue;
|
|
8624
8557
|
|
|
8625
8558
|
const cellArray = Array.isArray(cells) ? cells : [cells];
|
|
8626
8559
|
for (const cell of cellArray) {
|
|
8627
|
-
const cellParas = cell[
|
|
8560
|
+
const cellParas = cell['w:p'];
|
|
8628
8561
|
if (!cellParas) continue;
|
|
8629
8562
|
|
|
8630
|
-
const cellParaArray = Array.isArray(cellParas)
|
|
8631
|
-
? cellParas
|
|
8632
|
-
: [cellParas];
|
|
8563
|
+
const cellParaArray = Array.isArray(cellParas) ? cellParas : [cellParas];
|
|
8633
8564
|
for (const para of cellParaArray) {
|
|
8634
8565
|
extractHeading(para);
|
|
8635
8566
|
}
|
|
@@ -8639,7 +8570,7 @@ export class Document {
|
|
|
8639
8570
|
}
|
|
8640
8571
|
} catch (error: unknown) {
|
|
8641
8572
|
defaultLogger.error(
|
|
8642
|
-
|
|
8573
|
+
'Error parsing document.xml for headings:',
|
|
8643
8574
|
error instanceof Error
|
|
8644
8575
|
? { message: error.message, stack: error.stack }
|
|
8645
8576
|
: { error: String(error) }
|
|
@@ -8657,8 +8588,7 @@ export class Document {
|
|
|
8657
8588
|
private findHeadingsForTOC(
|
|
8658
8589
|
levels: number[]
|
|
8659
8590
|
): { level: number; text: string; bookmark: string }[] {
|
|
8660
|
-
const headings: { level: number; text: string; bookmark: string }[] =
|
|
8661
|
-
[];
|
|
8591
|
+
const headings: { level: number; text: string; bookmark: string }[] = [];
|
|
8662
8592
|
const levelSet = new Set(levels);
|
|
8663
8593
|
|
|
8664
8594
|
// Iterate through body elements
|
|
@@ -8679,8 +8609,7 @@ export class Document {
|
|
|
8679
8609
|
|
|
8680
8610
|
if (text) {
|
|
8681
8611
|
// Create or get bookmark for this heading
|
|
8682
|
-
const bookmark =
|
|
8683
|
-
this.bookmarkManager.createHeadingBookmark(text);
|
|
8612
|
+
const bookmark = this.bookmarkManager.createHeadingBookmark(text);
|
|
8684
8613
|
|
|
8685
8614
|
headings.push({
|
|
8686
8615
|
level: headingLevel,
|
|
@@ -8715,30 +8644,28 @@ export class Document {
|
|
|
8715
8644
|
): string {
|
|
8716
8645
|
const sdtId = Math.floor(Math.random() * 2000000000) - 1000000000;
|
|
8717
8646
|
|
|
8718
|
-
let tocXml =
|
|
8647
|
+
let tocXml = '<w:sdt>';
|
|
8719
8648
|
|
|
8720
8649
|
// SDT properties
|
|
8721
|
-
tocXml +=
|
|
8650
|
+
tocXml += '<w:sdtPr>';
|
|
8722
8651
|
tocXml += `<w:id w:val="${sdtId}"/>`;
|
|
8723
|
-
tocXml +=
|
|
8652
|
+
tocXml += '<w:docPartObj>';
|
|
8724
8653
|
tocXml += '<w:docPartGallery w:val="Table of Contents"/>';
|
|
8725
8654
|
tocXml += '<w:docPartUnique w:val="1"/>';
|
|
8726
|
-
tocXml +=
|
|
8727
|
-
tocXml +=
|
|
8655
|
+
tocXml += '</w:docPartObj>';
|
|
8656
|
+
tocXml += '</w:sdtPr>';
|
|
8728
8657
|
|
|
8729
8658
|
// SDT content
|
|
8730
|
-
tocXml +=
|
|
8659
|
+
tocXml += '<w:sdtContent>';
|
|
8731
8660
|
|
|
8732
8661
|
// Calculate minimum level for relative indentation
|
|
8733
8662
|
// If TOC shows only Heading 2s, minLevel=2, so Heading 2 gets 0" indent
|
|
8734
|
-
const minLevel =
|
|
8735
|
-
headings.length > 0 ? Math.min(...headings.map((h) => h.level)) : 1;
|
|
8663
|
+
const minLevel = headings.length > 0 ? Math.min(...headings.map((h) => h.level)) : 1;
|
|
8736
8664
|
|
|
8737
8665
|
// First paragraph: field begin + instruction + separator + first entry (if any)
|
|
8738
|
-
tocXml +=
|
|
8739
|
-
tocXml +=
|
|
8740
|
-
tocXml +=
|
|
8741
|
-
'<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
|
|
8666
|
+
tocXml += '<w:p>';
|
|
8667
|
+
tocXml += '<w:pPr>';
|
|
8668
|
+
tocXml += '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
|
|
8742
8669
|
|
|
8743
8670
|
// Add indentation for first entry relative to minimum level (0.25" per level)
|
|
8744
8671
|
if (headings.length > 0 && headings[0]) {
|
|
@@ -8748,17 +8675,17 @@ export class Document {
|
|
|
8748
8675
|
}
|
|
8749
8676
|
}
|
|
8750
8677
|
|
|
8751
|
-
tocXml +=
|
|
8678
|
+
tocXml += '</w:pPr>';
|
|
8752
8679
|
|
|
8753
8680
|
// Field begin
|
|
8754
8681
|
tocXml += '<w:r><w:fldChar w:fldCharType="begin"/></w:r>';
|
|
8755
8682
|
|
|
8756
8683
|
// Field instruction (preserve original switches)
|
|
8757
|
-
tocXml +=
|
|
8684
|
+
tocXml += '<w:r>';
|
|
8758
8685
|
tocXml += `<w:instrText xml:space="preserve">${this.escapeXml(
|
|
8759
8686
|
originalInstrText
|
|
8760
8687
|
)}</w:instrText>`;
|
|
8761
|
-
tocXml +=
|
|
8688
|
+
tocXml += '</w:r>';
|
|
8762
8689
|
|
|
8763
8690
|
// Field separator
|
|
8764
8691
|
tocXml += '<w:r><w:fldChar w:fldCharType="separate"/></w:r>';
|
|
@@ -8768,17 +8695,16 @@ export class Document {
|
|
|
8768
8695
|
tocXml += this.buildTOCEntryXML(headings[0]);
|
|
8769
8696
|
}
|
|
8770
8697
|
|
|
8771
|
-
tocXml +=
|
|
8698
|
+
tocXml += '</w:p>';
|
|
8772
8699
|
|
|
8773
8700
|
// Remaining entries (each in its own paragraph)
|
|
8774
8701
|
for (let i = 1; i < headings.length; i++) {
|
|
8775
8702
|
const heading = headings[i];
|
|
8776
8703
|
if (!heading) continue;
|
|
8777
8704
|
|
|
8778
|
-
tocXml +=
|
|
8779
|
-
tocXml +=
|
|
8780
|
-
tocXml +=
|
|
8781
|
-
'<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
|
|
8705
|
+
tocXml += '<w:p>';
|
|
8706
|
+
tocXml += '<w:pPr>';
|
|
8707
|
+
tocXml += '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
|
|
8782
8708
|
|
|
8783
8709
|
// Add indentation relative to minimum level (0.25" per level above minimum)
|
|
8784
8710
|
const indent = (heading.level - minLevel) * 360;
|
|
@@ -8786,22 +8712,21 @@ export class Document {
|
|
|
8786
8712
|
tocXml += `<w:ind w:left="${indent}"/>`;
|
|
8787
8713
|
}
|
|
8788
8714
|
|
|
8789
|
-
tocXml +=
|
|
8715
|
+
tocXml += '</w:pPr>';
|
|
8790
8716
|
tocXml += this.buildTOCEntryXML(heading);
|
|
8791
|
-
tocXml +=
|
|
8717
|
+
tocXml += '</w:p>';
|
|
8792
8718
|
}
|
|
8793
8719
|
|
|
8794
8720
|
// Final paragraph with field end
|
|
8795
|
-
tocXml +=
|
|
8796
|
-
tocXml +=
|
|
8797
|
-
tocXml +=
|
|
8798
|
-
|
|
8799
|
-
tocXml += "</w:pPr>";
|
|
8721
|
+
tocXml += '<w:p>';
|
|
8722
|
+
tocXml += '<w:pPr>';
|
|
8723
|
+
tocXml += '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
|
|
8724
|
+
tocXml += '</w:pPr>';
|
|
8800
8725
|
tocXml += '<w:r><w:fldChar w:fldCharType="end"/></w:r>';
|
|
8801
|
-
tocXml +=
|
|
8726
|
+
tocXml += '</w:p>';
|
|
8802
8727
|
|
|
8803
|
-
tocXml +=
|
|
8804
|
-
tocXml +=
|
|
8728
|
+
tocXml += '</w:sdtContent>';
|
|
8729
|
+
tocXml += '</w:sdt>';
|
|
8805
8730
|
|
|
8806
8731
|
return tocXml;
|
|
8807
8732
|
}
|
|
@@ -8812,27 +8737,22 @@ export class Document {
|
|
|
8812
8737
|
* @param heading Heading information
|
|
8813
8738
|
* @returns XML string for the TOC entry
|
|
8814
8739
|
*/
|
|
8815
|
-
private buildTOCEntryXML(heading: {
|
|
8816
|
-
level: number;
|
|
8817
|
-
text: string;
|
|
8818
|
-
bookmark: string;
|
|
8819
|
-
}): string {
|
|
8740
|
+
private buildTOCEntryXML(heading: { level: number; text: string; bookmark: string }): string {
|
|
8820
8741
|
const escapedText = this.escapeXml(heading.text);
|
|
8821
8742
|
|
|
8822
|
-
let xml =
|
|
8743
|
+
let xml = '';
|
|
8823
8744
|
xml += `<w:hyperlink w:anchor="${this.escapeXml(heading.bookmark)}">`;
|
|
8824
|
-
xml +=
|
|
8825
|
-
xml +=
|
|
8826
|
-
xml +=
|
|
8827
|
-
'<w:rFonts w:ascii="Verdana" w:hAnsi="Verdana" w:cs="Verdana" w:eastAsia="Verdana"/>';
|
|
8745
|
+
xml += '<w:r>';
|
|
8746
|
+
xml += '<w:rPr>';
|
|
8747
|
+
xml += '<w:rFonts w:ascii="Verdana" w:hAnsi="Verdana" w:cs="Verdana" w:eastAsia="Verdana"/>';
|
|
8828
8748
|
xml += '<w:color w:val="0000FF"/>';
|
|
8829
8749
|
xml += '<w:sz w:val="24"/>';
|
|
8830
8750
|
xml += '<w:szCs w:val="24"/>';
|
|
8831
8751
|
xml += '<w:u w:val="single"/>';
|
|
8832
|
-
xml +=
|
|
8752
|
+
xml += '</w:rPr>';
|
|
8833
8753
|
xml += `<w:t xml:space="preserve">${escapedText}</w:t>`;
|
|
8834
|
-
xml +=
|
|
8835
|
-
xml +=
|
|
8754
|
+
xml += '</w:r>';
|
|
8755
|
+
xml += '</w:hyperlink>';
|
|
8836
8756
|
|
|
8837
8757
|
return xml;
|
|
8838
8758
|
}
|
|
@@ -8845,11 +8765,11 @@ export class Document {
|
|
|
8845
8765
|
*/
|
|
8846
8766
|
private escapeXml(text: string): string {
|
|
8847
8767
|
return text
|
|
8848
|
-
.replace(/&/g,
|
|
8849
|
-
.replace(/</g,
|
|
8850
|
-
.replace(/>/g,
|
|
8851
|
-
.replace(/"/g,
|
|
8852
|
-
.replace(/'/g,
|
|
8768
|
+
.replace(/&/g, '&')
|
|
8769
|
+
.replace(/</g, '<')
|
|
8770
|
+
.replace(/>/g, '>')
|
|
8771
|
+
.replace(/"/g, '"')
|
|
8772
|
+
.replace(/'/g, ''');
|
|
8853
8773
|
}
|
|
8854
8774
|
|
|
8855
8775
|
/**
|
|
@@ -8866,7 +8786,7 @@ export class Document {
|
|
|
8866
8786
|
await handler.load(filePath);
|
|
8867
8787
|
|
|
8868
8788
|
// Get document.xml
|
|
8869
|
-
const docXml = handler.getFileAsString(
|
|
8789
|
+
const docXml = handler.getFileAsString('word/document.xml');
|
|
8870
8790
|
if (!docXml) {
|
|
8871
8791
|
return 0;
|
|
8872
8792
|
}
|
|
@@ -8876,7 +8796,7 @@ export class Document {
|
|
|
8876
8796
|
|
|
8877
8797
|
// Update and save if changes were made
|
|
8878
8798
|
if (modifiedXml !== docXml) {
|
|
8879
|
-
handler.updateFile(
|
|
8799
|
+
handler.updateFile('word/document.xml', modifiedXml);
|
|
8880
8800
|
await handler.save(filePath);
|
|
8881
8801
|
|
|
8882
8802
|
// Count TOCs that were populated
|
|
@@ -8913,9 +8833,9 @@ export class Document {
|
|
|
8913
8833
|
if (!instrMatch?.[1]) continue;
|
|
8914
8834
|
|
|
8915
8835
|
const fieldInstruction = instrMatch[1]
|
|
8916
|
-
.replace(/&/g,
|
|
8917
|
-
.replace(/</g,
|
|
8918
|
-
.replace(/>/g,
|
|
8836
|
+
.replace(/&/g, '&')
|
|
8837
|
+
.replace(/</g, '<')
|
|
8838
|
+
.replace(/>/g, '>')
|
|
8919
8839
|
.replace(/"/g, '"')
|
|
8920
8840
|
.replace(/'/g, "'");
|
|
8921
8841
|
|
|
@@ -8927,7 +8847,7 @@ export class Document {
|
|
|
8927
8847
|
modifiedXml = modifiedXml.replace(tocXml, newTocXml);
|
|
8928
8848
|
} catch (error: unknown) {
|
|
8929
8849
|
this.logger.error(
|
|
8930
|
-
|
|
8850
|
+
'Error populating SDT TOC',
|
|
8931
8851
|
error instanceof Error
|
|
8932
8852
|
? { message: error.message, stack: error.stack }
|
|
8933
8853
|
: { error: String(error) }
|
|
@@ -8952,9 +8872,9 @@ export class Document {
|
|
|
8952
8872
|
if (!instrText) continue;
|
|
8953
8873
|
|
|
8954
8874
|
const fieldInstruction = instrText
|
|
8955
|
-
.replace(/&/g,
|
|
8956
|
-
.replace(/</g,
|
|
8957
|
-
.replace(/>/g,
|
|
8875
|
+
.replace(/&/g, '&')
|
|
8876
|
+
.replace(/</g, '<')
|
|
8877
|
+
.replace(/>/g, '>')
|
|
8958
8878
|
.replace(/"/g, '"')
|
|
8959
8879
|
.replace(/'/g, "'");
|
|
8960
8880
|
|
|
@@ -8989,12 +8909,10 @@ export class Document {
|
|
|
8989
8909
|
const newTocXml = this.generateSimpleTOCXML(headings, fieldInstruction);
|
|
8990
8910
|
modifiedXml = modifiedXml.replace(fullTocXml, newTocXml);
|
|
8991
8911
|
|
|
8992
|
-
this.logger.info(
|
|
8993
|
-
`Populated simple TOC with ${headings.length} heading entries`
|
|
8994
|
-
);
|
|
8912
|
+
this.logger.info(`Populated simple TOC with ${headings.length} heading entries`);
|
|
8995
8913
|
} catch (error: unknown) {
|
|
8996
8914
|
this.logger.error(
|
|
8997
|
-
|
|
8915
|
+
'Error populating simple TOC',
|
|
8998
8916
|
error instanceof Error
|
|
8999
8917
|
? { message: error.message, stack: error.stack }
|
|
9000
8918
|
: { error: String(error) }
|
|
@@ -9029,29 +8947,29 @@ export class Document {
|
|
|
9029
8947
|
|
|
9030
8948
|
// Escape text for XML
|
|
9031
8949
|
const escapedText = heading.text
|
|
9032
|
-
.replace(/&/g,
|
|
9033
|
-
.replace(/</g,
|
|
9034
|
-
.replace(/>/g,
|
|
9035
|
-
.replace(/"/g,
|
|
8950
|
+
.replace(/&/g, '&')
|
|
8951
|
+
.replace(/</g, '<')
|
|
8952
|
+
.replace(/>/g, '>')
|
|
8953
|
+
.replace(/"/g, '"');
|
|
9036
8954
|
|
|
9037
8955
|
// Create hyperlinked TOC entry paragraph
|
|
9038
8956
|
entries.push(
|
|
9039
8957
|
`<w:p>` +
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
8958
|
+
`<w:pPr><w:pStyle w:val="${tocStyle}"/></w:pPr>` +
|
|
8959
|
+
`<w:hyperlink w:anchor="${heading.bookmark}" w:history="1">` +
|
|
8960
|
+
`<w:r><w:rPr><w:rStyle w:val="Hyperlink"/></w:rPr>` +
|
|
8961
|
+
`<w:t>${escapedText}</w:t></w:r>` +
|
|
8962
|
+
`</w:hyperlink>` +
|
|
8963
|
+
`</w:p>`
|
|
9046
8964
|
);
|
|
9047
8965
|
}
|
|
9048
8966
|
|
|
9049
8967
|
// Escape field instruction for XML
|
|
9050
8968
|
const escapedInstruction = fieldInstruction
|
|
9051
|
-
.replace(/&/g,
|
|
9052
|
-
.replace(/</g,
|
|
9053
|
-
.replace(/>/g,
|
|
9054
|
-
.replace(/"/g,
|
|
8969
|
+
.replace(/&/g, '&')
|
|
8970
|
+
.replace(/</g, '<')
|
|
8971
|
+
.replace(/>/g, '>')
|
|
8972
|
+
.replace(/"/g, '"');
|
|
9055
8973
|
|
|
9056
8974
|
// Build the complete TOC field structure
|
|
9057
8975
|
return (
|
|
@@ -9063,7 +8981,7 @@ export class Document {
|
|
|
9063
8981
|
`<w:r><w:fldChar w:fldCharType="separate"/></w:r>` +
|
|
9064
8982
|
`</w:p>` +
|
|
9065
8983
|
// TOC entry paragraphs (between separate and end)
|
|
9066
|
-
entries.join(
|
|
8984
|
+
entries.join('') +
|
|
9067
8985
|
// Last paragraph: fldChar end
|
|
9068
8986
|
`<w:p>` +
|
|
9069
8987
|
`<w:pPr><w:pStyle w:val="TOC2"/></w:pPr>` +
|
|
@@ -9127,11 +9045,7 @@ export class Document {
|
|
|
9127
9045
|
* @param orientation Page orientation
|
|
9128
9046
|
* @returns This document for chaining
|
|
9129
9047
|
*/
|
|
9130
|
-
setPageSize(
|
|
9131
|
-
width: number,
|
|
9132
|
-
height: number,
|
|
9133
|
-
orientation?: "portrait" | "landscape"
|
|
9134
|
-
): this {
|
|
9048
|
+
setPageSize(width: number, height: number, orientation?: 'portrait' | 'landscape'): this {
|
|
9135
9049
|
this.section.setPageSize(width, height, orientation);
|
|
9136
9050
|
return this;
|
|
9137
9051
|
}
|
|
@@ -9141,7 +9055,7 @@ export class Document {
|
|
|
9141
9055
|
* @param orientation Page orientation
|
|
9142
9056
|
* @returns This document for chaining
|
|
9143
9057
|
*/
|
|
9144
|
-
setPageOrientation(orientation:
|
|
9058
|
+
setPageOrientation(orientation: 'portrait' | 'landscape'): this {
|
|
9145
9059
|
this.section.setOrientation(orientation);
|
|
9146
9060
|
return this;
|
|
9147
9061
|
}
|
|
@@ -9171,15 +9085,13 @@ export class Document {
|
|
|
9171
9085
|
*/
|
|
9172
9086
|
setHeader(header: Header): this {
|
|
9173
9087
|
// Generate relationship for header
|
|
9174
|
-
const relationship = this.relationshipManager.addHeader(
|
|
9175
|
-
`${header.getFilename(1)}`
|
|
9176
|
-
);
|
|
9088
|
+
const relationship = this.relationshipManager.addHeader(`${header.getFilename(1)}`);
|
|
9177
9089
|
|
|
9178
9090
|
// Register with manager
|
|
9179
9091
|
this.headerFooterManager.registerHeader(header, relationship.getId());
|
|
9180
9092
|
|
|
9181
9093
|
// Link to section
|
|
9182
|
-
this.section.setHeaderReference(
|
|
9094
|
+
this.section.setHeaderReference('default', relationship.getId());
|
|
9183
9095
|
|
|
9184
9096
|
return this;
|
|
9185
9097
|
}
|
|
@@ -9202,7 +9114,7 @@ export class Document {
|
|
|
9202
9114
|
this.headerFooterManager.registerHeader(header, relationship.getId());
|
|
9203
9115
|
|
|
9204
9116
|
// Link to section
|
|
9205
|
-
this.section.setHeaderReference(
|
|
9117
|
+
this.section.setHeaderReference('first', relationship.getId());
|
|
9206
9118
|
|
|
9207
9119
|
return this;
|
|
9208
9120
|
}
|
|
@@ -9222,7 +9134,7 @@ export class Document {
|
|
|
9222
9134
|
this.headerFooterManager.registerHeader(header, relationship.getId());
|
|
9223
9135
|
|
|
9224
9136
|
// Link to section
|
|
9225
|
-
this.section.setHeaderReference(
|
|
9137
|
+
this.section.setHeaderReference('even', relationship.getId());
|
|
9226
9138
|
|
|
9227
9139
|
return this;
|
|
9228
9140
|
}
|
|
@@ -9234,15 +9146,13 @@ export class Document {
|
|
|
9234
9146
|
*/
|
|
9235
9147
|
setFooter(footer: Footer): this {
|
|
9236
9148
|
// Generate relationship for footer
|
|
9237
|
-
const relationship = this.relationshipManager.addFooter(
|
|
9238
|
-
`${footer.getFilename(1)}`
|
|
9239
|
-
);
|
|
9149
|
+
const relationship = this.relationshipManager.addFooter(`${footer.getFilename(1)}`);
|
|
9240
9150
|
|
|
9241
9151
|
// Register with manager
|
|
9242
9152
|
this.headerFooterManager.registerFooter(footer, relationship.getId());
|
|
9243
9153
|
|
|
9244
9154
|
// Link to section
|
|
9245
|
-
this.section.setFooterReference(
|
|
9155
|
+
this.section.setFooterReference('default', relationship.getId());
|
|
9246
9156
|
|
|
9247
9157
|
return this;
|
|
9248
9158
|
}
|
|
@@ -9265,7 +9175,7 @@ export class Document {
|
|
|
9265
9175
|
this.headerFooterManager.registerFooter(footer, relationship.getId());
|
|
9266
9176
|
|
|
9267
9177
|
// Link to section
|
|
9268
|
-
this.section.setFooterReference(
|
|
9178
|
+
this.section.setFooterReference('first', relationship.getId());
|
|
9269
9179
|
|
|
9270
9180
|
return this;
|
|
9271
9181
|
}
|
|
@@ -9285,7 +9195,7 @@ export class Document {
|
|
|
9285
9195
|
this.headerFooterManager.registerFooter(footer, relationship.getId());
|
|
9286
9196
|
|
|
9287
9197
|
// Link to section
|
|
9288
|
-
this.section.setFooterReference(
|
|
9198
|
+
this.section.setFooterReference('even', relationship.getId());
|
|
9289
9199
|
|
|
9290
9200
|
return this;
|
|
9291
9201
|
}
|
|
@@ -9305,7 +9215,7 @@ export class Document {
|
|
|
9305
9215
|
* @param type Header type to remove (default, first, even)
|
|
9306
9216
|
* @returns This document for chaining
|
|
9307
9217
|
*/
|
|
9308
|
-
removeHeader(type:
|
|
9218
|
+
removeHeader(type: 'default' | 'first' | 'even'): this {
|
|
9309
9219
|
const sectionProps = this.section.getProperties();
|
|
9310
9220
|
|
|
9311
9221
|
// Get the relationship ID from section properties
|
|
@@ -9351,7 +9261,7 @@ export class Document {
|
|
|
9351
9261
|
* @param type Footer type to remove (default, first, even)
|
|
9352
9262
|
* @returns This document for chaining
|
|
9353
9263
|
*/
|
|
9354
|
-
removeFooter(type:
|
|
9264
|
+
removeFooter(type: 'default' | 'first' | 'even'): this {
|
|
9355
9265
|
const sectionProps = this.section.getProperties();
|
|
9356
9266
|
|
|
9357
9267
|
// Get the relationship ID from section properties
|
|
@@ -9400,7 +9310,7 @@ export class Document {
|
|
|
9400
9310
|
|
|
9401
9311
|
// Remove each header type
|
|
9402
9312
|
if (sectionProps.headers) {
|
|
9403
|
-
const types = Object.keys(sectionProps.headers) as (
|
|
9313
|
+
const types = Object.keys(sectionProps.headers) as ('default' | 'first' | 'even')[];
|
|
9404
9314
|
for (const type of types) {
|
|
9405
9315
|
this.removeHeader(type);
|
|
9406
9316
|
}
|
|
@@ -9422,7 +9332,7 @@ export class Document {
|
|
|
9422
9332
|
|
|
9423
9333
|
// Remove each footer type
|
|
9424
9334
|
if (sectionProps.footers) {
|
|
9425
|
-
const types = Object.keys(sectionProps.footers) as (
|
|
9335
|
+
const types = Object.keys(sectionProps.footers) as ('default' | 'first' | 'even')[];
|
|
9426
9336
|
for (const type of types) {
|
|
9427
9337
|
this.removeFooter(type);
|
|
9428
9338
|
}
|
|
@@ -9441,9 +9351,7 @@ export class Document {
|
|
|
9441
9351
|
*/
|
|
9442
9352
|
addImage(image: Image): this {
|
|
9443
9353
|
// Generate relationship ID
|
|
9444
|
-
const target = `media/image${
|
|
9445
|
-
this.imageManager.getImageCount() + 1
|
|
9446
|
-
}.${image.getExtension()}`;
|
|
9354
|
+
const target = `media/image${this.imageManager.getImageCount() + 1}.${image.getExtension()}`;
|
|
9447
9355
|
const relationship = this.relationshipManager.addImage(target);
|
|
9448
9356
|
|
|
9449
9357
|
// Register image with manager
|
|
@@ -9555,7 +9463,7 @@ export class Document {
|
|
|
9555
9463
|
*/
|
|
9556
9464
|
private updateRelationships(): void {
|
|
9557
9465
|
const xml = this.relationshipManager.generateXml();
|
|
9558
|
-
this.zipHandler.updateFile(
|
|
9466
|
+
this.zipHandler.updateFile('word/_rels/document.xml.rels', xml);
|
|
9559
9467
|
}
|
|
9560
9468
|
|
|
9561
9469
|
/**
|
|
@@ -9566,26 +9474,29 @@ export class Document {
|
|
|
9566
9474
|
// Comments were modified — regenerate from in-memory model
|
|
9567
9475
|
if (this.commentManager.getCount() > 0) {
|
|
9568
9476
|
const xml = this.commentManager.generateCommentsXml();
|
|
9569
|
-
this.zipHandler.addFile(
|
|
9477
|
+
this.zipHandler.addFile('word/comments.xml', xml);
|
|
9570
9478
|
|
|
9571
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9479
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9480
|
+
RelationshipType.COMMENTS
|
|
9481
|
+
);
|
|
9572
9482
|
if (existingRels.length === 0) {
|
|
9573
9483
|
this.relationshipManager.addComments();
|
|
9574
9484
|
}
|
|
9575
9485
|
} else {
|
|
9576
9486
|
// All comments removed — clean up comments.xml and its relationship
|
|
9577
|
-
this.zipHandler.removeFile(
|
|
9578
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9487
|
+
this.zipHandler.removeFile('word/comments.xml');
|
|
9488
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9489
|
+
RelationshipType.COMMENTS
|
|
9490
|
+
);
|
|
9579
9491
|
for (const rel of existingRels) {
|
|
9580
9492
|
this.relationshipManager.removeRelationship(rel.getId());
|
|
9581
9493
|
}
|
|
9582
9494
|
}
|
|
9583
9495
|
// Remove companion files since regenerated comments lack w14:paraId
|
|
9584
9496
|
this.removeCommentCompanionFiles();
|
|
9585
|
-
|
|
9586
9497
|
} else if (this._originalCommentsXml) {
|
|
9587
9498
|
// Passthrough — preserve original comments.xml exactly
|
|
9588
|
-
this.zipHandler.addFile(
|
|
9499
|
+
this.zipHandler.addFile('word/comments.xml', this._originalCommentsXml);
|
|
9589
9500
|
|
|
9590
9501
|
// Preserve companion files as-is
|
|
9591
9502
|
for (const [path, content] of this._originalCommentCompanionFiles) {
|
|
@@ -9593,17 +9504,20 @@ export class Document {
|
|
|
9593
9504
|
}
|
|
9594
9505
|
|
|
9595
9506
|
// Ensure comments relationship exists
|
|
9596
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9507
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9508
|
+
RelationshipType.COMMENTS
|
|
9509
|
+
);
|
|
9597
9510
|
if (existingRels.length === 0) {
|
|
9598
9511
|
this.relationshipManager.addComments();
|
|
9599
9512
|
}
|
|
9600
|
-
|
|
9601
9513
|
} else if (this.commentManager.getCount() > 0) {
|
|
9602
9514
|
// New comments created on a document that had no original comments
|
|
9603
9515
|
const xml = this.commentManager.generateCommentsXml();
|
|
9604
|
-
this.zipHandler.addFile(
|
|
9516
|
+
this.zipHandler.addFile('word/comments.xml', xml);
|
|
9605
9517
|
|
|
9606
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9518
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9519
|
+
RelationshipType.COMMENTS
|
|
9520
|
+
);
|
|
9607
9521
|
if (existingRels.length === 0) {
|
|
9608
9522
|
this.relationshipManager.addComments();
|
|
9609
9523
|
}
|
|
@@ -9611,7 +9525,11 @@ export class Document {
|
|
|
9611
9525
|
}
|
|
9612
9526
|
|
|
9613
9527
|
private removeCommentCompanionFiles(): void {
|
|
9614
|
-
const companionPaths = [
|
|
9528
|
+
const companionPaths = [
|
|
9529
|
+
DOCX_PATHS.COMMENTS_EXTENDED,
|
|
9530
|
+
DOCX_PATHS.COMMENTS_IDS,
|
|
9531
|
+
DOCX_PATHS.COMMENTS_EXTENSIBLE,
|
|
9532
|
+
];
|
|
9615
9533
|
for (const filePath of companionPaths) {
|
|
9616
9534
|
this.zipHandler.removeFile(filePath);
|
|
9617
9535
|
const fileName = filePath.replace('word/', '');
|
|
@@ -9628,7 +9546,9 @@ export class Document {
|
|
|
9628
9546
|
const xml = this.footnoteManager.generateFootnotesXml();
|
|
9629
9547
|
this.zipHandler.addFile(DOCX_PATHS.FOOTNOTES, xml);
|
|
9630
9548
|
|
|
9631
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9549
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9550
|
+
RelationshipType.FOOTNOTES
|
|
9551
|
+
);
|
|
9632
9552
|
if (existingRels.length === 0) {
|
|
9633
9553
|
this.relationshipManager.addFootnotes();
|
|
9634
9554
|
}
|
|
@@ -9636,14 +9556,16 @@ export class Document {
|
|
|
9636
9556
|
// Per OOXML, relationships are part-scoped: hyperlinks in footnotes.xml
|
|
9637
9557
|
// need word/_rels/footnotes.xml.rels, not word/_rels/document.xml.rels
|
|
9638
9558
|
this.generatePartLevelHyperlinkRels(
|
|
9639
|
-
this.footnoteManager.getAllFootnotes().flatMap(fn => fn.getParagraphs()),
|
|
9559
|
+
this.footnoteManager.getAllFootnotes().flatMap((fn) => fn.getParagraphs()),
|
|
9640
9560
|
'word/_rels/footnotes.xml.rels'
|
|
9641
9561
|
);
|
|
9642
9562
|
} else if (this._originalFootnotesXml) {
|
|
9643
9563
|
// Passthrough — preserve original XML exactly
|
|
9644
9564
|
this.zipHandler.addFile(DOCX_PATHS.FOOTNOTES, this._originalFootnotesXml);
|
|
9645
9565
|
|
|
9646
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9566
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9567
|
+
RelationshipType.FOOTNOTES
|
|
9568
|
+
);
|
|
9647
9569
|
if (existingRels.length === 0) {
|
|
9648
9570
|
this.relationshipManager.addFootnotes();
|
|
9649
9571
|
}
|
|
@@ -9655,7 +9577,9 @@ export class Document {
|
|
|
9655
9577
|
const xml = this.endnoteManager.generateEndnotesXml();
|
|
9656
9578
|
this.zipHandler.addFile(DOCX_PATHS.ENDNOTES, xml);
|
|
9657
9579
|
|
|
9658
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9580
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9581
|
+
RelationshipType.ENDNOTES
|
|
9582
|
+
);
|
|
9659
9583
|
if (existingRels.length === 0) {
|
|
9660
9584
|
this.relationshipManager.addEndnotes();
|
|
9661
9585
|
}
|
|
@@ -9663,14 +9587,16 @@ export class Document {
|
|
|
9663
9587
|
// Per OOXML, relationships are part-scoped: hyperlinks in endnotes.xml
|
|
9664
9588
|
// need word/_rels/endnotes.xml.rels, not word/_rels/document.xml.rels
|
|
9665
9589
|
this.generatePartLevelHyperlinkRels(
|
|
9666
|
-
this.endnoteManager.getAllEndnotes().flatMap(en => en.getParagraphs()),
|
|
9590
|
+
this.endnoteManager.getAllEndnotes().flatMap((en) => en.getParagraphs()),
|
|
9667
9591
|
'word/_rels/endnotes.xml.rels'
|
|
9668
9592
|
);
|
|
9669
9593
|
} else if (this._originalEndnotesXml) {
|
|
9670
9594
|
// Passthrough — preserve original XML exactly
|
|
9671
9595
|
this.zipHandler.addFile(DOCX_PATHS.ENDNOTES, this._originalEndnotesXml);
|
|
9672
9596
|
|
|
9673
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9597
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9598
|
+
RelationshipType.ENDNOTES
|
|
9599
|
+
);
|
|
9674
9600
|
if (existingRels.length === 0) {
|
|
9675
9601
|
this.relationshipManager.addEndnotes();
|
|
9676
9602
|
}
|
|
@@ -9733,7 +9659,8 @@ export class Document {
|
|
|
9733
9659
|
|
|
9734
9660
|
if (hyperlinkRels.length > 0) {
|
|
9735
9661
|
let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
|
|
9736
|
-
xml +=
|
|
9662
|
+
xml +=
|
|
9663
|
+
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n';
|
|
9737
9664
|
for (const rel of hyperlinkRels) {
|
|
9738
9665
|
const escapedId = XMLBuilder.escapeXmlAttribute(rel.id);
|
|
9739
9666
|
const escapedUrl = XMLBuilder.escapeXmlAttribute(rel.url);
|
|
@@ -9814,14 +9741,23 @@ export class Document {
|
|
|
9814
9741
|
if (missingAuthors.length === 0) return;
|
|
9815
9742
|
|
|
9816
9743
|
// Build person elements for missing authors
|
|
9817
|
-
const personElements = missingAuthors
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9744
|
+
const personElements = missingAuthors
|
|
9745
|
+
.map((author) => {
|
|
9746
|
+
const escapedAuthor = author
|
|
9747
|
+
.replace(/&/g, '&')
|
|
9748
|
+
.replace(/"/g, '"')
|
|
9749
|
+
.replace(/</g, '<')
|
|
9750
|
+
.replace(/>/g, '>');
|
|
9751
|
+
return `<w15:person w15:author="${escapedAuthor}"><w15:presenceInfo w15:providerId="None" w15:userId="${escapedAuthor}"/></w15:person>`;
|
|
9752
|
+
})
|
|
9753
|
+
.join('');
|
|
9821
9754
|
|
|
9822
9755
|
if (existingPeopleXml) {
|
|
9823
9756
|
// Insert new person elements before closing tag
|
|
9824
|
-
const updatedXml = existingPeopleXml.replace(
|
|
9757
|
+
const updatedXml = existingPeopleXml.replace(
|
|
9758
|
+
'</w15:people>',
|
|
9759
|
+
`${personElements}</w15:people>`
|
|
9760
|
+
);
|
|
9825
9761
|
this.zipHandler.updateFile('word/people.xml', updatedXml);
|
|
9826
9762
|
} else {
|
|
9827
9763
|
// Create new people.xml
|
|
@@ -9836,7 +9772,10 @@ export class Document {
|
|
|
9836
9772
|
this.relationshipManager.addPeople();
|
|
9837
9773
|
}
|
|
9838
9774
|
|
|
9839
|
-
this.logger.info('Updated people.xml with missing authors', {
|
|
9775
|
+
this.logger.info('Updated people.xml with missing authors', {
|
|
9776
|
+
added: missingAuthors.length,
|
|
9777
|
+
authors: missingAuthors,
|
|
9778
|
+
});
|
|
9840
9779
|
}
|
|
9841
9780
|
|
|
9842
9781
|
/**
|
|
@@ -9880,11 +9819,9 @@ export class Document {
|
|
|
9880
9819
|
this.properties.customProperties &&
|
|
9881
9820
|
Object.keys(this.properties.customProperties).length > 0
|
|
9882
9821
|
) {
|
|
9883
|
-
const customXml = this.generator.generateCustomProps(
|
|
9884
|
-
this.properties.customProperties
|
|
9885
|
-
);
|
|
9822
|
+
const customXml = this.generator.generateCustomProps(this.properties.customProperties);
|
|
9886
9823
|
if (customXml) {
|
|
9887
|
-
this.zipHandler.addFile(
|
|
9824
|
+
this.zipHandler.addFile('docProps/custom.xml', customXml);
|
|
9888
9825
|
}
|
|
9889
9826
|
}
|
|
9890
9827
|
}
|
|
@@ -9901,13 +9838,17 @@ export class Document {
|
|
|
9901
9838
|
const overrides = new Set<string>();
|
|
9902
9839
|
|
|
9903
9840
|
// Extract all <Default Extension="..." ContentType="..."/> entries
|
|
9904
|
-
const defaultMatches = xml.matchAll(
|
|
9841
|
+
const defaultMatches = xml.matchAll(
|
|
9842
|
+
/<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"\s*\/>/g
|
|
9843
|
+
);
|
|
9905
9844
|
for (const match of defaultMatches) {
|
|
9906
9845
|
defaults.add(`${match[1]}|${match[2]}`); // Store as "ext|mimetype"
|
|
9907
9846
|
}
|
|
9908
9847
|
|
|
9909
9848
|
// Extract all <Override PartName="..." ContentType="..."/> entries
|
|
9910
|
-
const overrideMatches = xml.matchAll(
|
|
9849
|
+
const overrideMatches = xml.matchAll(
|
|
9850
|
+
/<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"\s*\/>/g
|
|
9851
|
+
);
|
|
9911
9852
|
for (const match of overrideMatches) {
|
|
9912
9853
|
overrides.add(`${match[1]}|${match[2]}`); // Store as "path|mimetype"
|
|
9913
9854
|
}
|
|
@@ -9921,21 +9862,19 @@ export class Document {
|
|
|
9921
9862
|
*/
|
|
9922
9863
|
private updateContentTypesWithImagesHeadersFootersAndComments(): void {
|
|
9923
9864
|
const hasCustomProps =
|
|
9924
|
-
this.properties.customProperties &&
|
|
9925
|
-
|
|
9926
|
-
|
|
9927
|
-
|
|
9928
|
-
this.
|
|
9929
|
-
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
|
|
9936
|
-
|
|
9937
|
-
this.endnoteManager
|
|
9938
|
-
);
|
|
9865
|
+
this.properties.customProperties && Object.keys(this.properties.customProperties).length > 0;
|
|
9866
|
+
|
|
9867
|
+
const contentTypes = this.generator.generateContentTypesWithImagesHeadersFootersAndComments(
|
|
9868
|
+
this.imageManager,
|
|
9869
|
+
this.headerFooterManager,
|
|
9870
|
+
this.commentManager,
|
|
9871
|
+
this.zipHandler, // Pass zipHandler to check file existence
|
|
9872
|
+
undefined, // fontManager (optional)
|
|
9873
|
+
hasCustomProps, // Flag to include custom.xml override
|
|
9874
|
+
this._originalContentTypes, // Pass preserved original entries for round-trip fidelity
|
|
9875
|
+
this.footnoteManager,
|
|
9876
|
+
this.endnoteManager
|
|
9877
|
+
);
|
|
9939
9878
|
this.zipHandler.updateFile(DOCX_PATHS.CONTENT_TYPES, contentTypes);
|
|
9940
9879
|
}
|
|
9941
9880
|
|
|
@@ -10016,14 +9955,9 @@ export class Document {
|
|
|
10016
9955
|
* @param bookmarkOrName - Bookmark object or bookmark name
|
|
10017
9956
|
* @returns The bookmark that was added
|
|
10018
9957
|
*/
|
|
10019
|
-
addBookmarkToParagraph(
|
|
10020
|
-
paragraph: Paragraph,
|
|
10021
|
-
bookmarkOrName: Bookmark | string
|
|
10022
|
-
): Bookmark {
|
|
9958
|
+
addBookmarkToParagraph(paragraph: Paragraph, bookmarkOrName: Bookmark | string): Bookmark {
|
|
10023
9959
|
const bookmark =
|
|
10024
|
-
typeof bookmarkOrName ===
|
|
10025
|
-
? this.createBookmark(bookmarkOrName)
|
|
10026
|
-
: bookmarkOrName;
|
|
9960
|
+
typeof bookmarkOrName === 'string' ? this.createBookmark(bookmarkOrName) : bookmarkOrName;
|
|
10027
9961
|
|
|
10028
9962
|
paragraph.addBookmark(bookmark);
|
|
10029
9963
|
return bookmark;
|
|
@@ -10065,7 +9999,7 @@ export class Document {
|
|
|
10065
9999
|
anchor: string;
|
|
10066
10000
|
hyperlink: (text: string, formatting?: RunFormatting) => Hyperlink;
|
|
10067
10001
|
} {
|
|
10068
|
-
const BOOKMARK_NAME =
|
|
10002
|
+
const BOOKMARK_NAME = '_top';
|
|
10069
10003
|
|
|
10070
10004
|
// Check if _top bookmark already exists
|
|
10071
10005
|
let bookmark = this.getBookmark(BOOKMARK_NAME);
|
|
@@ -10223,10 +10157,10 @@ export class Document {
|
|
|
10223
10157
|
* }
|
|
10224
10158
|
* ```
|
|
10225
10159
|
*/
|
|
10226
|
-
validateAndFixRevisions(options?: {
|
|
10227
|
-
validation
|
|
10228
|
-
|
|
10229
|
-
}
|
|
10160
|
+
validateAndFixRevisions(options?: { validation?: ValidationOptions; autoFix?: AutoFixOptions }): {
|
|
10161
|
+
validation: ValidationResult;
|
|
10162
|
+
fix: AutoFixResult;
|
|
10163
|
+
} {
|
|
10230
10164
|
// First validate
|
|
10231
10165
|
const validation = this.validateRevisions(options?.validation);
|
|
10232
10166
|
|
|
@@ -10279,12 +10213,7 @@ export class Document {
|
|
|
10279
10213
|
* @param date - Optional date (defaults to now)
|
|
10280
10214
|
* @returns The created and registered revision
|
|
10281
10215
|
*/
|
|
10282
|
-
createRevisionFromText(
|
|
10283
|
-
type: RevisionType,
|
|
10284
|
-
author: string,
|
|
10285
|
-
text: string,
|
|
10286
|
-
date?: Date
|
|
10287
|
-
): Revision {
|
|
10216
|
+
createRevisionFromText(type: RevisionType, author: string, text: string, date?: Date): Revision {
|
|
10288
10217
|
const revision = Revision.fromText(type, author, text, date);
|
|
10289
10218
|
return this.revisionManager.register(revision);
|
|
10290
10219
|
}
|
|
@@ -10305,7 +10234,10 @@ export class Document {
|
|
|
10305
10234
|
* @param runIndex - Optional run index within the paragraph
|
|
10306
10235
|
* @returns RevisionLocation with paragraph index, or undefined if not found
|
|
10307
10236
|
*/
|
|
10308
|
-
private createRevisionLocation(
|
|
10237
|
+
private createRevisionLocation(
|
|
10238
|
+
paragraph: Paragraph,
|
|
10239
|
+
runIndex?: number
|
|
10240
|
+
): RevisionLocation | undefined {
|
|
10309
10241
|
const paragraphIndex = this.findParagraphIndex(paragraph);
|
|
10310
10242
|
if (paragraphIndex === -1) {
|
|
10311
10243
|
return undefined; // Paragraph not found in document
|
|
@@ -10325,13 +10257,8 @@ export class Document {
|
|
|
10325
10257
|
* @param date - Optional date (defaults to now)
|
|
10326
10258
|
* @returns The created revision with location set
|
|
10327
10259
|
*/
|
|
10328
|
-
trackInsertion(
|
|
10329
|
-
|
|
10330
|
-
author: string,
|
|
10331
|
-
text: string,
|
|
10332
|
-
date?: Date
|
|
10333
|
-
): Revision {
|
|
10334
|
-
const revision = this.createRevisionFromText("insert", author, text, date);
|
|
10260
|
+
trackInsertion(paragraph: Paragraph, author: string, text: string, date?: Date): Revision {
|
|
10261
|
+
const revision = this.createRevisionFromText('insert', author, text, date);
|
|
10335
10262
|
|
|
10336
10263
|
// Set location for changelog/tracking purposes
|
|
10337
10264
|
const location = this.createRevisionLocation(paragraph);
|
|
@@ -10353,13 +10280,8 @@ export class Document {
|
|
|
10353
10280
|
* @param date - Optional date (defaults to now)
|
|
10354
10281
|
* @returns The created revision with location set
|
|
10355
10282
|
*/
|
|
10356
|
-
trackDeletion(
|
|
10357
|
-
|
|
10358
|
-
author: string,
|
|
10359
|
-
text: string,
|
|
10360
|
-
date?: Date
|
|
10361
|
-
): Revision {
|
|
10362
|
-
const revision = this.createRevisionFromText("delete", author, text, date);
|
|
10283
|
+
trackDeletion(paragraph: Paragraph, author: string, text: string, date?: Date): Revision {
|
|
10284
|
+
const revision = this.createRevisionFromText('delete', author, text, date);
|
|
10363
10285
|
|
|
10364
10286
|
// Set location for changelog/tracking purposes
|
|
10365
10287
|
const location = this.createRevisionLocation(paragraph);
|
|
@@ -10424,11 +10346,7 @@ export class Document {
|
|
|
10424
10346
|
* doc.trackParagraphMarkDeletion(para, 'Alice');
|
|
10425
10347
|
* // In Word, this shows the ¶ symbol as deleted
|
|
10426
10348
|
*/
|
|
10427
|
-
trackParagraphMarkDeletion(
|
|
10428
|
-
paragraph: Paragraph,
|
|
10429
|
-
author: string,
|
|
10430
|
-
date?: Date
|
|
10431
|
-
): Paragraph {
|
|
10349
|
+
trackParagraphMarkDeletion(paragraph: Paragraph, author: string, date?: Date): Paragraph {
|
|
10432
10350
|
const revisionId = this.revisionManager.consumeNextId();
|
|
10433
10351
|
paragraph.markParagraphMarkAsDeleted(revisionId, author, date);
|
|
10434
10352
|
return paragraph;
|
|
@@ -10622,15 +10540,13 @@ export class Document {
|
|
|
10622
10540
|
this.trackFormatting = options.trackFormatting;
|
|
10623
10541
|
}
|
|
10624
10542
|
if (options.showInsertionsAndDeletions !== undefined) {
|
|
10625
|
-
this.revisionViewSettings.showInsertionsAndDeletions =
|
|
10626
|
-
options.showInsertionsAndDeletions;
|
|
10543
|
+
this.revisionViewSettings.showInsertionsAndDeletions = options.showInsertionsAndDeletions;
|
|
10627
10544
|
}
|
|
10628
10545
|
if (options.showFormatting !== undefined) {
|
|
10629
10546
|
this.revisionViewSettings.showFormatting = options.showFormatting;
|
|
10630
10547
|
}
|
|
10631
10548
|
if (options.showInkAnnotations !== undefined) {
|
|
10632
|
-
this.revisionViewSettings.showInkAnnotations =
|
|
10633
|
-
options.showInkAnnotations;
|
|
10549
|
+
this.revisionViewSettings.showInkAnnotations = options.showInkAnnotations;
|
|
10634
10550
|
}
|
|
10635
10551
|
}
|
|
10636
10552
|
|
|
@@ -10769,6 +10685,41 @@ export class Document {
|
|
|
10769
10685
|
}
|
|
10770
10686
|
// Bind tracking to section
|
|
10771
10687
|
this.bindTrackingToElement(this.section);
|
|
10688
|
+
|
|
10689
|
+
// Headers
|
|
10690
|
+
for (const entry of this.headerFooterManager.getAllHeaders()) {
|
|
10691
|
+
for (const element of entry.header.getElements()) {
|
|
10692
|
+
this.bindTrackingToElement(element);
|
|
10693
|
+
}
|
|
10694
|
+
}
|
|
10695
|
+
|
|
10696
|
+
// Footers
|
|
10697
|
+
for (const entry of this.headerFooterManager.getAllFooters()) {
|
|
10698
|
+
for (const element of entry.footer.getElements()) {
|
|
10699
|
+
this.bindTrackingToElement(element);
|
|
10700
|
+
}
|
|
10701
|
+
}
|
|
10702
|
+
|
|
10703
|
+
// Footnotes
|
|
10704
|
+
for (const footnote of this.footnoteManager.getAllFootnotes()) {
|
|
10705
|
+
for (const para of footnote.getParagraphs()) {
|
|
10706
|
+
this.bindTrackingToElement(para);
|
|
10707
|
+
}
|
|
10708
|
+
}
|
|
10709
|
+
|
|
10710
|
+
// Endnotes
|
|
10711
|
+
for (const endnote of this.endnoteManager.getAllEndnotes()) {
|
|
10712
|
+
for (const para of endnote.getParagraphs()) {
|
|
10713
|
+
this.bindTrackingToElement(para);
|
|
10714
|
+
}
|
|
10715
|
+
}
|
|
10716
|
+
|
|
10717
|
+
// Comments
|
|
10718
|
+
for (const comment of this.commentManager.getAllCommentsWithReplies()) {
|
|
10719
|
+
for (const run of comment.getRuns()) {
|
|
10720
|
+
this.bindTrackingToElement(run);
|
|
10721
|
+
}
|
|
10722
|
+
}
|
|
10772
10723
|
}
|
|
10773
10724
|
|
|
10774
10725
|
/**
|
|
@@ -10823,7 +10774,7 @@ export class Document {
|
|
|
10823
10774
|
setRsidRoot(rsidRoot: string): this {
|
|
10824
10775
|
// Validate RSID format (8 hex characters)
|
|
10825
10776
|
if (!/^[0-9A-Fa-f]{8}$/.test(rsidRoot)) {
|
|
10826
|
-
throw new Error(
|
|
10777
|
+
throw new Error('RSID must be an 8-character hexadecimal value');
|
|
10827
10778
|
}
|
|
10828
10779
|
this.rsidRoot = rsidRoot.toUpperCase();
|
|
10829
10780
|
this.rsids.add(this.rsidRoot);
|
|
@@ -10839,7 +10790,7 @@ export class Document {
|
|
|
10839
10790
|
addRsid(rsid: string): this {
|
|
10840
10791
|
// Validate RSID format
|
|
10841
10792
|
if (!/^[0-9A-Fa-f]{8}$/.test(rsid)) {
|
|
10842
|
-
throw new Error(
|
|
10793
|
+
throw new Error('RSID must be an 8-character hexadecimal value');
|
|
10843
10794
|
}
|
|
10844
10795
|
this.rsids.add(rsid.toUpperCase());
|
|
10845
10796
|
this._settingsModified = true;
|
|
@@ -10854,7 +10805,7 @@ export class Document {
|
|
|
10854
10805
|
const rsid = Math.floor(Math.random() * 0xffffffff)
|
|
10855
10806
|
.toString(16)
|
|
10856
10807
|
.toUpperCase()
|
|
10857
|
-
.padStart(8,
|
|
10808
|
+
.padStart(8, '0');
|
|
10858
10809
|
this.rsids.add(rsid);
|
|
10859
10810
|
this._settingsModified = true;
|
|
10860
10811
|
return rsid;
|
|
@@ -10881,7 +10832,7 @@ export class Document {
|
|
|
10881
10832
|
* @param protection - Document protection settings
|
|
10882
10833
|
*/
|
|
10883
10834
|
protectDocument(protection: {
|
|
10884
|
-
edit:
|
|
10835
|
+
edit: 'readOnly' | 'comments' | 'trackedChanges' | 'forms';
|
|
10885
10836
|
enforcement?: boolean;
|
|
10886
10837
|
password?: string;
|
|
10887
10838
|
cryptProviderType?: string;
|
|
@@ -10904,17 +10855,11 @@ export class Document {
|
|
|
10904
10855
|
// If password provided, generate hash and salt
|
|
10905
10856
|
if (protection.password) {
|
|
10906
10857
|
// For now, use a simple hash. In production, use proper cryptographic functions
|
|
10907
|
-
const crypto = require(
|
|
10908
|
-
const salt = crypto.randomBytes(16).toString(
|
|
10858
|
+
const crypto = require('crypto');
|
|
10859
|
+
const salt = crypto.randomBytes(16).toString('base64');
|
|
10909
10860
|
const hash = crypto
|
|
10910
|
-
.pbkdf2Sync(
|
|
10911
|
-
|
|
10912
|
-
salt,
|
|
10913
|
-
protection.cryptSpinCount || 100000,
|
|
10914
|
-
32,
|
|
10915
|
-
"sha512"
|
|
10916
|
-
)
|
|
10917
|
-
.toString("base64");
|
|
10861
|
+
.pbkdf2Sync(protection.password, salt, protection.cryptSpinCount || 100000, 32, 'sha512')
|
|
10862
|
+
.toString('base64');
|
|
10918
10863
|
|
|
10919
10864
|
this.documentProtection.hash = hash;
|
|
10920
10865
|
this.documentProtection.salt = salt;
|
|
@@ -10967,7 +10912,9 @@ export class Document {
|
|
|
10967
10912
|
* Gets the document background per ECMA-376 Part 1 §17.2.1
|
|
10968
10913
|
* @returns Background properties or undefined
|
|
10969
10914
|
*/
|
|
10970
|
-
getDocumentBackground():
|
|
10915
|
+
getDocumentBackground():
|
|
10916
|
+
| { color?: string; themeColor?: string; themeTint?: string; themeShade?: string }
|
|
10917
|
+
| undefined {
|
|
10971
10918
|
return this._documentBackground ? { ...this._documentBackground } : undefined;
|
|
10972
10919
|
}
|
|
10973
10920
|
|
|
@@ -10975,7 +10922,11 @@ export class Document {
|
|
|
10975
10922
|
* Sets the document background per ECMA-376 Part 1 §17.2.1
|
|
10976
10923
|
* @param background - Background properties (color, themeColor, etc.) or undefined to remove
|
|
10977
10924
|
*/
|
|
10978
|
-
setDocumentBackground(
|
|
10925
|
+
setDocumentBackground(
|
|
10926
|
+
background:
|
|
10927
|
+
| { color?: string; themeColor?: string; themeTint?: string; themeShade?: string }
|
|
10928
|
+
| undefined
|
|
10929
|
+
): void {
|
|
10979
10930
|
this._documentBackground = background ? { ...background } : undefined;
|
|
10980
10931
|
}
|
|
10981
10932
|
|
|
@@ -11196,12 +11147,14 @@ export class Document {
|
|
|
11196
11147
|
* @returns CompatibilityInfo with all parsed settings
|
|
11197
11148
|
*/
|
|
11198
11149
|
getCompatibilityInfo(): CompatibilityInfo {
|
|
11199
|
-
return
|
|
11200
|
-
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
|
|
11204
|
-
|
|
11150
|
+
return (
|
|
11151
|
+
this._compatInfo ?? {
|
|
11152
|
+
mode: CompatibilityMode.Word2007,
|
|
11153
|
+
isLegacyMode: true,
|
|
11154
|
+
compatSettings: [],
|
|
11155
|
+
legacyFlags: [],
|
|
11156
|
+
}
|
|
11157
|
+
);
|
|
11205
11158
|
}
|
|
11206
11159
|
|
|
11207
11160
|
/**
|
|
@@ -11281,7 +11234,7 @@ export class Document {
|
|
|
11281
11234
|
previousMode: currentMode,
|
|
11282
11235
|
newMode: 15,
|
|
11283
11236
|
removedFlags: [],
|
|
11284
|
-
addedSettings: MODERN_COMPAT_SETTINGS.map(s => s.name),
|
|
11237
|
+
addedSettings: MODERN_COMPAT_SETTINGS.map((s) => s.name),
|
|
11285
11238
|
namespacesExpanded: nsResult.expanded,
|
|
11286
11239
|
changed: currentMode !== CompatibilityMode.Word2013Plus,
|
|
11287
11240
|
};
|
|
@@ -11301,12 +11254,7 @@ export class Document {
|
|
|
11301
11254
|
previousProperties: Record<string, any>,
|
|
11302
11255
|
date?: Date
|
|
11303
11256
|
): Revision {
|
|
11304
|
-
const revision = Revision.createRunPropertiesChange(
|
|
11305
|
-
author,
|
|
11306
|
-
content,
|
|
11307
|
-
previousProperties,
|
|
11308
|
-
date
|
|
11309
|
-
);
|
|
11257
|
+
const revision = Revision.createRunPropertiesChange(author, content, previousProperties, date);
|
|
11310
11258
|
return this.revisionManager.register(revision);
|
|
11311
11259
|
}
|
|
11312
11260
|
|
|
@@ -11364,12 +11312,7 @@ export class Document {
|
|
|
11364
11312
|
* @param date - Optional date (defaults to now)
|
|
11365
11313
|
* @returns The created and registered revision
|
|
11366
11314
|
*/
|
|
11367
|
-
createMoveFrom(
|
|
11368
|
-
author: string,
|
|
11369
|
-
content: Run | Run[],
|
|
11370
|
-
moveId: string,
|
|
11371
|
-
date?: Date
|
|
11372
|
-
): Revision {
|
|
11315
|
+
createMoveFrom(author: string, content: Run | Run[], moveId: string, date?: Date): Revision {
|
|
11373
11316
|
const revision = Revision.createMoveFrom(author, content, moveId, date);
|
|
11374
11317
|
return this.revisionManager.register(revision);
|
|
11375
11318
|
}
|
|
@@ -11382,12 +11325,7 @@ export class Document {
|
|
|
11382
11325
|
* @param date - Optional date (defaults to now)
|
|
11383
11326
|
* @returns The created and registered revision
|
|
11384
11327
|
*/
|
|
11385
|
-
createMoveTo(
|
|
11386
|
-
author: string,
|
|
11387
|
-
content: Run | Run[],
|
|
11388
|
-
moveId: string,
|
|
11389
|
-
date?: Date
|
|
11390
|
-
): Revision {
|
|
11328
|
+
createMoveTo(author: string, content: Run | Run[], moveId: string, date?: Date): Revision {
|
|
11391
11329
|
const revision = Revision.createMoveTo(author, content, moveId, date);
|
|
11392
11330
|
return this.revisionManager.register(revision);
|
|
11393
11331
|
}
|
|
@@ -11413,9 +11351,7 @@ export class Document {
|
|
|
11413
11351
|
moveToRangeEnd: RangeMarker;
|
|
11414
11352
|
} {
|
|
11415
11353
|
// Generate unique move ID and name
|
|
11416
|
-
const moveId = `move${Date.now()}_${Math.random()
|
|
11417
|
-
.toString(36)
|
|
11418
|
-
.substr(2, 9)}`;
|
|
11354
|
+
const moveId = `move${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
11419
11355
|
const moveName = `move${Date.now()}`;
|
|
11420
11356
|
|
|
11421
11357
|
// Get unique IDs for range markers (use revision manager's next ID)
|
|
@@ -11431,12 +11367,7 @@ export class Document {
|
|
|
11431
11367
|
const moveFromRangeEnd = RangeMarker.createMoveFromEnd(rangeIdStart);
|
|
11432
11368
|
|
|
11433
11369
|
// Create range markers for moveTo
|
|
11434
|
-
const moveToRangeStart = RangeMarker.createMoveToStart(
|
|
11435
|
-
rangeIdStart,
|
|
11436
|
-
moveName,
|
|
11437
|
-
author,
|
|
11438
|
-
date
|
|
11439
|
-
);
|
|
11370
|
+
const moveToRangeStart = RangeMarker.createMoveToStart(rangeIdStart, moveName, author, date);
|
|
11440
11371
|
const moveToRangeEnd = RangeMarker.createMoveToEnd(rangeIdStart);
|
|
11441
11372
|
|
|
11442
11373
|
// Create the actual move revisions
|
|
@@ -11461,11 +11392,7 @@ export class Document {
|
|
|
11461
11392
|
* @param date - Optional date (defaults to now)
|
|
11462
11393
|
* @returns The created and registered revision
|
|
11463
11394
|
*/
|
|
11464
|
-
createTableCellInsert(
|
|
11465
|
-
author: string,
|
|
11466
|
-
content: Run | Run[],
|
|
11467
|
-
date?: Date
|
|
11468
|
-
): Revision {
|
|
11395
|
+
createTableCellInsert(author: string, content: Run | Run[], date?: Date): Revision {
|
|
11469
11396
|
const revision = Revision.createTableCellInsert(author, content, date);
|
|
11470
11397
|
return this.revisionManager.register(revision);
|
|
11471
11398
|
}
|
|
@@ -11477,11 +11404,7 @@ export class Document {
|
|
|
11477
11404
|
* @param date - Optional date (defaults to now)
|
|
11478
11405
|
* @returns The created and registered revision
|
|
11479
11406
|
*/
|
|
11480
|
-
createTableCellDelete(
|
|
11481
|
-
author: string,
|
|
11482
|
-
content: Run | Run[],
|
|
11483
|
-
date?: Date
|
|
11484
|
-
): Revision {
|
|
11407
|
+
createTableCellDelete(author: string, content: Run | Run[], date?: Date): Revision {
|
|
11485
11408
|
const revision = Revision.createTableCellDelete(author, content, date);
|
|
11486
11409
|
return this.revisionManager.register(revision);
|
|
11487
11410
|
}
|
|
@@ -11493,11 +11416,7 @@ export class Document {
|
|
|
11493
11416
|
* @param date - Optional date (defaults to now)
|
|
11494
11417
|
* @returns The created and registered revision
|
|
11495
11418
|
*/
|
|
11496
|
-
createTableCellMerge(
|
|
11497
|
-
author: string,
|
|
11498
|
-
content: Run | Run[],
|
|
11499
|
-
date?: Date
|
|
11500
|
-
): Revision {
|
|
11419
|
+
createTableCellMerge(author: string, content: Run | Run[], date?: Date): Revision {
|
|
11501
11420
|
const revision = Revision.createTableCellMerge(author, content, date);
|
|
11502
11421
|
return this.revisionManager.register(revision);
|
|
11503
11422
|
}
|
|
@@ -11516,12 +11435,7 @@ export class Document {
|
|
|
11516
11435
|
previousProperties: Record<string, any>,
|
|
11517
11436
|
date?: Date
|
|
11518
11437
|
): Revision {
|
|
11519
|
-
const revision = Revision.createNumberingChange(
|
|
11520
|
-
author,
|
|
11521
|
-
content,
|
|
11522
|
-
previousProperties,
|
|
11523
|
-
date
|
|
11524
|
-
);
|
|
11438
|
+
const revision = Revision.createNumberingChange(author, content, previousProperties, date);
|
|
11525
11439
|
return this.revisionManager.register(revision);
|
|
11526
11440
|
}
|
|
11527
11441
|
|
|
@@ -11575,11 +11489,7 @@ export class Document {
|
|
|
11575
11489
|
* const comment = doc.createComment('Alice', run, 'A');
|
|
11576
11490
|
* ```
|
|
11577
11491
|
*/
|
|
11578
|
-
createComment(
|
|
11579
|
-
author: string,
|
|
11580
|
-
content: string | Run | Run[],
|
|
11581
|
-
initials?: string
|
|
11582
|
-
): Comment {
|
|
11492
|
+
createComment(author: string, content: string | Run | Run[], initials?: string): Comment {
|
|
11583
11493
|
this._commentsModified = true;
|
|
11584
11494
|
return this.commentManager.createComment(author, content, initials);
|
|
11585
11495
|
}
|
|
@@ -11599,12 +11509,7 @@ export class Document {
|
|
|
11599
11509
|
initials?: string
|
|
11600
11510
|
): Comment {
|
|
11601
11511
|
this._commentsModified = true;
|
|
11602
|
-
return this.commentManager.createReply(
|
|
11603
|
-
parentCommentId,
|
|
11604
|
-
author,
|
|
11605
|
-
content,
|
|
11606
|
-
initials
|
|
11607
|
-
);
|
|
11512
|
+
return this.commentManager.createReply(parentCommentId, author, content, initials);
|
|
11608
11513
|
}
|
|
11609
11514
|
|
|
11610
11515
|
/**
|
|
@@ -11676,7 +11581,7 @@ export class Document {
|
|
|
11676
11581
|
initials?: string
|
|
11677
11582
|
): Comment {
|
|
11678
11583
|
const comment =
|
|
11679
|
-
typeof commentOrAuthor ===
|
|
11584
|
+
typeof commentOrAuthor === 'string'
|
|
11680
11585
|
? this.createComment(commentOrAuthor, content!, initials)
|
|
11681
11586
|
: commentOrAuthor;
|
|
11682
11587
|
|
|
@@ -11719,9 +11624,7 @@ export class Document {
|
|
|
11719
11624
|
* @param commentId - ID of the top-level comment
|
|
11720
11625
|
* @returns Object with the comment and its replies, or undefined if not found
|
|
11721
11626
|
*/
|
|
11722
|
-
getCommentThread(
|
|
11723
|
-
commentId: number
|
|
11724
|
-
): { comment: Comment; replies: Comment[] } | undefined {
|
|
11627
|
+
getCommentThread(commentId: number): { comment: Comment; replies: Comment[] } | undefined {
|
|
11725
11628
|
return this.commentManager.getCommentThread(commentId);
|
|
11726
11629
|
}
|
|
11727
11630
|
|
|
@@ -12000,16 +11903,16 @@ export class Document {
|
|
|
12000
11903
|
|
|
12001
11904
|
/**
|
|
12002
11905
|
* Strips all tracked changes from the document
|
|
12003
|
-
*
|
|
11906
|
+
*
|
|
12004
11907
|
* Removes all revision markup (<w:ins>, <w:del>, <w:moveFrom>, <w:moveTo>) from the document's XML
|
|
12005
|
-
* and cleans up related metadata. This effectively "accepts" all changes without using Word's
|
|
11908
|
+
* and cleans up related metadata. This effectively "accepts" all changes without using Word's
|
|
12006
11909
|
* built-in Accept Changes feature.
|
|
12007
|
-
*
|
|
11910
|
+
*
|
|
12008
11911
|
* **IMPORTANT**: This operation:
|
|
12009
11912
|
* 1. Modifies the raw XML in the ZIP package to remove all tracked changes
|
|
12010
11913
|
* 2. Clears Revision objects from the in-memory object model to prevent re-serialization
|
|
12011
11914
|
* 3. Sets flag to prevent XML regeneration on save (preserves the cleaned XML)
|
|
12012
|
-
*
|
|
11915
|
+
*
|
|
12013
11916
|
* What gets removed:
|
|
12014
11917
|
* - All insertion markers (<w:ins>) - content is kept, wrapper removed
|
|
12015
11918
|
* - All deletion markers (<w:del>) - entire element including content removed
|
|
@@ -12019,22 +11922,22 @@ export class Document {
|
|
|
12019
11922
|
* - Revision authors from word/people.xml
|
|
12020
11923
|
* - Track changes settings from word/settings.xml
|
|
12021
11924
|
* - Revision count from docProps/core.xml
|
|
12022
|
-
*
|
|
11925
|
+
*
|
|
12023
11926
|
* @returns This document instance for method chaining
|
|
12024
|
-
*
|
|
11927
|
+
*
|
|
12025
11928
|
* @example
|
|
12026
11929
|
* ```typescript
|
|
12027
11930
|
* // Load document with tracked changes
|
|
12028
11931
|
* const doc = await Document.load('document-with-revisions.docx');
|
|
12029
|
-
*
|
|
11932
|
+
*
|
|
12030
11933
|
* // Strip all tracked changes
|
|
12031
11934
|
* await doc.stripTrackedChanges();
|
|
12032
|
-
*
|
|
11935
|
+
*
|
|
12033
11936
|
* // Now process the document as normal
|
|
12034
11937
|
* doc.applyStyles();
|
|
12035
11938
|
* await doc.save('cleaned.docx');
|
|
12036
11939
|
* ```
|
|
12037
|
-
*
|
|
11940
|
+
*
|
|
12038
11941
|
* @example
|
|
12039
11942
|
* ```typescript
|
|
12040
11943
|
* // Check for tracked changes first
|
|
@@ -12045,7 +11948,7 @@ export class Document {
|
|
|
12045
11948
|
* }
|
|
12046
11949
|
* await doc.save('output.docx');
|
|
12047
11950
|
* ```
|
|
12048
|
-
*
|
|
11951
|
+
*
|
|
12049
11952
|
* @deprecated Use {@link acceptAllRevisions} instead - this method will be removed in a future version
|
|
12050
11953
|
*/
|
|
12051
11954
|
async stripTrackedChanges(): Promise<this> {
|
|
@@ -12055,7 +11958,7 @@ export class Document {
|
|
|
12055
11958
|
|
|
12056
11959
|
/**
|
|
12057
11960
|
* Accepts all tracked changes in the document
|
|
12058
|
-
*
|
|
11961
|
+
*
|
|
12059
11962
|
* Processes all revision markup following Microsoft's official OpenXML SDK approach:
|
|
12060
11963
|
* - Insertions (<w:ins>): Keep the inserted content, remove wrapper tags
|
|
12061
11964
|
* - Deletions (<w:del>): Remove entirely (content was deleted, so discard it)
|
|
@@ -12063,32 +11966,32 @@ export class Document {
|
|
|
12063
11966
|
* - Move To (<w:moveTo>): Keep content, remove wrapper (destination of moved content)
|
|
12064
11967
|
* - Property changes: Remove all tracking elements
|
|
12065
11968
|
* - Range markers: Remove all boundary markers
|
|
12066
|
-
*
|
|
11969
|
+
*
|
|
12067
11970
|
* Also cleans up metadata:
|
|
12068
11971
|
* - Revision authors from word/people.xml
|
|
12069
11972
|
* - Track changes settings from word/settings.xml
|
|
12070
11973
|
* - Revision count from docProps/core.xml
|
|
12071
|
-
*
|
|
11974
|
+
*
|
|
12072
11975
|
* **IMPORTANT**: This operation:
|
|
12073
11976
|
* 1. Modifies the raw XML in the ZIP package
|
|
12074
11977
|
* 2. Clears Revision objects from the in-memory object model
|
|
12075
11978
|
* 3. Sets flag to prevent XML regeneration on save (preserves the cleaned XML)
|
|
12076
|
-
*
|
|
11979
|
+
*
|
|
12077
11980
|
* @returns This document instance for method chaining
|
|
12078
|
-
*
|
|
11981
|
+
*
|
|
12079
11982
|
* @example
|
|
12080
11983
|
* ```typescript
|
|
12081
11984
|
* // Load document with tracked changes
|
|
12082
11985
|
* const doc = await Document.load('document-with-revisions.docx');
|
|
12083
|
-
*
|
|
11986
|
+
*
|
|
12084
11987
|
* // Accept all tracked changes
|
|
12085
11988
|
* await doc.acceptAllRevisions();
|
|
12086
|
-
*
|
|
11989
|
+
*
|
|
12087
11990
|
* // Now process the document as normal
|
|
12088
11991
|
* doc.applyStyles();
|
|
12089
11992
|
* await doc.save('cleaned.docx');
|
|
12090
11993
|
* ```
|
|
12091
|
-
*
|
|
11994
|
+
*
|
|
12092
11995
|
* @example
|
|
12093
11996
|
* ```typescript
|
|
12094
11997
|
* // Check for tracked changes first
|
|
@@ -12099,7 +12002,7 @@ export class Document {
|
|
|
12099
12002
|
* }
|
|
12100
12003
|
* await doc.save('output.docx');
|
|
12101
12004
|
* ```
|
|
12102
|
-
*
|
|
12005
|
+
*
|
|
12103
12006
|
* @see https://learn.microsoft.com/en-us/office/open-xml/how-to-accept-all-revisions
|
|
12104
12007
|
*/
|
|
12105
12008
|
async acceptAllRevisions(): Promise<this> {
|
|
@@ -12172,7 +12075,10 @@ export class Document {
|
|
|
12172
12075
|
* await doc.save('output.docx');
|
|
12173
12076
|
* ```
|
|
12174
12077
|
*/
|
|
12175
|
-
consolidateAllRevisions(timeWindowMs = 1000): {
|
|
12078
|
+
consolidateAllRevisions(timeWindowMs = 1000): {
|
|
12079
|
+
paragraphsProcessed: number;
|
|
12080
|
+
revisionsConsolidated: number;
|
|
12081
|
+
} {
|
|
12176
12082
|
let paragraphsProcessed = 0;
|
|
12177
12083
|
let totalConsolidated = 0;
|
|
12178
12084
|
|
|
@@ -12295,16 +12201,16 @@ export class Document {
|
|
|
12295
12201
|
*/
|
|
12296
12202
|
private clearRevisionsFromAllParagraphs(): void {
|
|
12297
12203
|
let clearedCount = 0;
|
|
12298
|
-
|
|
12204
|
+
|
|
12299
12205
|
// Clear revisions from all paragraphs in the document
|
|
12300
12206
|
for (const para of this.getAllParagraphs()) {
|
|
12301
12207
|
const revisions = para.getRevisions();
|
|
12302
|
-
|
|
12208
|
+
|
|
12303
12209
|
if (revisions.length > 0) {
|
|
12304
12210
|
// Filter out all Revision objects from paragraph content
|
|
12305
12211
|
const content = para.getContent();
|
|
12306
|
-
const nonRevisionContent = content.filter(item => !(item instanceof Revision));
|
|
12307
|
-
|
|
12212
|
+
const nonRevisionContent = content.filter((item) => !(item instanceof Revision));
|
|
12213
|
+
|
|
12308
12214
|
// Replace paragraph content with filtered version
|
|
12309
12215
|
para.clearContent();
|
|
12310
12216
|
for (const item of nonRevisionContent) {
|
|
@@ -12316,11 +12222,11 @@ export class Document {
|
|
|
12316
12222
|
para.addField(item);
|
|
12317
12223
|
}
|
|
12318
12224
|
}
|
|
12319
|
-
|
|
12225
|
+
|
|
12320
12226
|
clearedCount += revisions.length;
|
|
12321
12227
|
}
|
|
12322
12228
|
}
|
|
12323
|
-
|
|
12229
|
+
|
|
12324
12230
|
if (clearedCount > 0) {
|
|
12325
12231
|
this.logger.info(`Cleared ${clearedCount} Revision object(s) from in-memory document model`);
|
|
12326
12232
|
}
|
|
@@ -12563,7 +12469,7 @@ export class Document {
|
|
|
12563
12469
|
// ZipWriter stores all content as Buffer internally, but DocumentPart expects string for text
|
|
12564
12470
|
let content: string | Buffer = file.content;
|
|
12565
12471
|
if (!file.isBinary && Buffer.isBuffer(file.content)) {
|
|
12566
|
-
content = file.content.toString(
|
|
12472
|
+
content = file.content.toString('utf-8');
|
|
12567
12473
|
}
|
|
12568
12474
|
|
|
12569
12475
|
return {
|
|
@@ -12641,7 +12547,7 @@ export class Document {
|
|
|
12641
12547
|
}
|
|
12642
12548
|
}
|
|
12643
12549
|
// Remove relationships targeting this part from _rels/.rels
|
|
12644
|
-
const relsXml = this.zipHandler.getFileAsString(
|
|
12550
|
+
const relsXml = this.zipHandler.getFileAsString('_rels/.rels');
|
|
12645
12551
|
if (relsXml) {
|
|
12646
12552
|
const target = partName.replace(/^\//, '');
|
|
12647
12553
|
const relPattern = new RegExp(
|
|
@@ -12650,7 +12556,7 @@ export class Document {
|
|
|
12650
12556
|
);
|
|
12651
12557
|
const cleaned = relsXml.replace(relPattern, '');
|
|
12652
12558
|
if (cleaned !== relsXml) {
|
|
12653
|
-
this.zipHandler.updateFile(
|
|
12559
|
+
this.zipHandler.updateFile('_rels/.rels', cleaned);
|
|
12654
12560
|
}
|
|
12655
12561
|
}
|
|
12656
12562
|
// Track removed parts to skip regeneration during save
|
|
@@ -12716,17 +12622,14 @@ export class Document {
|
|
|
12716
12622
|
const contentTypes = new Map<string, string>();
|
|
12717
12623
|
|
|
12718
12624
|
try {
|
|
12719
|
-
const contentTypesXml = this.zipHandler.getFileAsString(
|
|
12720
|
-
"[Content_Types].xml"
|
|
12721
|
-
);
|
|
12625
|
+
const contentTypesXml = this.zipHandler.getFileAsString('[Content_Types].xml');
|
|
12722
12626
|
if (!contentTypesXml) {
|
|
12723
12627
|
return contentTypes;
|
|
12724
12628
|
}
|
|
12725
12629
|
|
|
12726
12630
|
// Parse content types XML
|
|
12727
12631
|
// Match Default elements (by extension)
|
|
12728
|
-
const defaultPattern =
|
|
12729
|
-
/<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"/g;
|
|
12632
|
+
const defaultPattern = /<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"/g;
|
|
12730
12633
|
let match;
|
|
12731
12634
|
while ((match = defaultPattern.exec(contentTypesXml)) !== null) {
|
|
12732
12635
|
if (match[1] && match[2]) {
|
|
@@ -12735,8 +12638,7 @@ export class Document {
|
|
|
12735
12638
|
}
|
|
12736
12639
|
|
|
12737
12640
|
// Match Override elements (by part name)
|
|
12738
|
-
const overridePattern =
|
|
12739
|
-
/<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"/g;
|
|
12641
|
+
const overridePattern = /<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"/g;
|
|
12740
12642
|
while ((match = overridePattern.exec(contentTypesXml)) !== null) {
|
|
12741
12643
|
if (match[1] && match[2]) {
|
|
12742
12644
|
contentTypes.set(match[1], match[2]);
|
|
@@ -12783,13 +12685,13 @@ export class Document {
|
|
|
12783
12685
|
}
|
|
12784
12686
|
|
|
12785
12687
|
// If already a string, return as-is
|
|
12786
|
-
if (typeof part.content ===
|
|
12688
|
+
if (typeof part.content === 'string') {
|
|
12787
12689
|
return part.content;
|
|
12788
12690
|
}
|
|
12789
12691
|
|
|
12790
12692
|
// If Buffer, decode as UTF-8 (standard for XML files)
|
|
12791
12693
|
if (Buffer.isBuffer(part.content)) {
|
|
12792
|
-
return part.content.toString(
|
|
12694
|
+
return part.content.toString('utf8');
|
|
12793
12695
|
}
|
|
12794
12696
|
|
|
12795
12697
|
return null;
|
|
@@ -12876,8 +12778,8 @@ export class Document {
|
|
|
12876
12778
|
* ```
|
|
12877
12779
|
*/
|
|
12878
12780
|
async setRawXml(partName: string, xmlContent: string): Promise<void> {
|
|
12879
|
-
if (typeof xmlContent !==
|
|
12880
|
-
throw new Error(
|
|
12781
|
+
if (typeof xmlContent !== 'string') {
|
|
12782
|
+
throw new Error('XML content must be a string');
|
|
12881
12783
|
}
|
|
12882
12784
|
|
|
12883
12785
|
// Use setPart to update the part (handles both string and binary detection)
|
|
@@ -12903,19 +12805,14 @@ export class Document {
|
|
|
12903
12805
|
* await doc.addContentType('.json', 'application/json');
|
|
12904
12806
|
* ```
|
|
12905
12807
|
*/
|
|
12906
|
-
async addContentType(
|
|
12907
|
-
partNameOrExtension: string,
|
|
12908
|
-
contentType: string
|
|
12909
|
-
): Promise<boolean> {
|
|
12808
|
+
async addContentType(partNameOrExtension: string, contentType: string): Promise<boolean> {
|
|
12910
12809
|
try {
|
|
12911
|
-
let contentTypesXml = this.zipHandler.getFileAsString(
|
|
12912
|
-
"[Content_Types].xml"
|
|
12913
|
-
);
|
|
12810
|
+
let contentTypesXml = this.zipHandler.getFileAsString('[Content_Types].xml');
|
|
12914
12811
|
if (!contentTypesXml) {
|
|
12915
12812
|
return false;
|
|
12916
12813
|
}
|
|
12917
12814
|
|
|
12918
|
-
const isExtension = partNameOrExtension.startsWith(
|
|
12815
|
+
const isExtension = partNameOrExtension.startsWith('.');
|
|
12919
12816
|
|
|
12920
12817
|
if (isExtension) {
|
|
12921
12818
|
// Add as Default element (for extensions)
|
|
@@ -12924,7 +12821,7 @@ export class Document {
|
|
|
12924
12821
|
// Check if already exists
|
|
12925
12822
|
const existingPattern = new RegExp(
|
|
12926
12823
|
`<Default\\s+Extension="${extension}"\\s+ContentType="[^"]+"/?>`,
|
|
12927
|
-
|
|
12824
|
+
'g'
|
|
12928
12825
|
);
|
|
12929
12826
|
if (existingPattern.test(contentTypesXml)) {
|
|
12930
12827
|
// Update existing
|
|
@@ -12935,13 +12832,13 @@ export class Document {
|
|
|
12935
12832
|
} else {
|
|
12936
12833
|
// Add new before closing tag
|
|
12937
12834
|
contentTypesXml = contentTypesXml.replace(
|
|
12938
|
-
|
|
12835
|
+
'</Types>',
|
|
12939
12836
|
` <Default Extension="${extension}" ContentType="${contentType}"/>\n</Types>`
|
|
12940
12837
|
);
|
|
12941
12838
|
}
|
|
12942
12839
|
} else {
|
|
12943
12840
|
// Add as Override element (for specific parts)
|
|
12944
|
-
const partName = partNameOrExtension.startsWith(
|
|
12841
|
+
const partName = partNameOrExtension.startsWith('/')
|
|
12945
12842
|
? partNameOrExtension
|
|
12946
12843
|
: `/${partNameOrExtension}`;
|
|
12947
12844
|
|
|
@@ -12949,9 +12846,9 @@ export class Document {
|
|
|
12949
12846
|
const existingPattern = new RegExp(
|
|
12950
12847
|
`<Override\\s+PartName="${partName.replace(
|
|
12951
12848
|
/[.*+?^${}()|[\]\\]/g,
|
|
12952
|
-
|
|
12849
|
+
'\\$&'
|
|
12953
12850
|
)}"\\s+ContentType="[^"]+"/?>`,
|
|
12954
|
-
|
|
12851
|
+
'g'
|
|
12955
12852
|
);
|
|
12956
12853
|
if (existingPattern.test(contentTypesXml)) {
|
|
12957
12854
|
// Update existing
|
|
@@ -12962,14 +12859,14 @@ export class Document {
|
|
|
12962
12859
|
} else {
|
|
12963
12860
|
// Add new before closing tag
|
|
12964
12861
|
contentTypesXml = contentTypesXml.replace(
|
|
12965
|
-
|
|
12862
|
+
'</Types>',
|
|
12966
12863
|
` <Override PartName="${partName}" ContentType="${contentType}"/>\n</Types>`
|
|
12967
12864
|
);
|
|
12968
12865
|
}
|
|
12969
12866
|
}
|
|
12970
12867
|
|
|
12971
12868
|
// Update the content types file
|
|
12972
|
-
this.zipHandler.updateFile(
|
|
12869
|
+
this.zipHandler.updateFile('[Content_Types].xml', contentTypesXml);
|
|
12973
12870
|
return true;
|
|
12974
12871
|
} catch (error: unknown) {
|
|
12975
12872
|
return false;
|
|
@@ -12997,9 +12894,7 @@ export class Document {
|
|
|
12997
12894
|
|
|
12998
12895
|
try {
|
|
12999
12896
|
// Get all .rels files
|
|
13000
|
-
const relsPaths = this.zipHandler
|
|
13001
|
-
.getFilePaths()
|
|
13002
|
-
.filter((path) => path.endsWith(".rels"));
|
|
12897
|
+
const relsPaths = this.zipHandler.getFilePaths().filter((path) => path.endsWith('.rels'));
|
|
13003
12898
|
|
|
13004
12899
|
for (const relsPath of relsPaths) {
|
|
13005
12900
|
const relsContent = this.zipHandler.getFileAsString(relsPath);
|
|
@@ -13014,22 +12909,16 @@ export class Document {
|
|
|
13014
12909
|
const rels: ParsedRelationship[] = [];
|
|
13015
12910
|
|
|
13016
12911
|
// Use XMLParser to extract all Relationship elements
|
|
13017
|
-
const relationshipElements = XMLParser.extractElements(
|
|
13018
|
-
relsContent,
|
|
13019
|
-
"Relationship"
|
|
13020
|
-
);
|
|
12912
|
+
const relationshipElements = XMLParser.extractElements(relsContent, 'Relationship');
|
|
13021
12913
|
|
|
13022
12914
|
for (const relElement of relationshipElements) {
|
|
13023
12915
|
const rel: ParsedRelationship = {};
|
|
13024
12916
|
|
|
13025
12917
|
// Extract attributes using XMLParser
|
|
13026
|
-
const id = XMLParser.extractAttribute(relElement,
|
|
13027
|
-
const type = XMLParser.extractAttribute(relElement,
|
|
13028
|
-
const target = XMLParser.extractAttribute(relElement,
|
|
13029
|
-
const targetMode = XMLParser.extractAttribute(
|
|
13030
|
-
relElement,
|
|
13031
|
-
"TargetMode"
|
|
13032
|
-
);
|
|
12918
|
+
const id = XMLParser.extractAttribute(relElement, 'Id');
|
|
12919
|
+
const type = XMLParser.extractAttribute(relElement, 'Type');
|
|
12920
|
+
const target = XMLParser.extractAttribute(relElement, 'Target');
|
|
12921
|
+
const targetMode = XMLParser.extractAttribute(relElement, 'TargetMode');
|
|
13033
12922
|
|
|
13034
12923
|
if (id) rel.id = id;
|
|
13035
12924
|
if (type) rel.type = type;
|
|
@@ -13078,19 +12967,15 @@ export class Document {
|
|
|
13078
12967
|
*/
|
|
13079
12968
|
async getRelationships(
|
|
13080
12969
|
partName: string
|
|
13081
|
-
): Promise<
|
|
13082
|
-
{ id?: string; type?: string; target?: string; targetMode?: string }[]
|
|
13083
|
-
> {
|
|
12970
|
+
): Promise<{ id?: string; type?: string; target?: string; targetMode?: string }[]> {
|
|
13084
12971
|
try {
|
|
13085
12972
|
// Construct the .rels path from the part name
|
|
13086
12973
|
// For 'word/document.xml' -> 'word/_rels/document.xml.rels'
|
|
13087
|
-
const lastSlash = partName.lastIndexOf(
|
|
12974
|
+
const lastSlash = partName.lastIndexOf('/');
|
|
13088
12975
|
const relsPath =
|
|
13089
12976
|
lastSlash === -1
|
|
13090
12977
|
? `_rels/${partName}.rels`
|
|
13091
|
-
: `${partName.substring(0, lastSlash)}/_rels/${partName.substring(
|
|
13092
|
-
lastSlash + 1
|
|
13093
|
-
)}.rels`;
|
|
12978
|
+
: `${partName.substring(0, lastSlash)}/_rels/${partName.substring(lastSlash + 1)}.rels`;
|
|
13094
12979
|
|
|
13095
12980
|
const relsContent = this.zipHandler.getFileAsString(relsPath);
|
|
13096
12981
|
if (!relsContent) {
|
|
@@ -13107,19 +12992,16 @@ export class Document {
|
|
|
13107
12992
|
const relationships: ParsedRelationship[] = [];
|
|
13108
12993
|
|
|
13109
12994
|
// Use XMLParser to extract all Relationship elements
|
|
13110
|
-
const relationshipElements = XMLParser.extractElements(
|
|
13111
|
-
relsContent,
|
|
13112
|
-
"Relationship"
|
|
13113
|
-
);
|
|
12995
|
+
const relationshipElements = XMLParser.extractElements(relsContent, 'Relationship');
|
|
13114
12996
|
|
|
13115
12997
|
for (const relElement of relationshipElements) {
|
|
13116
12998
|
const rel: ParsedRelationship = {};
|
|
13117
12999
|
|
|
13118
13000
|
// Extract attributes using XMLParser
|
|
13119
|
-
const id = XMLParser.extractAttribute(relElement,
|
|
13120
|
-
const type = XMLParser.extractAttribute(relElement,
|
|
13121
|
-
const target = XMLParser.extractAttribute(relElement,
|
|
13122
|
-
const targetMode = XMLParser.extractAttribute(relElement,
|
|
13001
|
+
const id = XMLParser.extractAttribute(relElement, 'Id');
|
|
13002
|
+
const type = XMLParser.extractAttribute(relElement, 'Type');
|
|
13003
|
+
const target = XMLParser.extractAttribute(relElement, 'Target');
|
|
13004
|
+
const targetMode = XMLParser.extractAttribute(relElement, 'TargetMode');
|
|
13123
13005
|
|
|
13124
13006
|
if (id) rel.id = id;
|
|
13125
13007
|
if (type) rel.type = type;
|
|
@@ -13142,9 +13024,7 @@ export class Document {
|
|
|
13142
13024
|
*/
|
|
13143
13025
|
private getContentTypeForPart(partName: string): string | undefined {
|
|
13144
13026
|
try {
|
|
13145
|
-
const contentTypesXml = this.zipHandler.getFileAsString(
|
|
13146
|
-
"[Content_Types].xml"
|
|
13147
|
-
);
|
|
13027
|
+
const contentTypesXml = this.zipHandler.getFileAsString('[Content_Types].xml');
|
|
13148
13028
|
if (!contentTypesXml) {
|
|
13149
13029
|
return undefined;
|
|
13150
13030
|
}
|
|
@@ -13153,9 +13033,9 @@ export class Document {
|
|
|
13153
13033
|
const overridePattern = new RegExp(
|
|
13154
13034
|
`<Override\\s+PartName="${partName.replace(
|
|
13155
13035
|
/[.*+?^${}()|[\]\\]/g,
|
|
13156
|
-
|
|
13036
|
+
'\\$&'
|
|
13157
13037
|
)}"\\s+ContentType="([^"]+)"`,
|
|
13158
|
-
|
|
13038
|
+
'i'
|
|
13159
13039
|
);
|
|
13160
13040
|
const overrideMatch = contentTypesXml.match(overridePattern);
|
|
13161
13041
|
if (overrideMatch) {
|
|
@@ -13163,13 +13043,11 @@ export class Document {
|
|
|
13163
13043
|
}
|
|
13164
13044
|
|
|
13165
13045
|
// Check for extension default
|
|
13166
|
-
const ext = partName.substring(partName.lastIndexOf(
|
|
13046
|
+
const ext = partName.substring(partName.lastIndexOf('.'));
|
|
13167
13047
|
if (ext) {
|
|
13168
13048
|
const defaultPattern = new RegExp(
|
|
13169
|
-
`<Default\\s+Extension="${ext.substring(
|
|
13170
|
-
|
|
13171
|
-
)}"\\s+ContentType="([^"]+)"`,
|
|
13172
|
-
"i"
|
|
13049
|
+
`<Default\\s+Extension="${ext.substring(1)}"\\s+ContentType="([^"]+)"`,
|
|
13050
|
+
'i'
|
|
13173
13051
|
);
|
|
13174
13052
|
const defaultMatch = contentTypesXml.match(defaultPattern);
|
|
13175
13053
|
if (defaultMatch) {
|
|
@@ -13261,8 +13139,8 @@ export class Document {
|
|
|
13261
13139
|
if (wholeWord) {
|
|
13262
13140
|
// Create word boundary regex
|
|
13263
13141
|
const wordPattern = new RegExp(
|
|
13264
|
-
`\\b${searchText.replace(/[.*+?^${}()|[\]\\]/g,
|
|
13265
|
-
caseSensitive ?
|
|
13142
|
+
`\\b${searchText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`,
|
|
13143
|
+
caseSensitive ? 'g' : 'gi'
|
|
13266
13144
|
);
|
|
13267
13145
|
let match;
|
|
13268
13146
|
while ((match = wordPattern.exec(runText)) !== null) {
|
|
@@ -13278,9 +13156,7 @@ export class Document {
|
|
|
13278
13156
|
} else {
|
|
13279
13157
|
// Simple substring search
|
|
13280
13158
|
let startIndex = 0;
|
|
13281
|
-
while (
|
|
13282
|
-
(startIndex = compareText.indexOf(searchText, startIndex)) !== -1
|
|
13283
|
-
) {
|
|
13159
|
+
while ((startIndex = compareText.indexOf(searchText, startIndex)) !== -1) {
|
|
13284
13160
|
results.push({
|
|
13285
13161
|
paragraph,
|
|
13286
13162
|
paragraphIndex: pIndex,
|
|
@@ -13317,18 +13193,13 @@ export class Document {
|
|
|
13317
13193
|
const run = runs[rIndex];
|
|
13318
13194
|
if (!run) continue;
|
|
13319
13195
|
const runText = run.getText();
|
|
13320
|
-
const compareText = caseSensitive
|
|
13321
|
-
? runText
|
|
13322
|
-
: runText.toLowerCase();
|
|
13196
|
+
const compareText = caseSensitive ? runText : runText.toLowerCase();
|
|
13323
13197
|
|
|
13324
13198
|
if (wholeWord) {
|
|
13325
13199
|
// Create word boundary regex
|
|
13326
13200
|
const wordPattern = new RegExp(
|
|
13327
|
-
`\\b${searchText.replace(
|
|
13328
|
-
|
|
13329
|
-
"\\$&"
|
|
13330
|
-
)}\\b`,
|
|
13331
|
-
caseSensitive ? "g" : "gi"
|
|
13201
|
+
`\\b${searchText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`,
|
|
13202
|
+
caseSensitive ? 'g' : 'gi'
|
|
13332
13203
|
);
|
|
13333
13204
|
let match;
|
|
13334
13205
|
while ((match = wordPattern.exec(runText)) !== null) {
|
|
@@ -13344,12 +13215,7 @@ export class Document {
|
|
|
13344
13215
|
} else {
|
|
13345
13216
|
// Simple substring search
|
|
13346
13217
|
let startIndex = 0;
|
|
13347
|
-
while (
|
|
13348
|
-
(startIndex = compareText.indexOf(
|
|
13349
|
-
searchText,
|
|
13350
|
-
startIndex
|
|
13351
|
-
)) !== -1
|
|
13352
|
-
) {
|
|
13218
|
+
while ((startIndex = compareText.indexOf(searchText, startIndex)) !== -1) {
|
|
13353
13219
|
results.push({
|
|
13354
13220
|
paragraph,
|
|
13355
13221
|
paragraphIndex: -1, // Not in main body, in table
|
|
@@ -13392,12 +13258,9 @@ export class Document {
|
|
|
13392
13258
|
* }
|
|
13393
13259
|
* ```
|
|
13394
13260
|
*/
|
|
13395
|
-
findParagraphsByText(
|
|
13396
|
-
pattern: string | RegExp
|
|
13397
|
-
): { paragraph: Paragraph; matches: string[] }[] {
|
|
13261
|
+
findParagraphsByText(pattern: string | RegExp): { paragraph: Paragraph; matches: string[] }[] {
|
|
13398
13262
|
const results: { paragraph: Paragraph; matches: string[] }[] = [];
|
|
13399
|
-
const regex =
|
|
13400
|
-
typeof pattern === "string" ? new RegExp(pattern, "gi") : pattern;
|
|
13263
|
+
const regex = typeof pattern === 'string' ? new RegExp(pattern, 'gi') : pattern;
|
|
13401
13264
|
|
|
13402
13265
|
for (const paragraph of this.getAllParagraphs()) {
|
|
13403
13266
|
const text = paragraph.getText();
|
|
@@ -13457,13 +13320,13 @@ export class Document {
|
|
|
13457
13320
|
getRunsByColor(color: string): Run[] {
|
|
13458
13321
|
const results: Run[] = [];
|
|
13459
13322
|
// Normalize color - remove # and convert to uppercase
|
|
13460
|
-
const normalizedColor = color.replace(/^#/,
|
|
13323
|
+
const normalizedColor = color.replace(/^#/, '').toUpperCase();
|
|
13461
13324
|
|
|
13462
13325
|
for (const paragraph of this.getAllParagraphs()) {
|
|
13463
13326
|
for (const run of paragraph.getRuns()) {
|
|
13464
13327
|
const formatting = run.getFormatting();
|
|
13465
13328
|
if (formatting.color) {
|
|
13466
|
-
const runColor = formatting.color.replace(/^#/,
|
|
13329
|
+
const runColor = formatting.color.replace(/^#/, '').toUpperCase();
|
|
13467
13330
|
if (runColor === normalizedColor) {
|
|
13468
13331
|
results.push(run);
|
|
13469
13332
|
}
|
|
@@ -13636,7 +13499,7 @@ export class Document {
|
|
|
13636
13499
|
sizes.set(formatting.size, (sizes.get(formatting.size) || 0) + 1);
|
|
13637
13500
|
}
|
|
13638
13501
|
if (formatting.color) {
|
|
13639
|
-
const normalizedColor = formatting.color.toUpperCase().replace(/^#/,
|
|
13502
|
+
const normalizedColor = formatting.color.toUpperCase().replace(/^#/, '');
|
|
13640
13503
|
colors.set(normalizedColor, (colors.get(normalizedColor) || 0) + 1);
|
|
13641
13504
|
}
|
|
13642
13505
|
}
|
|
@@ -13736,8 +13599,8 @@ export class Document {
|
|
|
13736
13599
|
if (wholeWord) {
|
|
13737
13600
|
// Use word boundary regex for whole word replacement
|
|
13738
13601
|
const wordPattern = new RegExp(
|
|
13739
|
-
`\\b${find.replace(/[.*+?^${}()|[\]\\]/g,
|
|
13740
|
-
caseSensitive ?
|
|
13602
|
+
`\\b${find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`,
|
|
13603
|
+
caseSensitive ? 'g' : 'gi'
|
|
13741
13604
|
);
|
|
13742
13605
|
const matches = originalText.match(wordPattern);
|
|
13743
13606
|
if (matches) {
|
|
@@ -13747,8 +13610,8 @@ export class Document {
|
|
|
13747
13610
|
} else {
|
|
13748
13611
|
// Simple substring replacement
|
|
13749
13612
|
const searchPattern = new RegExp(
|
|
13750
|
-
find.replace(/[.*+?^${}()|[\]\\]/g,
|
|
13751
|
-
caseSensitive ?
|
|
13613
|
+
find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'),
|
|
13614
|
+
caseSensitive ? 'g' : 'gi'
|
|
13752
13615
|
);
|
|
13753
13616
|
const matches = originalText.match(searchPattern);
|
|
13754
13617
|
if (matches) {
|
|
@@ -13818,7 +13681,7 @@ export class Document {
|
|
|
13818
13681
|
caseSensitive = false,
|
|
13819
13682
|
wholeWord = false,
|
|
13820
13683
|
trackChanges = false,
|
|
13821
|
-
author =
|
|
13684
|
+
author = 'Unknown',
|
|
13822
13685
|
} = options || {};
|
|
13823
13686
|
|
|
13824
13687
|
let count = 0;
|
|
@@ -13826,17 +13689,15 @@ export class Document {
|
|
|
13826
13689
|
|
|
13827
13690
|
// Convert pattern to RegExp if it's a string
|
|
13828
13691
|
let regex: RegExp;
|
|
13829
|
-
if (typeof pattern ===
|
|
13692
|
+
if (typeof pattern === 'string') {
|
|
13830
13693
|
// Escape special regex characters
|
|
13831
|
-
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g,
|
|
13694
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
13832
13695
|
const boundaryPattern = wholeWord ? `\\b${escaped}\\b` : escaped;
|
|
13833
|
-
const flags = caseSensitive ?
|
|
13696
|
+
const flags = caseSensitive ? 'g' : 'gi';
|
|
13834
13697
|
regex = new RegExp(boundaryPattern, flags);
|
|
13835
13698
|
} else {
|
|
13836
13699
|
// Use provided RegExp, ensure global flag
|
|
13837
|
-
const flags = pattern.flags.includes(
|
|
13838
|
-
? pattern.flags
|
|
13839
|
-
: pattern.flags + "g";
|
|
13700
|
+
const flags = pattern.flags.includes('g') ? pattern.flags : pattern.flags + 'g';
|
|
13840
13701
|
regex = new RegExp(pattern.source, flags);
|
|
13841
13702
|
}
|
|
13842
13703
|
|
|
@@ -13864,15 +13725,23 @@ export class Document {
|
|
|
13864
13725
|
|
|
13865
13726
|
if (useGranular) {
|
|
13866
13727
|
for (const seg of segments) {
|
|
13867
|
-
if (seg.type ===
|
|
13728
|
+
if (seg.type === 'equal') {
|
|
13868
13729
|
newContent.push(new Run(seg.text, formatting));
|
|
13869
|
-
} else if (seg.type ===
|
|
13870
|
-
const delRev = Revision.createDeletion(
|
|
13730
|
+
} else if (seg.type === 'delete') {
|
|
13731
|
+
const delRev = Revision.createDeletion(
|
|
13732
|
+
author,
|
|
13733
|
+
new Run(seg.text, formatting),
|
|
13734
|
+
now
|
|
13735
|
+
);
|
|
13871
13736
|
this.revisionManager.register(delRev);
|
|
13872
13737
|
revisions.push(delRev);
|
|
13873
13738
|
newContent.push(delRev);
|
|
13874
|
-
} else if (seg.type ===
|
|
13875
|
-
const insRev = Revision.createInsertion(
|
|
13739
|
+
} else if (seg.type === 'insert') {
|
|
13740
|
+
const insRev = Revision.createInsertion(
|
|
13741
|
+
author,
|
|
13742
|
+
new Run(seg.text, formatting),
|
|
13743
|
+
now
|
|
13744
|
+
);
|
|
13876
13745
|
this.revisionManager.register(insRev);
|
|
13877
13746
|
revisions.push(insRev);
|
|
13878
13747
|
newContent.push(insRev);
|
|
@@ -13880,7 +13749,11 @@ export class Document {
|
|
|
13880
13749
|
}
|
|
13881
13750
|
} else {
|
|
13882
13751
|
// Whole-run delete + insert
|
|
13883
|
-
const deletion = Revision.createDeletion(
|
|
13752
|
+
const deletion = Revision.createDeletion(
|
|
13753
|
+
author,
|
|
13754
|
+
new Run(originalText, formatting),
|
|
13755
|
+
now
|
|
13756
|
+
);
|
|
13884
13757
|
this.revisionManager.register(deletion);
|
|
13885
13758
|
revisions.push(deletion);
|
|
13886
13759
|
newContent.push(deletion);
|
|
@@ -14006,7 +13879,7 @@ export class Document {
|
|
|
14006
13879
|
if (includeSpaces) {
|
|
14007
13880
|
totalChars += text.length;
|
|
14008
13881
|
} else {
|
|
14009
|
-
totalChars += text.replace(/\s/g,
|
|
13882
|
+
totalChars += text.replace(/\s/g, '').length;
|
|
14010
13883
|
}
|
|
14011
13884
|
}
|
|
14012
13885
|
|
|
@@ -14029,7 +13902,7 @@ export class Document {
|
|
|
14029
13902
|
if (includeSpaces) {
|
|
14030
13903
|
totalChars += text.length;
|
|
14031
13904
|
} else {
|
|
14032
|
-
totalChars += text.replace(/\s/g,
|
|
13905
|
+
totalChars += text.replace(/\s/g, '').length;
|
|
14033
13906
|
}
|
|
14034
13907
|
}
|
|
14035
13908
|
}
|
|
@@ -14047,7 +13920,7 @@ export class Document {
|
|
|
14047
13920
|
removeParagraph(paragraphOrIndex: Paragraph | number): boolean {
|
|
14048
13921
|
let index: number;
|
|
14049
13922
|
|
|
14050
|
-
if (typeof paragraphOrIndex ===
|
|
13923
|
+
if (typeof paragraphOrIndex === 'number') {
|
|
14051
13924
|
index = paragraphOrIndex;
|
|
14052
13925
|
} else {
|
|
14053
13926
|
// Find the index of the paragraph
|
|
@@ -14084,7 +13957,7 @@ export class Document {
|
|
|
14084
13957
|
removeTable(tableOrIndex: Table | number): boolean {
|
|
14085
13958
|
let index: number;
|
|
14086
13959
|
|
|
14087
|
-
if (typeof tableOrIndex ===
|
|
13960
|
+
if (typeof tableOrIndex === 'number') {
|
|
14088
13961
|
// If number provided, find the nth table
|
|
14089
13962
|
const tables = this.getTables();
|
|
14090
13963
|
if (tableOrIndex >= 0 && tableOrIndex < tables.length) {
|
|
@@ -14118,9 +13991,7 @@ export class Document {
|
|
|
14118
13991
|
private validateParagraph(paragraph: Paragraph): void {
|
|
14119
13992
|
// Type validation
|
|
14120
13993
|
if (!(paragraph instanceof Paragraph)) {
|
|
14121
|
-
throw new Error(
|
|
14122
|
-
"insertParagraphAt: parameter must be a Paragraph instance"
|
|
14123
|
-
);
|
|
13994
|
+
throw new Error('insertParagraphAt: parameter must be a Paragraph instance');
|
|
14124
13995
|
}
|
|
14125
13996
|
|
|
14126
13997
|
// Check for duplicate paragraph IDs
|
|
@@ -14163,19 +14034,19 @@ export class Document {
|
|
|
14163
14034
|
private validateTable(table: Table): void {
|
|
14164
14035
|
// Type validation
|
|
14165
14036
|
if (!(table instanceof Table)) {
|
|
14166
|
-
throw new Error(
|
|
14037
|
+
throw new Error('insertTableAt: parameter must be a Table instance');
|
|
14167
14038
|
}
|
|
14168
14039
|
|
|
14169
14040
|
// Content validation - table must have rows
|
|
14170
14041
|
const rows = table.getRows();
|
|
14171
14042
|
if (rows.length === 0) {
|
|
14172
|
-
throw new Error(
|
|
14043
|
+
throw new Error('insertTableAt: table must have at least one row');
|
|
14173
14044
|
}
|
|
14174
14045
|
|
|
14175
14046
|
// Check first row has cells (rows.length > 0 already checked above)
|
|
14176
14047
|
const firstRow = rows[0];
|
|
14177
14048
|
if (firstRow?.getCells().length === 0) {
|
|
14178
|
-
throw new Error(
|
|
14049
|
+
throw new Error('insertTableAt: table rows must have at least one cell');
|
|
14179
14050
|
}
|
|
14180
14051
|
|
|
14181
14052
|
// Warn about missing table styles
|
|
@@ -14195,27 +14066,25 @@ export class Document {
|
|
|
14195
14066
|
private validateToc(toc: TableOfContentsElement): void {
|
|
14196
14067
|
// Type validation
|
|
14197
14068
|
if (!(toc instanceof TableOfContentsElement)) {
|
|
14198
|
-
throw new Error(
|
|
14199
|
-
"insertTocAt: parameter must be a TableOfContentsElement instance"
|
|
14200
|
-
);
|
|
14069
|
+
throw new Error('insertTocAt: parameter must be a TableOfContentsElement instance');
|
|
14201
14070
|
}
|
|
14202
14071
|
|
|
14203
14072
|
// Check if document has heading styles for TOC to reference
|
|
14204
14073
|
const hasHeadings = [
|
|
14205
|
-
|
|
14206
|
-
|
|
14207
|
-
|
|
14208
|
-
|
|
14209
|
-
|
|
14210
|
-
|
|
14211
|
-
|
|
14212
|
-
|
|
14213
|
-
|
|
14074
|
+
'Heading1',
|
|
14075
|
+
'Heading2',
|
|
14076
|
+
'Heading3',
|
|
14077
|
+
'Heading4',
|
|
14078
|
+
'Heading5',
|
|
14079
|
+
'Heading6',
|
|
14080
|
+
'Heading7',
|
|
14081
|
+
'Heading8',
|
|
14082
|
+
'Heading9',
|
|
14214
14083
|
].some((style) => this.stylesManager.hasStyle(style));
|
|
14215
14084
|
|
|
14216
14085
|
if (!hasHeadings) {
|
|
14217
14086
|
defaultLogger.warn(
|
|
14218
|
-
|
|
14087
|
+
'No heading styles found in document. Table of Contents may not display entries correctly.'
|
|
14219
14088
|
);
|
|
14220
14089
|
}
|
|
14221
14090
|
}
|
|
@@ -14544,8 +14413,7 @@ export class Document {
|
|
|
14544
14413
|
* ```
|
|
14545
14414
|
*/
|
|
14546
14415
|
getHyperlinks(): { hyperlink: Hyperlink; paragraph: Paragraph }[] {
|
|
14547
|
-
const hyperlinks: { hyperlink: Hyperlink; paragraph: Paragraph }[] =
|
|
14548
|
-
[];
|
|
14416
|
+
const hyperlinks: { hyperlink: Hyperlink; paragraph: Paragraph }[] = [];
|
|
14549
14417
|
|
|
14550
14418
|
// Helper function to extract hyperlinks from paragraph content,
|
|
14551
14419
|
// including those inside Revision elements (w:ins, w:del, etc.)
|
|
@@ -14573,8 +14441,7 @@ export class Document {
|
|
|
14573
14441
|
for (const row of table.getRows()) {
|
|
14574
14442
|
for (const cell of row.getCells()) {
|
|
14575
14443
|
// TableCell has getParagraphs method
|
|
14576
|
-
const cellParagraphs =
|
|
14577
|
-
cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
14444
|
+
const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
14578
14445
|
for (const para of cellParagraphs) {
|
|
14579
14446
|
extractHyperlinksFromParagraph(para);
|
|
14580
14447
|
}
|
|
@@ -14611,7 +14478,9 @@ export class Document {
|
|
|
14611
14478
|
}
|
|
14612
14479
|
};
|
|
14613
14480
|
|
|
14614
|
-
const scanElement = (
|
|
14481
|
+
const scanElement = (
|
|
14482
|
+
element: BodyElement | Paragraph | Table | StructuredDocumentTag
|
|
14483
|
+
): void => {
|
|
14615
14484
|
if (element instanceof Paragraph) {
|
|
14616
14485
|
scanParagraph(element);
|
|
14617
14486
|
} else if (element instanceof Table) {
|
|
@@ -14697,8 +14566,7 @@ export class Document {
|
|
|
14697
14566
|
resetFormatting?: boolean;
|
|
14698
14567
|
cleanupRelationships?: boolean;
|
|
14699
14568
|
}): number {
|
|
14700
|
-
const { resetFormatting = false, cleanupRelationships = false } =
|
|
14701
|
-
options || {};
|
|
14569
|
+
const { resetFormatting = false, cleanupRelationships = false } = options || {};
|
|
14702
14570
|
|
|
14703
14571
|
// Guard: Skip when track changes is enabled - prevents field structure corruption
|
|
14704
14572
|
// The mergeConsecutiveHyperlinks() method uses clearContent() + addHyperlink()
|
|
@@ -14707,7 +14575,7 @@ export class Document {
|
|
|
14707
14575
|
if (this.trackChangesEnabled) {
|
|
14708
14576
|
defaultLogger.warn(
|
|
14709
14577
|
'defragmentHyperlinks skipped: track changes is enabled. ' +
|
|
14710
|
-
|
|
14578
|
+
'Call defragmentHyperlinks before enableTrackChanges() to avoid field corruption.'
|
|
14711
14579
|
);
|
|
14712
14580
|
return 0;
|
|
14713
14581
|
}
|
|
@@ -14736,8 +14604,7 @@ export class Document {
|
|
|
14736
14604
|
for (const table of this.getTables()) {
|
|
14737
14605
|
for (const row of table.getRows()) {
|
|
14738
14606
|
for (const cell of row.getCells()) {
|
|
14739
|
-
const cellParagraphs =
|
|
14740
|
-
cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
14607
|
+
const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
14741
14608
|
for (const para of cellParagraphs) {
|
|
14742
14609
|
const originalContent = para.getContent();
|
|
14743
14610
|
|
|
@@ -14759,12 +14626,9 @@ export class Document {
|
|
|
14759
14626
|
const referencedIds = this.collectAllReferencedHyperlinkIds();
|
|
14760
14627
|
|
|
14761
14628
|
// Remove orphaned hyperlink relationships
|
|
14762
|
-
const removedCount =
|
|
14763
|
-
this.relationshipManager.removeOrphanedHyperlinks(referencedIds);
|
|
14629
|
+
const removedCount = this.relationshipManager.removeOrphanedHyperlinks(referencedIds);
|
|
14764
14630
|
if (removedCount > 0) {
|
|
14765
|
-
defaultLogger.info(
|
|
14766
|
-
`Cleaned up ${removedCount} orphaned hyperlink relationship(s)`
|
|
14767
|
-
);
|
|
14631
|
+
defaultLogger.info(`Cleaned up ${removedCount} orphaned hyperlink relationship(s)`);
|
|
14768
14632
|
}
|
|
14769
14633
|
}
|
|
14770
14634
|
|
|
@@ -14848,8 +14712,7 @@ export class Document {
|
|
|
14848
14712
|
for (const table of this.getTables()) {
|
|
14849
14713
|
for (const row of table.getRows()) {
|
|
14850
14714
|
for (const cell of row.getCells()) {
|
|
14851
|
-
const cellParagraphs =
|
|
14852
|
-
cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
14715
|
+
const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
14853
14716
|
for (const para of cellParagraphs) {
|
|
14854
14717
|
for (const field of para.getFields()) {
|
|
14855
14718
|
results.push({ field, paragraph: para, table });
|
|
@@ -14910,7 +14773,10 @@ export class Document {
|
|
|
14910
14773
|
await this.imageManager.loadAllImageData();
|
|
14911
14774
|
|
|
14912
14775
|
// 2. Group images by filename (avoid processing same file twice)
|
|
14913
|
-
const imagesByFilename = new Map<
|
|
14776
|
+
const imagesByFilename = new Map<
|
|
14777
|
+
string,
|
|
14778
|
+
{ image: Image; relationshipId: string; filename: string }[]
|
|
14779
|
+
>();
|
|
14914
14780
|
for (const entry of this.imageManager.getAllImages()) {
|
|
14915
14781
|
const group = imagesByFilename.get(entry.filename) || [];
|
|
14916
14782
|
group.push(entry);
|
|
@@ -14968,7 +14834,11 @@ export class Document {
|
|
|
14968
14834
|
* Handles both document body and header/footer relationships.
|
|
14969
14835
|
* @private
|
|
14970
14836
|
*/
|
|
14971
|
-
private updateImageRelationshipTarget(
|
|
14837
|
+
private updateImageRelationshipTarget(
|
|
14838
|
+
relId: string,
|
|
14839
|
+
oldFilename: string,
|
|
14840
|
+
newFilename: string
|
|
14841
|
+
): void {
|
|
14972
14842
|
// Try document body relationship manager first
|
|
14973
14843
|
const rel = this.relationshipManager.getRelationship(relId);
|
|
14974
14844
|
if (rel) {
|
|
@@ -15029,8 +14899,7 @@ export class Document {
|
|
|
15029
14899
|
for (const table of this.getTables()) {
|
|
15030
14900
|
for (const row of table.getRows()) {
|
|
15031
14901
|
for (const cell of row.getCells()) {
|
|
15032
|
-
const cellParagraphs =
|
|
15033
|
-
cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
14902
|
+
const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
15034
14903
|
for (const para of cellParagraphs) {
|
|
15035
14904
|
runs.push(...para.getRuns());
|
|
15036
14905
|
}
|
|
@@ -15070,9 +14939,10 @@ export class Document {
|
|
|
15070
14939
|
* });
|
|
15071
14940
|
* ```
|
|
15072
14941
|
*/
|
|
15073
|
-
hyperlinkEmails(options?: {
|
|
15074
|
-
|
|
15075
|
-
|
|
14942
|
+
hyperlinkEmails(options?: { formatting?: RunFormatting }): {
|
|
14943
|
+
emailsLinked: number;
|
|
14944
|
+
paragraphsModified: number;
|
|
14945
|
+
} {
|
|
15076
14946
|
// Default formatting: Verdana 12pt, Underline, no bold, #0000FF
|
|
15077
14947
|
const defaultFormatting: RunFormatting = {
|
|
15078
14948
|
font: 'Verdana',
|
|
@@ -15189,23 +15059,23 @@ export class Document {
|
|
|
15189
15059
|
*/
|
|
15190
15060
|
removeFormattingFromAll(
|
|
15191
15061
|
type:
|
|
15192
|
-
|
|
|
15193
|
-
|
|
|
15194
|
-
|
|
|
15195
|
-
|
|
|
15196
|
-
|
|
|
15197
|
-
|
|
|
15198
|
-
|
|
|
15199
|
-
|
|
|
15200
|
-
|
|
|
15201
|
-
|
|
|
15202
|
-
|
|
|
15203
|
-
|
|
|
15204
|
-
|
|
|
15205
|
-
|
|
|
15206
|
-
|
|
|
15207
|
-
|
|
|
15208
|
-
|
|
|
15062
|
+
| 'bold'
|
|
15063
|
+
| 'italic'
|
|
15064
|
+
| 'underline'
|
|
15065
|
+
| 'strike'
|
|
15066
|
+
| 'dstrike'
|
|
15067
|
+
| 'highlight'
|
|
15068
|
+
| 'color'
|
|
15069
|
+
| 'font'
|
|
15070
|
+
| 'size'
|
|
15071
|
+
| 'subscript'
|
|
15072
|
+
| 'superscript'
|
|
15073
|
+
| 'smallCaps'
|
|
15074
|
+
| 'allCaps'
|
|
15075
|
+
| 'outline'
|
|
15076
|
+
| 'shadow'
|
|
15077
|
+
| 'emboss'
|
|
15078
|
+
| 'imprint'
|
|
15209
15079
|
): number {
|
|
15210
15080
|
let modifiedCount = 0;
|
|
15211
15081
|
|
|
@@ -15274,9 +15144,7 @@ export class Document {
|
|
|
15274
15144
|
* });
|
|
15275
15145
|
* ```
|
|
15276
15146
|
*/
|
|
15277
|
-
updateAllHyperlinks(
|
|
15278
|
-
formatter: (hyperlink: Hyperlink, paragraph: Paragraph) => void
|
|
15279
|
-
): number {
|
|
15147
|
+
updateAllHyperlinks(formatter: (hyperlink: Hyperlink, paragraph: Paragraph) => void): number {
|
|
15280
15148
|
// Get all hyperlinks with their containing paragraphs
|
|
15281
15149
|
const hyperlinks = this.getHyperlinks();
|
|
15282
15150
|
|
|
@@ -15349,7 +15217,7 @@ export class Document {
|
|
|
15349
15217
|
|
|
15350
15218
|
this.bodyElements.forEach((element, index) => {
|
|
15351
15219
|
if (element instanceof Paragraph) {
|
|
15352
|
-
const isEmpty = element.getText().trim() ===
|
|
15220
|
+
const isEmpty = element.getText().trim() === '';
|
|
15353
15221
|
if (isEmpty && lastWasEmpty) {
|
|
15354
15222
|
toRemove.push(index);
|
|
15355
15223
|
}
|
|
@@ -15380,7 +15248,7 @@ export class Document {
|
|
|
15380
15248
|
}
|
|
15381
15249
|
|
|
15382
15250
|
if (standardLineSpacing !== undefined) {
|
|
15383
|
-
para.setLineSpacing(standardLineSpacing,
|
|
15251
|
+
para.setLineSpacing(standardLineSpacing, 'auto');
|
|
15384
15252
|
normalized++;
|
|
15385
15253
|
}
|
|
15386
15254
|
|
|
@@ -15405,8 +15273,7 @@ export class Document {
|
|
|
15405
15273
|
for (const table of this.getTables()) {
|
|
15406
15274
|
for (const row of table.getRows()) {
|
|
15407
15275
|
for (const cell of row.getCells()) {
|
|
15408
|
-
const cellParagraphs =
|
|
15409
|
-
cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
15276
|
+
const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
15410
15277
|
for (const para of cellParagraphs) {
|
|
15411
15278
|
if (standardParagraphSpacing) {
|
|
15412
15279
|
if (standardParagraphSpacing.before !== undefined) {
|
|
@@ -15420,7 +15287,7 @@ export class Document {
|
|
|
15420
15287
|
}
|
|
15421
15288
|
|
|
15422
15289
|
if (standardLineSpacing !== undefined) {
|
|
15423
|
-
para.setLineSpacing(standardLineSpacing,
|
|
15290
|
+
para.setLineSpacing(standardLineSpacing, 'auto');
|
|
15424
15291
|
normalized++;
|
|
15425
15292
|
}
|
|
15426
15293
|
|
|
@@ -15509,13 +15376,13 @@ export class Document {
|
|
|
15509
15376
|
|
|
15510
15377
|
/**
|
|
15511
15378
|
* Rebuilds all Table of Contents in the document
|
|
15512
|
-
*
|
|
15379
|
+
*
|
|
15513
15380
|
* Analyzes each TOC in the document, parses its field instructions to determine
|
|
15514
15381
|
* which heading levels to include, searches for matching headings (including those
|
|
15515
15382
|
* in nested tables), and returns a summary of TOC instructions and heading counts.
|
|
15516
|
-
*
|
|
15383
|
+
*
|
|
15517
15384
|
* **NEW: This method now also populates the TOCs with hyperlinked entries automatically!**
|
|
15518
|
-
*
|
|
15385
|
+
*
|
|
15519
15386
|
* The method:
|
|
15520
15387
|
* 1. Removes SDT wrappers around tables if found (uses clearCustom helper)
|
|
15521
15388
|
* 2. Ensures `_top` bookmark exists at document start for TOC linking
|
|
@@ -15528,7 +15395,7 @@ export class Document {
|
|
|
15528
15395
|
* 9. **Updates document.xml with the populated TOC**
|
|
15529
15396
|
* 10. Retains field instructions so TOCs can be manually updated later
|
|
15530
15397
|
* 11. Returns summary: [instruction, [h1Count, h2Count, h3Count, ...]]
|
|
15531
|
-
*
|
|
15398
|
+
*
|
|
15532
15399
|
* **Key Features:**
|
|
15533
15400
|
* - No arguments required - analyzes the current document state
|
|
15534
15401
|
* - Searches nested tables when counting headings
|
|
@@ -15539,19 +15406,19 @@ export class Document {
|
|
|
15539
15406
|
* - **Field instructions preserved for manual updates**
|
|
15540
15407
|
* - **No page numbers displayed (pure hyperlink navigation)**
|
|
15541
15408
|
* - Returns summary data for diagnostics and verification
|
|
15542
|
-
*
|
|
15409
|
+
*
|
|
15543
15410
|
* **Output Format:**
|
|
15544
15411
|
* Returns a 2D array where each row contains:
|
|
15545
15412
|
* - Index 0: The TOC field instruction text (e.g., "TOC \\o \"1-3\"")
|
|
15546
15413
|
* - Index 1: Array of heading counts by level (e.g., [5, 12, 8] = 5 H1s, 12 H2s, 8 H3s)
|
|
15547
|
-
*
|
|
15414
|
+
*
|
|
15548
15415
|
* @returns Two-dimensional array of [instruction, headingCounts[]] for each TOC
|
|
15549
|
-
*
|
|
15416
|
+
*
|
|
15550
15417
|
* @example
|
|
15551
15418
|
* ```typescript
|
|
15552
15419
|
* const doc = await Document.load('document.docx');
|
|
15553
15420
|
* const tocInfo = doc.rebuildTOCs();
|
|
15554
|
-
*
|
|
15421
|
+
*
|
|
15555
15422
|
* console.log(`Found ${tocInfo.length} Table(s) of Contents`);
|
|
15556
15423
|
* for (const [instruction, counts] of tocInfo) {
|
|
15557
15424
|
* console.log(`TOC Instruction: ${instruction}`);
|
|
@@ -15561,59 +15428,59 @@ export class Document {
|
|
|
15561
15428
|
* }
|
|
15562
15429
|
* });
|
|
15563
15430
|
* }
|
|
15564
|
-
*
|
|
15431
|
+
*
|
|
15565
15432
|
* // TOCs are now populated with hyperlinks - save the document
|
|
15566
15433
|
* await doc.save('output.docx');
|
|
15567
15434
|
* // When opened in Word, TOCs will display with clickable links, no manual update needed
|
|
15568
15435
|
* ```
|
|
15569
|
-
*
|
|
15436
|
+
*
|
|
15570
15437
|
* @example
|
|
15571
15438
|
* ```typescript
|
|
15572
15439
|
* // Rebuild TOCs and save with populated entries
|
|
15573
15440
|
* const doc = await Document.load('input.docx');
|
|
15574
15441
|
* const tocSummary = doc.rebuildTOCs();
|
|
15575
15442
|
* await doc.save('output.docx');
|
|
15576
|
-
*
|
|
15443
|
+
*
|
|
15577
15444
|
* console.log(`Processed ${tocSummary.length} TOCs with hyperlinked entries`);
|
|
15578
15445
|
* ```
|
|
15579
15446
|
*/
|
|
15580
15447
|
public rebuildTOCs(): [string, number[]][] {
|
|
15581
15448
|
const results: [string, number[]][] = [];
|
|
15582
|
-
|
|
15449
|
+
|
|
15583
15450
|
// Step 1: Remove SDT wrappers around tables if found (helper already exists)
|
|
15584
15451
|
this.clearCustom();
|
|
15585
|
-
|
|
15452
|
+
|
|
15586
15453
|
// Step 2: Ensure _top bookmark exists at document start
|
|
15587
15454
|
this.addTopBookmark();
|
|
15588
|
-
|
|
15455
|
+
|
|
15589
15456
|
// Step 3: Get document.xml to scan for TOC elements
|
|
15590
15457
|
const docXml = this.zipHandler.getFileAsString('word/document.xml');
|
|
15591
15458
|
if (!docXml) {
|
|
15592
15459
|
return results;
|
|
15593
15460
|
}
|
|
15594
|
-
|
|
15461
|
+
|
|
15595
15462
|
// Step 4: Find all TOC SDT elements
|
|
15596
15463
|
const tocRegex = /<w:sdt>[\s\S]*?<w:docPartGallery w:val="Table of Contents"[\s\S]*?<\/w:sdt>/g;
|
|
15597
15464
|
const tocMatches = Array.from(docXml.matchAll(tocRegex));
|
|
15598
|
-
|
|
15465
|
+
|
|
15599
15466
|
if (tocMatches.length === 0) {
|
|
15600
15467
|
return results;
|
|
15601
15468
|
}
|
|
15602
|
-
|
|
15469
|
+
|
|
15603
15470
|
// Step 5: For each TOC, parse instructions and count headings
|
|
15604
15471
|
for (const match of tocMatches) {
|
|
15605
15472
|
try {
|
|
15606
15473
|
const tocXml = match[0];
|
|
15607
|
-
|
|
15474
|
+
|
|
15608
15475
|
// Extract field instruction
|
|
15609
15476
|
const instrMatch = /<w:instrText[^>]*>([\s\S]*?)<\/w:instrText>/.exec(tocXml);
|
|
15610
15477
|
if (!instrMatch?.[1]) {
|
|
15611
15478
|
continue;
|
|
15612
15479
|
}
|
|
15613
|
-
|
|
15480
|
+
|
|
15614
15481
|
// TypeScript type narrowing: assign to const variable
|
|
15615
15482
|
const instrText = instrMatch[1];
|
|
15616
|
-
|
|
15483
|
+
|
|
15617
15484
|
// Decode XML entities
|
|
15618
15485
|
const fieldInstruction = instrText
|
|
15619
15486
|
.replace(/&/g, '&')
|
|
@@ -15621,23 +15488,23 @@ export class Document {
|
|
|
15621
15488
|
.replace(/>/g, '>')
|
|
15622
15489
|
.replace(/"/g, '"')
|
|
15623
15490
|
.replace(/'/g, "'");
|
|
15624
|
-
|
|
15491
|
+
|
|
15625
15492
|
// Parse the instruction to get heading levels
|
|
15626
15493
|
const levels = this.parseTOCFieldInstruction(fieldInstruction);
|
|
15627
|
-
|
|
15494
|
+
|
|
15628
15495
|
// Find all headings in document (including nested tables)
|
|
15629
15496
|
const headings = this.findHeadingsForTOCFromXML(docXml, levels);
|
|
15630
|
-
|
|
15497
|
+
|
|
15631
15498
|
// Count headings by level (create array with counts for each level 1-9)
|
|
15632
15499
|
const headingCounts: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0]; // Indices 0-8 for levels 1-9
|
|
15633
|
-
|
|
15500
|
+
|
|
15634
15501
|
for (const heading of headings) {
|
|
15635
15502
|
if (heading.level >= 1 && heading.level <= 9) {
|
|
15636
15503
|
const index = heading.level - 1;
|
|
15637
15504
|
headingCounts[index] = (headingCounts[index] || 0) + 1;
|
|
15638
15505
|
}
|
|
15639
15506
|
}
|
|
15640
|
-
|
|
15507
|
+
|
|
15641
15508
|
// Add to results: [instruction, counts]
|
|
15642
15509
|
results.push([fieldInstruction, headingCounts]);
|
|
15643
15510
|
} catch (error: unknown) {
|
|
@@ -15651,20 +15518,18 @@ export class Document {
|
|
|
15651
15518
|
continue;
|
|
15652
15519
|
}
|
|
15653
15520
|
}
|
|
15654
|
-
|
|
15521
|
+
|
|
15655
15522
|
// Step 6: Populate all TOCs in the document with hyperlinked entries
|
|
15656
15523
|
// This modifies the XML to include pre-populated TOC entries with hyperlinks
|
|
15657
15524
|
const populatedXml = this.populateAllTOCsInXML(docXml);
|
|
15658
|
-
|
|
15525
|
+
|
|
15659
15526
|
// Step 7: Update document.xml with the populated TOCs
|
|
15660
15527
|
if (populatedXml !== docXml) {
|
|
15661
15528
|
this.zipHandler.updateFile('word/document.xml', populatedXml);
|
|
15662
|
-
|
|
15663
|
-
this.logger.info(
|
|
15664
|
-
`Successfully populated ${results.length} TOC(s) with hyperlinked entries`
|
|
15665
|
-
);
|
|
15529
|
+
|
|
15530
|
+
this.logger.info(`Successfully populated ${results.length} TOC(s) with hyperlinked entries`);
|
|
15666
15531
|
}
|
|
15667
|
-
|
|
15532
|
+
|
|
15668
15533
|
return results;
|
|
15669
15534
|
}
|
|
15670
15535
|
|
|
@@ -15696,14 +15561,14 @@ export class Document {
|
|
|
15696
15561
|
* ```
|
|
15697
15562
|
*/
|
|
15698
15563
|
normalizeTableBorders(options?: {
|
|
15699
|
-
style?:
|
|
15564
|
+
style?: 'single' | 'double' | 'dotted' | 'dashed' | 'thick' | 'none';
|
|
15700
15565
|
size?: number;
|
|
15701
15566
|
color?: string;
|
|
15702
15567
|
}): number {
|
|
15703
15568
|
const border: TableBorder = {
|
|
15704
|
-
style: options?.style ??
|
|
15569
|
+
style: options?.style ?? 'single',
|
|
15705
15570
|
size: options?.size ?? 4,
|
|
15706
|
-
color: options?.color ??
|
|
15571
|
+
color: options?.color ?? '000000',
|
|
15707
15572
|
};
|
|
15708
15573
|
|
|
15709
15574
|
return this.applyBordersToAllTables(border);
|
|
@@ -15754,8 +15619,8 @@ export class Document {
|
|
|
15754
15619
|
|
|
15755
15620
|
// Create regex pattern
|
|
15756
15621
|
const pattern = matchCase
|
|
15757
|
-
? new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g,
|
|
15758
|
-
: new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g,
|
|
15622
|
+
? new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
|
|
15623
|
+
: new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
|
|
15759
15624
|
|
|
15760
15625
|
// Process body paragraphs
|
|
15761
15626
|
for (const para of this.getAllParagraphs()) {
|
|
@@ -15839,36 +15704,36 @@ export class Document {
|
|
|
15839
15704
|
|
|
15840
15705
|
// [Content_Types].xml - minimal
|
|
15841
15706
|
zipHandler.addFile(
|
|
15842
|
-
|
|
15707
|
+
'[Content_Types].xml',
|
|
15843
15708
|
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
15844
15709
|
'<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">\n' +
|
|
15845
15710
|
' <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>\n' +
|
|
15846
15711
|
' <Default Extension="xml" ContentType="application/xml"/>\n' +
|
|
15847
15712
|
' <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>\n' +
|
|
15848
|
-
|
|
15713
|
+
'</Types>'
|
|
15849
15714
|
);
|
|
15850
15715
|
|
|
15851
15716
|
// _rels/.rels - only reference parts that actually exist
|
|
15852
15717
|
zipHandler.addFile(
|
|
15853
|
-
|
|
15718
|
+
'_rels/.rels',
|
|
15854
15719
|
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
15855
15720
|
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n' +
|
|
15856
15721
|
' <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>\n' +
|
|
15857
|
-
|
|
15722
|
+
'</Relationships>'
|
|
15858
15723
|
);
|
|
15859
15724
|
|
|
15860
15725
|
// word/document.xml - empty body
|
|
15861
15726
|
zipHandler.addFile(
|
|
15862
|
-
|
|
15727
|
+
'word/document.xml',
|
|
15863
15728
|
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
15864
15729
|
'<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">\n' +
|
|
15865
|
-
|
|
15866
|
-
|
|
15730
|
+
' <w:body/>\n' +
|
|
15731
|
+
'</w:document>'
|
|
15867
15732
|
);
|
|
15868
15733
|
|
|
15869
15734
|
// word/_rels/document.xml.rels - empty relationships
|
|
15870
15735
|
zipHandler.addFile(
|
|
15871
|
-
|
|
15736
|
+
'word/_rels/document.xml.rels',
|
|
15872
15737
|
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
15873
15738
|
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"/>'
|
|
15874
15739
|
);
|