docxmlater 10.1.3 → 10.1.5
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 +50 -50
- package/dist/core/Document.d.ts.map +1 -1
- package/dist/core/Document.js +483 -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.map +1 -1
- package/dist/formatting/NumberingManager.js +9 -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 +985 -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 +833 -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
|
|
|
@@ -5783,12 +5786,7 @@ export class Document {
|
|
|
5783
5786
|
leftIndent: number,
|
|
5784
5787
|
hangingIndent?: number
|
|
5785
5788
|
): this {
|
|
5786
|
-
this.numberingManager.setListIndentation(
|
|
5787
|
-
numId,
|
|
5788
|
-
level,
|
|
5789
|
-
leftIndent,
|
|
5790
|
-
hangingIndent
|
|
5791
|
-
);
|
|
5789
|
+
this.numberingManager.setListIndentation(numId, level, leftIndent, hangingIndent);
|
|
5792
5790
|
return this;
|
|
5793
5791
|
}
|
|
5794
5792
|
|
|
@@ -5838,14 +5836,13 @@ export class Document {
|
|
|
5838
5836
|
|
|
5839
5837
|
for (const instance of instances) {
|
|
5840
5838
|
const abstractNumId = instance.getAbstractNumId();
|
|
5841
|
-
const abstractNum =
|
|
5842
|
-
this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
5839
|
+
const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
5843
5840
|
|
|
5844
5841
|
if (!abstractNum) continue;
|
|
5845
5842
|
|
|
5846
5843
|
// Only process bullet lists (skip numbered lists)
|
|
5847
5844
|
const level0 = abstractNum.getLevel(0);
|
|
5848
|
-
if (level0?.getFormat() !==
|
|
5845
|
+
if (level0?.getFormat() !== 'bullet') continue;
|
|
5849
5846
|
|
|
5850
5847
|
// Update all 9 levels (0-8) with standard formatting
|
|
5851
5848
|
for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
|
|
@@ -5853,11 +5850,11 @@ export class Document {
|
|
|
5853
5850
|
if (!numLevel) continue;
|
|
5854
5851
|
|
|
5855
5852
|
// Alternate bullets: even levels = solid (•), odd levels = open (○)
|
|
5856
|
-
const bullet = levelIndex % 2 === 0 ?
|
|
5853
|
+
const bullet = levelIndex % 2 === 0 ? '•' : '○';
|
|
5857
5854
|
numLevel.setText(bullet);
|
|
5858
5855
|
|
|
5859
5856
|
// Set bullet font to Arial (Unicode bullets require a regular font, not Symbol)
|
|
5860
|
-
numLevel.setFont(
|
|
5857
|
+
numLevel.setFont('Arial');
|
|
5861
5858
|
|
|
5862
5859
|
// Set bullet size to 12pt (24 half-points)
|
|
5863
5860
|
numLevel.setFontSize(24);
|
|
@@ -5908,14 +5905,13 @@ export class Document {
|
|
|
5908
5905
|
|
|
5909
5906
|
for (const instance of instances) {
|
|
5910
5907
|
const abstractNumId = instance.getAbstractNumId();
|
|
5911
|
-
const abstractNum =
|
|
5912
|
-
this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
5908
|
+
const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
5913
5909
|
|
|
5914
5910
|
if (!abstractNum) continue;
|
|
5915
5911
|
|
|
5916
5912
|
// Only process numbered lists (skip bullet lists)
|
|
5917
5913
|
const level0 = abstractNum.getLevel(0);
|
|
5918
|
-
if (!level0 || level0.getFormat() ===
|
|
5914
|
+
if (!level0 || level0.getFormat() === 'bullet') continue;
|
|
5919
5915
|
|
|
5920
5916
|
// Update all 9 levels (0-8) with standard formatting
|
|
5921
5917
|
for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
|
|
@@ -5923,7 +5919,7 @@ export class Document {
|
|
|
5923
5919
|
if (!numLevel) continue;
|
|
5924
5920
|
|
|
5925
5921
|
// Set number font to Verdana 12pt
|
|
5926
|
-
numLevel.setFont(
|
|
5922
|
+
numLevel.setFont('Verdana');
|
|
5927
5923
|
numLevel.setFontSize(24); // 12pt = 24 half-points
|
|
5928
5924
|
|
|
5929
5925
|
// Indentation: 0.5" per level (720 twips)
|
|
@@ -5934,7 +5930,7 @@ export class Document {
|
|
|
5934
5930
|
numLevel.setHangingIndent(360);
|
|
5935
5931
|
|
|
5936
5932
|
// Set alignment to left
|
|
5937
|
-
numLevel.setAlignment(
|
|
5933
|
+
numLevel.setAlignment('left');
|
|
5938
5934
|
}
|
|
5939
5935
|
|
|
5940
5936
|
// Apply paragraph formatting to all paragraphs using this list
|
|
@@ -5960,7 +5956,7 @@ export class Document {
|
|
|
5960
5956
|
// Apply font to all runs in the paragraph
|
|
5961
5957
|
const runs = para.getRuns();
|
|
5962
5958
|
for (const run of runs) {
|
|
5963
|
-
run.setFont(
|
|
5959
|
+
run.setFont('Verdana', 12);
|
|
5964
5960
|
}
|
|
5965
5961
|
|
|
5966
5962
|
// Apply paragraph spacing
|
|
@@ -6024,7 +6020,7 @@ export class Document {
|
|
|
6024
6020
|
// Find the paragraph index in bodyElements
|
|
6025
6021
|
const paraIndex = this.bodyElements.indexOf(para);
|
|
6026
6022
|
if (paraIndex === -1) {
|
|
6027
|
-
throw new Error(
|
|
6023
|
+
throw new Error('Paragraph not found in document body elements');
|
|
6028
6024
|
}
|
|
6029
6025
|
|
|
6030
6026
|
// Create 1x1 table
|
|
@@ -6032,7 +6028,7 @@ export class Document {
|
|
|
6032
6028
|
const cell = table.getCell(0, 0);
|
|
6033
6029
|
|
|
6034
6030
|
if (!cell) {
|
|
6035
|
-
throw new Error(
|
|
6031
|
+
throw new Error('Failed to get cell from newly created table');
|
|
6036
6032
|
}
|
|
6037
6033
|
|
|
6038
6034
|
// Move paragraph to cell
|
|
@@ -6064,7 +6060,7 @@ export class Document {
|
|
|
6064
6060
|
// Set table width (percentage of page width)
|
|
6065
6061
|
if (options.tableWidthPercent !== undefined) {
|
|
6066
6062
|
table.setWidth(options.tableWidthPercent);
|
|
6067
|
-
table.setWidthType(
|
|
6063
|
+
table.setWidthType('pct');
|
|
6068
6064
|
}
|
|
6069
6065
|
|
|
6070
6066
|
// Insert table where paragraph was
|
|
@@ -6076,30 +6072,30 @@ export class Document {
|
|
|
6076
6072
|
// Default style configurations for applyStyles()
|
|
6077
6073
|
private static readonly DEFAULT_HEADING1_CONFIG: StyleConfig = {
|
|
6078
6074
|
run: {
|
|
6079
|
-
font:
|
|
6075
|
+
font: 'Verdana',
|
|
6080
6076
|
size: 18,
|
|
6081
6077
|
bold: true,
|
|
6082
|
-
color:
|
|
6078
|
+
color: '000000',
|
|
6083
6079
|
},
|
|
6084
6080
|
paragraph: {
|
|
6085
|
-
alignment:
|
|
6086
|
-
spacing: { before: 0, after: 240, line: 240, lineRule:
|
|
6081
|
+
alignment: 'left',
|
|
6082
|
+
spacing: { before: 0, after: 240, line: 240, lineRule: 'auto' },
|
|
6087
6083
|
},
|
|
6088
6084
|
};
|
|
6089
6085
|
|
|
6090
6086
|
private static readonly DEFAULT_HEADING2_CONFIG: Heading2Config = {
|
|
6091
6087
|
run: {
|
|
6092
|
-
font:
|
|
6088
|
+
font: 'Verdana',
|
|
6093
6089
|
size: 14,
|
|
6094
6090
|
bold: true,
|
|
6095
|
-
color:
|
|
6091
|
+
color: '000000',
|
|
6096
6092
|
},
|
|
6097
6093
|
paragraph: {
|
|
6098
|
-
alignment:
|
|
6099
|
-
spacing: { before: 120, after: 120, line: 240, lineRule:
|
|
6094
|
+
alignment: 'left',
|
|
6095
|
+
spacing: { before: 120, after: 120, line: 240, lineRule: 'auto' },
|
|
6100
6096
|
},
|
|
6101
6097
|
tableOptions: {
|
|
6102
|
-
shading:
|
|
6098
|
+
shading: 'BFBFBF',
|
|
6103
6099
|
marginTop: 0,
|
|
6104
6100
|
marginBottom: 0,
|
|
6105
6101
|
marginLeft: 115,
|
|
@@ -6110,38 +6106,38 @@ export class Document {
|
|
|
6110
6106
|
|
|
6111
6107
|
private static readonly DEFAULT_HEADING3_CONFIG: StyleConfig = {
|
|
6112
6108
|
run: {
|
|
6113
|
-
font:
|
|
6109
|
+
font: 'Verdana',
|
|
6114
6110
|
size: 12,
|
|
6115
6111
|
bold: true,
|
|
6116
|
-
color:
|
|
6112
|
+
color: '000000',
|
|
6117
6113
|
},
|
|
6118
6114
|
paragraph: {
|
|
6119
|
-
alignment:
|
|
6120
|
-
spacing: { before: 60, after: 60, line: 240, lineRule:
|
|
6115
|
+
alignment: 'left',
|
|
6116
|
+
spacing: { before: 60, after: 60, line: 240, lineRule: 'auto' },
|
|
6121
6117
|
},
|
|
6122
6118
|
};
|
|
6123
6119
|
|
|
6124
6120
|
private static readonly DEFAULT_NORMAL_CONFIG: StyleConfig = {
|
|
6125
6121
|
run: {
|
|
6126
|
-
font:
|
|
6122
|
+
font: 'Verdana',
|
|
6127
6123
|
size: 12,
|
|
6128
|
-
color:
|
|
6124
|
+
color: '000000',
|
|
6129
6125
|
},
|
|
6130
6126
|
paragraph: {
|
|
6131
|
-
alignment:
|
|
6132
|
-
spacing: { before: 60, after: 60, line: 240, lineRule:
|
|
6127
|
+
alignment: 'left',
|
|
6128
|
+
spacing: { before: 60, after: 60, line: 240, lineRule: 'auto' },
|
|
6133
6129
|
},
|
|
6134
6130
|
};
|
|
6135
6131
|
|
|
6136
6132
|
private static readonly DEFAULT_LIST_PARAGRAPH_CONFIG: StyleConfig = {
|
|
6137
6133
|
run: {
|
|
6138
|
-
font:
|
|
6134
|
+
font: 'Verdana',
|
|
6139
6135
|
size: 12,
|
|
6140
|
-
color:
|
|
6136
|
+
color: '000000',
|
|
6141
6137
|
},
|
|
6142
6138
|
paragraph: {
|
|
6143
|
-
alignment:
|
|
6144
|
-
spacing: { before: 0, after: 60, line: 240, lineRule:
|
|
6139
|
+
alignment: 'left',
|
|
6140
|
+
spacing: { before: 0, after: 60, line: 240, lineRule: 'auto' },
|
|
6145
6141
|
indentation: { left: 360, hanging: 360 },
|
|
6146
6142
|
contextualSpacing: true,
|
|
6147
6143
|
},
|
|
@@ -6260,35 +6256,31 @@ export class Document {
|
|
|
6260
6256
|
};
|
|
6261
6257
|
|
|
6262
6258
|
// 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(
|
|
6259
|
+
const heading1 = this.stylesManager.getStyle('Heading1');
|
|
6260
|
+
const heading2 = this.stylesManager.getStyle('Heading2');
|
|
6261
|
+
const heading3 = this.stylesManager.getStyle('Heading3');
|
|
6262
|
+
const normal = this.stylesManager.getStyle('Normal');
|
|
6263
|
+
const listParagraph = this.stylesManager.getStyle('ListParagraph');
|
|
6268
6264
|
|
|
6269
6265
|
// Merge provided options with ACTUAL current style values (not hardcoded defaults)
|
|
6270
6266
|
// This allows users to only specify properties they want to change
|
|
6271
6267
|
const h1Config = {
|
|
6272
6268
|
run: {
|
|
6273
|
-
...(heading1?.getRunFormatting() ||
|
|
6274
|
-
Document.DEFAULT_HEADING1_CONFIG.run),
|
|
6269
|
+
...(heading1?.getRunFormatting() || Document.DEFAULT_HEADING1_CONFIG.run),
|
|
6275
6270
|
...options?.heading1?.run,
|
|
6276
6271
|
},
|
|
6277
6272
|
paragraph: {
|
|
6278
|
-
...(heading1?.getParagraphFormatting() ||
|
|
6279
|
-
Document.DEFAULT_HEADING1_CONFIG.paragraph),
|
|
6273
|
+
...(heading1?.getParagraphFormatting() || Document.DEFAULT_HEADING1_CONFIG.paragraph),
|
|
6280
6274
|
...options?.heading1?.paragraph,
|
|
6281
6275
|
},
|
|
6282
6276
|
};
|
|
6283
6277
|
const h2Config = {
|
|
6284
6278
|
run: {
|
|
6285
|
-
...(heading2?.getRunFormatting() ||
|
|
6286
|
-
Document.DEFAULT_HEADING2_CONFIG.run),
|
|
6279
|
+
...(heading2?.getRunFormatting() || Document.DEFAULT_HEADING2_CONFIG.run),
|
|
6287
6280
|
...options?.heading2?.run,
|
|
6288
6281
|
},
|
|
6289
6282
|
paragraph: {
|
|
6290
|
-
...(heading2?.getParagraphFormatting() ||
|
|
6291
|
-
Document.DEFAULT_HEADING2_CONFIG.paragraph),
|
|
6283
|
+
...(heading2?.getParagraphFormatting() || Document.DEFAULT_HEADING2_CONFIG.paragraph),
|
|
6292
6284
|
...options?.heading2?.paragraph,
|
|
6293
6285
|
},
|
|
6294
6286
|
tableOptions: {
|
|
@@ -6298,13 +6290,11 @@ export class Document {
|
|
|
6298
6290
|
};
|
|
6299
6291
|
const h3Config = {
|
|
6300
6292
|
run: {
|
|
6301
|
-
...(heading3?.getRunFormatting() ||
|
|
6302
|
-
Document.DEFAULT_HEADING3_CONFIG.run),
|
|
6293
|
+
...(heading3?.getRunFormatting() || Document.DEFAULT_HEADING3_CONFIG.run),
|
|
6303
6294
|
...options?.heading3?.run,
|
|
6304
6295
|
},
|
|
6305
6296
|
paragraph: {
|
|
6306
|
-
...(heading3?.getParagraphFormatting() ||
|
|
6307
|
-
Document.DEFAULT_HEADING3_CONFIG.paragraph),
|
|
6297
|
+
...(heading3?.getParagraphFormatting() || Document.DEFAULT_HEADING3_CONFIG.paragraph),
|
|
6308
6298
|
...options?.heading3?.paragraph,
|
|
6309
6299
|
},
|
|
6310
6300
|
};
|
|
@@ -6314,15 +6304,13 @@ export class Document {
|
|
|
6314
6304
|
...options?.normal?.run,
|
|
6315
6305
|
},
|
|
6316
6306
|
paragraph: {
|
|
6317
|
-
...(normal?.getParagraphFormatting() ||
|
|
6318
|
-
Document.DEFAULT_NORMAL_CONFIG.paragraph),
|
|
6307
|
+
...(normal?.getParagraphFormatting() || Document.DEFAULT_NORMAL_CONFIG.paragraph),
|
|
6319
6308
|
...options?.normal?.paragraph,
|
|
6320
6309
|
},
|
|
6321
6310
|
};
|
|
6322
6311
|
const listParaConfig = {
|
|
6323
6312
|
run: {
|
|
6324
|
-
...(listParagraph?.getRunFormatting() ||
|
|
6325
|
-
Document.DEFAULT_LIST_PARAGRAPH_CONFIG.run),
|
|
6313
|
+
...(listParagraph?.getRunFormatting() || Document.DEFAULT_LIST_PARAGRAPH_CONFIG.run),
|
|
6326
6314
|
...options?.listParagraph?.run,
|
|
6327
6315
|
},
|
|
6328
6316
|
paragraph: {
|
|
@@ -6333,14 +6321,12 @@ export class Document {
|
|
|
6333
6321
|
};
|
|
6334
6322
|
|
|
6335
6323
|
// Extract preserve blank lines option (defaults to true)
|
|
6336
|
-
const preserveBlankLines =
|
|
6337
|
-
options?.preserveBlankLinesAfterHeading2Tables ?? true;
|
|
6324
|
+
const preserveBlankLines = options?.preserveBlankLinesAfterHeading2Tables ?? true;
|
|
6338
6325
|
|
|
6339
6326
|
// Modify Heading1 definition
|
|
6340
6327
|
if (heading1 && h1Config.run && h1Config.paragraph) {
|
|
6341
6328
|
if (h1Config.run) heading1.setRunFormatting(h1Config.run);
|
|
6342
|
-
if (h1Config.paragraph)
|
|
6343
|
-
heading1.setParagraphFormatting(h1Config.paragraph);
|
|
6329
|
+
if (h1Config.paragraph) heading1.setParagraphFormatting(h1Config.paragraph);
|
|
6344
6330
|
// Mark style as modified so it gets included in mergeStylesWithOriginal()
|
|
6345
6331
|
this.addStyle(heading1);
|
|
6346
6332
|
results.heading1 = true;
|
|
@@ -6349,8 +6335,7 @@ export class Document {
|
|
|
6349
6335
|
// Modify Heading2 definition
|
|
6350
6336
|
if (heading2 && h2Config.run && h2Config.paragraph) {
|
|
6351
6337
|
if (h2Config.run) heading2.setRunFormatting(h2Config.run);
|
|
6352
|
-
if (h2Config.paragraph)
|
|
6353
|
-
heading2.setParagraphFormatting(h2Config.paragraph);
|
|
6338
|
+
if (h2Config.paragraph) heading2.setParagraphFormatting(h2Config.paragraph);
|
|
6354
6339
|
// Mark style as modified so it gets included in mergeStylesWithOriginal()
|
|
6355
6340
|
this.addStyle(heading2);
|
|
6356
6341
|
results.heading2 = true;
|
|
@@ -6359,8 +6344,7 @@ export class Document {
|
|
|
6359
6344
|
// Modify Heading3 definition
|
|
6360
6345
|
if (heading3 && h3Config.run && h3Config.paragraph) {
|
|
6361
6346
|
if (h3Config.run) heading3.setRunFormatting(h3Config.run);
|
|
6362
|
-
if (h3Config.paragraph)
|
|
6363
|
-
heading3.setParagraphFormatting(h3Config.paragraph);
|
|
6347
|
+
if (h3Config.paragraph) heading3.setParagraphFormatting(h3Config.paragraph);
|
|
6364
6348
|
// Mark style as modified so it gets included in mergeStylesWithOriginal()
|
|
6365
6349
|
this.addStyle(heading3);
|
|
6366
6350
|
results.heading3 = true;
|
|
@@ -6369,8 +6353,7 @@ export class Document {
|
|
|
6369
6353
|
// Modify Normal definition
|
|
6370
6354
|
if (normal && normalConfig.run && normalConfig.paragraph) {
|
|
6371
6355
|
if (normalConfig.run) normal.setRunFormatting(normalConfig.run);
|
|
6372
|
-
if (normalConfig.paragraph)
|
|
6373
|
-
normal.setParagraphFormatting(normalConfig.paragraph);
|
|
6356
|
+
if (normalConfig.paragraph) normal.setParagraphFormatting(normalConfig.paragraph);
|
|
6374
6357
|
// Mark style as modified so it gets included in mergeStylesWithOriginal()
|
|
6375
6358
|
this.addStyle(normal);
|
|
6376
6359
|
|
|
@@ -6378,12 +6361,11 @@ export class Document {
|
|
|
6378
6361
|
// Default is true - changes to Normal automatically apply to NormalWeb
|
|
6379
6362
|
const shouldLinkNormalWeb = options?.linkNormalWebToNormal !== false;
|
|
6380
6363
|
if (shouldLinkNormalWeb) {
|
|
6381
|
-
const normalWeb = this.stylesManager.getStyle(
|
|
6364
|
+
const normalWeb = this.stylesManager.getStyle('NormalWeb');
|
|
6382
6365
|
if (normalWeb) {
|
|
6383
6366
|
// Apply same formatting to NormalWeb
|
|
6384
6367
|
if (normalConfig.run) normalWeb.setRunFormatting(normalConfig.run);
|
|
6385
|
-
if (normalConfig.paragraph)
|
|
6386
|
-
normalWeb.setParagraphFormatting(normalConfig.paragraph);
|
|
6368
|
+
if (normalConfig.paragraph) normalWeb.setParagraphFormatting(normalConfig.paragraph);
|
|
6387
6369
|
// Mark as modified for selective merging during save
|
|
6388
6370
|
this.addStyle(normalWeb);
|
|
6389
6371
|
}
|
|
@@ -6394,8 +6376,7 @@ export class Document {
|
|
|
6394
6376
|
|
|
6395
6377
|
// Modify List Paragraph definition
|
|
6396
6378
|
if (listParagraph && listParaConfig.run && listParaConfig.paragraph) {
|
|
6397
|
-
if (listParaConfig.run)
|
|
6398
|
-
listParagraph.setRunFormatting(listParaConfig.run);
|
|
6379
|
+
if (listParaConfig.run) listParagraph.setRunFormatting(listParaConfig.run);
|
|
6399
6380
|
if (listParaConfig.paragraph) {
|
|
6400
6381
|
// Validate indentation: hanging must not exceed left to prevent negative bullet position
|
|
6401
6382
|
const indent = listParaConfig.paragraph.indentation;
|
|
@@ -6404,7 +6385,7 @@ export class Document {
|
|
|
6404
6385
|
const logger = getGlobalLogger();
|
|
6405
6386
|
logger.warn(
|
|
6406
6387
|
`[Document] ListParagraph indentation: hanging (${indent.hanging}) > left (${indent.left}). ` +
|
|
6407
|
-
|
|
6388
|
+
`Capping hanging to left to prevent negative bullet position.`
|
|
6408
6389
|
);
|
|
6409
6390
|
indent.hanging = indent.left;
|
|
6410
6391
|
}
|
|
@@ -6459,12 +6440,12 @@ export class Document {
|
|
|
6459
6440
|
const styleId = para.getStyle();
|
|
6460
6441
|
|
|
6461
6442
|
// Process Heading1 paragraphs
|
|
6462
|
-
if (styleId ===
|
|
6443
|
+
if (styleId === 'Heading1' && heading1) {
|
|
6463
6444
|
// Save white font status BEFORE clearing (clearDirectFormattingConflicts clears color)
|
|
6464
6445
|
const allRuns = this.getAllRunsFromParagraph(para);
|
|
6465
6446
|
const whiteFontRuns = new Set(
|
|
6466
6447
|
options?.preserveWhiteFont
|
|
6467
|
-
? allRuns.filter(run => run.getColor()?.toUpperCase() === 'FFFFFF')
|
|
6448
|
+
? allRuns.filter((run) => run.getColor()?.toUpperCase() === 'FFFFFF')
|
|
6468
6449
|
: []
|
|
6469
6450
|
);
|
|
6470
6451
|
|
|
@@ -6483,7 +6464,7 @@ export class Document {
|
|
|
6483
6464
|
run.setItalic(h1Config.run?.italic ?? false);
|
|
6484
6465
|
}
|
|
6485
6466
|
if (!h1Preserve.underline) {
|
|
6486
|
-
run.setUnderline(h1Config.run?.underline ?
|
|
6467
|
+
run.setUnderline(h1Config.run?.underline ? 'single' : false);
|
|
6487
6468
|
}
|
|
6488
6469
|
// Apply font, color, and size - skip color if run was white font
|
|
6489
6470
|
if (h1Config.run?.font) {
|
|
@@ -6505,25 +6486,13 @@ export class Document {
|
|
|
6505
6486
|
// Update paragraph mark properties to match configuration
|
|
6506
6487
|
if (para.formatting.paragraphMarkRunProperties) {
|
|
6507
6488
|
const markProps = para.formatting.paragraphMarkRunProperties;
|
|
6508
|
-
if (
|
|
6509
|
-
!h1Preserve.bold &&
|
|
6510
|
-
h1Config.run?.bold === false &&
|
|
6511
|
-
markProps.bold
|
|
6512
|
-
) {
|
|
6489
|
+
if (!h1Preserve.bold && h1Config.run?.bold === false && markProps.bold) {
|
|
6513
6490
|
delete markProps.bold;
|
|
6514
6491
|
}
|
|
6515
|
-
if (
|
|
6516
|
-
!h1Preserve.italic &&
|
|
6517
|
-
h1Config.run?.italic === false &&
|
|
6518
|
-
markProps.italic
|
|
6519
|
-
) {
|
|
6492
|
+
if (!h1Preserve.italic && h1Config.run?.italic === false && markProps.italic) {
|
|
6520
6493
|
delete markProps.italic;
|
|
6521
6494
|
}
|
|
6522
|
-
if (
|
|
6523
|
-
!h1Preserve.underline &&
|
|
6524
|
-
h1Config.run?.underline === false &&
|
|
6525
|
-
markProps.underline
|
|
6526
|
-
) {
|
|
6495
|
+
if (!h1Preserve.underline && h1Config.run?.underline === false && markProps.underline) {
|
|
6527
6496
|
delete markProps.underline;
|
|
6528
6497
|
}
|
|
6529
6498
|
// Update paragraph mark font, color, size
|
|
@@ -6542,10 +6511,11 @@ export class Document {
|
|
|
6542
6511
|
}
|
|
6543
6512
|
|
|
6544
6513
|
// Process Heading2 paragraphs
|
|
6545
|
-
else if (styleId ===
|
|
6514
|
+
else if (styleId === 'Heading2' && heading2) {
|
|
6546
6515
|
// Check if paragraph has actual text content (skip empty paragraphs)
|
|
6547
|
-
const hasContent = this.getAllRunsFromParagraph(para)
|
|
6548
|
-
|
|
6516
|
+
const hasContent = this.getAllRunsFromParagraph(para).some(
|
|
6517
|
+
(run) => run.getText().trim().length > 0
|
|
6518
|
+
);
|
|
6549
6519
|
|
|
6550
6520
|
if (!hasContent) {
|
|
6551
6521
|
// Skip empty Heading2 paragraphs - don't wrap them in tables
|
|
@@ -6578,8 +6548,7 @@ export class Document {
|
|
|
6578
6548
|
const isMultiCellTable = !(rowCount === 1 && colCount === 1);
|
|
6579
6549
|
const cellFormatting = cell.getFormatting();
|
|
6580
6550
|
const cellHasShading = !!(
|
|
6581
|
-
cellFormatting?.shading?.fill ||
|
|
6582
|
-
cellFormatting?.shading?.pattern
|
|
6551
|
+
cellFormatting?.shading?.fill || cellFormatting?.shading?.pattern
|
|
6583
6552
|
);
|
|
6584
6553
|
|
|
6585
6554
|
if (isMultiCellTable && cellHasShading && para.formatting.alignment) {
|
|
@@ -6592,7 +6561,7 @@ export class Document {
|
|
|
6592
6561
|
const allRuns = this.getAllRunsFromParagraph(para);
|
|
6593
6562
|
const whiteFontRuns = new Set(
|
|
6594
6563
|
options?.preserveWhiteFont
|
|
6595
|
-
? allRuns.filter(run => run.getColor()?.toUpperCase() === 'FFFFFF')
|
|
6564
|
+
? allRuns.filter((run) => run.getColor()?.toUpperCase() === 'FFFFFF')
|
|
6596
6565
|
: []
|
|
6597
6566
|
);
|
|
6598
6567
|
|
|
@@ -6617,7 +6586,7 @@ export class Document {
|
|
|
6617
6586
|
run.setItalic(h2Config.run?.italic ?? false);
|
|
6618
6587
|
}
|
|
6619
6588
|
if (!h2Preserve.underline) {
|
|
6620
|
-
run.setUnderline(h2Config.run?.underline ?
|
|
6589
|
+
run.setUnderline(h2Config.run?.underline ? 'single' : false);
|
|
6621
6590
|
}
|
|
6622
6591
|
// Apply font, color, and size - skip color if run was white font
|
|
6623
6592
|
if (h2Config.run?.font) {
|
|
@@ -6639,25 +6608,13 @@ export class Document {
|
|
|
6639
6608
|
// Update paragraph mark properties to match configuration
|
|
6640
6609
|
if (para.formatting.paragraphMarkRunProperties) {
|
|
6641
6610
|
const markProps = para.formatting.paragraphMarkRunProperties;
|
|
6642
|
-
if (
|
|
6643
|
-
!h2Preserve.bold &&
|
|
6644
|
-
h2Config.run?.bold === false &&
|
|
6645
|
-
markProps.bold
|
|
6646
|
-
) {
|
|
6611
|
+
if (!h2Preserve.bold && h2Config.run?.bold === false && markProps.bold) {
|
|
6647
6612
|
delete markProps.bold;
|
|
6648
6613
|
}
|
|
6649
|
-
if (
|
|
6650
|
-
!h2Preserve.italic &&
|
|
6651
|
-
h2Config.run?.italic === false &&
|
|
6652
|
-
markProps.italic
|
|
6653
|
-
) {
|
|
6614
|
+
if (!h2Preserve.italic && h2Config.run?.italic === false && markProps.italic) {
|
|
6654
6615
|
delete markProps.italic;
|
|
6655
6616
|
}
|
|
6656
|
-
if (
|
|
6657
|
-
!h2Preserve.underline &&
|
|
6658
|
-
h2Config.run?.underline === false &&
|
|
6659
|
-
markProps.underline
|
|
6660
|
-
) {
|
|
6617
|
+
if (!h2Preserve.underline && h2Config.run?.underline === false && markProps.underline) {
|
|
6661
6618
|
delete markProps.underline;
|
|
6662
6619
|
}
|
|
6663
6620
|
// Update paragraph mark font, color, size
|
|
@@ -6703,13 +6660,13 @@ export class Document {
|
|
|
6703
6660
|
});
|
|
6704
6661
|
if (table) {
|
|
6705
6662
|
table.setWidth(h2Config.tableOptions.tableWidthPercent);
|
|
6706
|
-
table.setWidthType(
|
|
6663
|
+
table.setWidthType('pct');
|
|
6707
6664
|
}
|
|
6708
6665
|
}
|
|
6709
6666
|
} else {
|
|
6710
6667
|
// Paragraph is not in a table - wrap it using config
|
|
6711
6668
|
const table = this.wrapParagraphInTable(para, {
|
|
6712
|
-
shading: h2Config.tableOptions?.shading ??
|
|
6669
|
+
shading: h2Config.tableOptions?.shading ?? 'BFBFBF',
|
|
6713
6670
|
marginTop: h2Config.tableOptions?.marginTop ?? 0,
|
|
6714
6671
|
marginBottom: h2Config.tableOptions?.marginBottom ?? 0,
|
|
6715
6672
|
marginLeft: h2Config.tableOptions?.marginLeft ?? 115,
|
|
@@ -6740,7 +6697,7 @@ export class Document {
|
|
|
6740
6697
|
// Runs with text count as content
|
|
6741
6698
|
if ((item as any).getText) {
|
|
6742
6699
|
const text = (item as any).getText().trim();
|
|
6743
|
-
if (text !==
|
|
6700
|
+
if (text !== '') return false;
|
|
6744
6701
|
}
|
|
6745
6702
|
}
|
|
6746
6703
|
|
|
@@ -6765,12 +6722,12 @@ export class Document {
|
|
|
6765
6722
|
}
|
|
6766
6723
|
|
|
6767
6724
|
// Process Heading3 paragraphs
|
|
6768
|
-
else if (styleId ===
|
|
6725
|
+
else if (styleId === 'Heading3' && heading3) {
|
|
6769
6726
|
// Save white font status BEFORE clearing (clearDirectFormattingConflicts clears color)
|
|
6770
6727
|
const allRuns = this.getAllRunsFromParagraph(para);
|
|
6771
6728
|
const whiteFontRuns = new Set(
|
|
6772
6729
|
options?.preserveWhiteFont
|
|
6773
|
-
? allRuns.filter(run => run.getColor()?.toUpperCase() === 'FFFFFF')
|
|
6730
|
+
? allRuns.filter((run) => run.getColor()?.toUpperCase() === 'FFFFFF')
|
|
6774
6731
|
: []
|
|
6775
6732
|
);
|
|
6776
6733
|
|
|
@@ -6789,7 +6746,7 @@ export class Document {
|
|
|
6789
6746
|
run.setItalic(h3Config.run?.italic ?? false);
|
|
6790
6747
|
}
|
|
6791
6748
|
if (!h3Preserve.underline) {
|
|
6792
|
-
run.setUnderline(h3Config.run?.underline ?
|
|
6749
|
+
run.setUnderline(h3Config.run?.underline ? 'single' : false);
|
|
6793
6750
|
}
|
|
6794
6751
|
// Apply font, color, and size - skip color if run was white font
|
|
6795
6752
|
if (h3Config.run?.font) {
|
|
@@ -6811,25 +6768,13 @@ export class Document {
|
|
|
6811
6768
|
// Update paragraph mark properties to match configuration
|
|
6812
6769
|
if (para.formatting.paragraphMarkRunProperties) {
|
|
6813
6770
|
const markProps = para.formatting.paragraphMarkRunProperties;
|
|
6814
|
-
if (
|
|
6815
|
-
!h3Preserve.bold &&
|
|
6816
|
-
h3Config.run?.bold === false &&
|
|
6817
|
-
markProps.bold
|
|
6818
|
-
) {
|
|
6771
|
+
if (!h3Preserve.bold && h3Config.run?.bold === false && markProps.bold) {
|
|
6819
6772
|
delete markProps.bold;
|
|
6820
6773
|
}
|
|
6821
|
-
if (
|
|
6822
|
-
!h3Preserve.italic &&
|
|
6823
|
-
h3Config.run?.italic === false &&
|
|
6824
|
-
markProps.italic
|
|
6825
|
-
) {
|
|
6774
|
+
if (!h3Preserve.italic && h3Config.run?.italic === false && markProps.italic) {
|
|
6826
6775
|
delete markProps.italic;
|
|
6827
6776
|
}
|
|
6828
|
-
if (
|
|
6829
|
-
!h3Preserve.underline &&
|
|
6830
|
-
h3Config.run?.underline === false &&
|
|
6831
|
-
markProps.underline
|
|
6832
|
-
) {
|
|
6777
|
+
if (!h3Preserve.underline && h3Config.run?.underline === false && markProps.underline) {
|
|
6833
6778
|
delete markProps.underline;
|
|
6834
6779
|
}
|
|
6835
6780
|
// Update paragraph mark font, color, size
|
|
@@ -6848,7 +6793,7 @@ export class Document {
|
|
|
6848
6793
|
}
|
|
6849
6794
|
|
|
6850
6795
|
// Process List Paragraph paragraphs
|
|
6851
|
-
else if (styleId ===
|
|
6796
|
+
else if (styleId === 'ListParagraph' && listParagraph) {
|
|
6852
6797
|
// Check for mis-styled paragraphs: ListParagraph + left:0 + no numbering
|
|
6853
6798
|
// These are not actual list items - change them to Normal style
|
|
6854
6799
|
const paraIndentation = para.getFormatting().indentation;
|
|
@@ -6856,7 +6801,7 @@ export class Document {
|
|
|
6856
6801
|
if (paraIndentation?.left === 0 && !hasNumbering) {
|
|
6857
6802
|
// This paragraph has ListParagraph style but explicitly overrides indent to 0
|
|
6858
6803
|
// and has no numbering - it should be Normal style, not ListParagraph
|
|
6859
|
-
para.setStyle(
|
|
6804
|
+
para.setStyle('Normal');
|
|
6860
6805
|
|
|
6861
6806
|
// Preserve existing bold formatting and white font before applying Normal style
|
|
6862
6807
|
const allRuns = this.getAllRunsFromParagraph(para);
|
|
@@ -6942,7 +6887,7 @@ export class Document {
|
|
|
6942
6887
|
run.setItalic(listParaConfig.run?.italic ?? false);
|
|
6943
6888
|
}
|
|
6944
6889
|
if (!listParaPreserve.underline) {
|
|
6945
|
-
run.setUnderline(listParaConfig.run?.underline ?
|
|
6890
|
+
run.setUnderline(listParaConfig.run?.underline ? 'single' : false);
|
|
6946
6891
|
}
|
|
6947
6892
|
// Apply font, color, and size - skip color if run was white font
|
|
6948
6893
|
if (listParaConfig.run?.font) {
|
|
@@ -6964,11 +6909,7 @@ export class Document {
|
|
|
6964
6909
|
// Update paragraph mark properties to match configuration
|
|
6965
6910
|
if (para.formatting.paragraphMarkRunProperties) {
|
|
6966
6911
|
const markProps = para.formatting.paragraphMarkRunProperties;
|
|
6967
|
-
if (
|
|
6968
|
-
!listParaPreserve.bold &&
|
|
6969
|
-
listParaConfig.run?.bold === false &&
|
|
6970
|
-
markProps.bold
|
|
6971
|
-
) {
|
|
6912
|
+
if (!listParaPreserve.bold && listParaConfig.run?.bold === false && markProps.bold) {
|
|
6972
6913
|
delete markProps.bold;
|
|
6973
6914
|
}
|
|
6974
6915
|
if (
|
|
@@ -7002,7 +6943,12 @@ export class Document {
|
|
|
7002
6943
|
|
|
7003
6944
|
// Process Normal paragraphs (including undefined style which defaults to Normal)
|
|
7004
6945
|
// Also process NormalWeb paragraphs when linkNormalWebToNormal is enabled (default: true)
|
|
7005
|
-
else if (
|
|
6946
|
+
else if (
|
|
6947
|
+
(styleId === 'Normal' ||
|
|
6948
|
+
styleId === undefined ||
|
|
6949
|
+
(styleId === 'NormalWeb' && options?.linkNormalWebToNormal !== false)) &&
|
|
6950
|
+
normal
|
|
6951
|
+
) {
|
|
7006
6952
|
// Save formatting that should be preserved BEFORE clearing
|
|
7007
6953
|
const allRuns = this.getAllRunsFromParagraph(para);
|
|
7008
6954
|
const preservedFormatting = allRuns.map((run) => {
|
|
@@ -7017,8 +6963,8 @@ export class Document {
|
|
|
7017
6963
|
});
|
|
7018
6964
|
|
|
7019
6965
|
// Save center alignment BEFORE clearing if preserveCenterAlignment is set
|
|
7020
|
-
const savedCenterAlignment =
|
|
7021
|
-
para.getAlignment() === 'center';
|
|
6966
|
+
const savedCenterAlignment =
|
|
6967
|
+
options?.normal?.preserveCenterAlignment && para.getAlignment() === 'center';
|
|
7022
6968
|
|
|
7023
6969
|
para.clearDirectFormattingConflicts(normal);
|
|
7024
6970
|
|
|
@@ -7054,7 +7000,7 @@ export class Document {
|
|
|
7054
7000
|
run.setItalic(normalConfig.run?.italic ?? false);
|
|
7055
7001
|
}
|
|
7056
7002
|
if (!normalPreserve.underline) {
|
|
7057
|
-
run.setUnderline(normalConfig.run?.underline ?
|
|
7003
|
+
run.setUnderline(normalConfig.run?.underline ? 'single' : false);
|
|
7058
7004
|
}
|
|
7059
7005
|
// Apply font, color, and size - skip color if run was white font
|
|
7060
7006
|
if (normalConfig.run?.font) {
|
|
@@ -7077,18 +7023,10 @@ export class Document {
|
|
|
7077
7023
|
// Update paragraph mark properties to match configuration
|
|
7078
7024
|
if (para.formatting.paragraphMarkRunProperties) {
|
|
7079
7025
|
const markProps = para.formatting.paragraphMarkRunProperties;
|
|
7080
|
-
if (
|
|
7081
|
-
!normalPreserve.bold &&
|
|
7082
|
-
normalConfig.run?.bold === false &&
|
|
7083
|
-
markProps.bold
|
|
7084
|
-
) {
|
|
7026
|
+
if (!normalPreserve.bold && normalConfig.run?.bold === false && markProps.bold) {
|
|
7085
7027
|
delete markProps.bold;
|
|
7086
7028
|
}
|
|
7087
|
-
if (
|
|
7088
|
-
!normalPreserve.italic &&
|
|
7089
|
-
normalConfig.run?.italic === false &&
|
|
7090
|
-
markProps.italic
|
|
7091
|
-
) {
|
|
7029
|
+
if (!normalPreserve.italic && normalConfig.run?.italic === false && markProps.italic) {
|
|
7092
7030
|
delete markProps.italic;
|
|
7093
7031
|
}
|
|
7094
7032
|
if (
|
|
@@ -7174,8 +7112,9 @@ export class Document {
|
|
|
7174
7112
|
indentPerLevel?: number;
|
|
7175
7113
|
}): { formatted: number[] } {
|
|
7176
7114
|
// Filter valid levels, deduplicate, and sort
|
|
7177
|
-
const validLevels = (options.levels ?? [1, 2, 3, 4, 5, 6, 7, 8, 9])
|
|
7178
|
-
|
|
7115
|
+
const validLevels = (options.levels ?? [1, 2, 3, 4, 5, 6, 7, 8, 9]).filter(
|
|
7116
|
+
(l) => l >= 1 && l <= 9
|
|
7117
|
+
);
|
|
7179
7118
|
const levels = [...new Set(validLevels)].sort((a, b) => a - b);
|
|
7180
7119
|
const formatted: number[] = [];
|
|
7181
7120
|
|
|
@@ -7277,14 +7216,14 @@ export class Document {
|
|
|
7277
7216
|
const styleId = style.getStyleId();
|
|
7278
7217
|
|
|
7279
7218
|
switch (styleId) {
|
|
7280
|
-
case
|
|
7219
|
+
case 'Heading1':
|
|
7281
7220
|
options.heading1 = {
|
|
7282
7221
|
run: style.getRunFormatting() as any,
|
|
7283
7222
|
paragraph: style.getParagraphFormatting() as any,
|
|
7284
7223
|
};
|
|
7285
7224
|
break;
|
|
7286
7225
|
|
|
7287
|
-
case
|
|
7226
|
+
case 'Heading2':
|
|
7288
7227
|
options.heading2 = {
|
|
7289
7228
|
run: style.getRunFormatting() as any,
|
|
7290
7229
|
paragraph: style.getParagraphFormatting() as any,
|
|
@@ -7292,21 +7231,21 @@ export class Document {
|
|
|
7292
7231
|
};
|
|
7293
7232
|
break;
|
|
7294
7233
|
|
|
7295
|
-
case
|
|
7234
|
+
case 'Heading3':
|
|
7296
7235
|
options.heading3 = {
|
|
7297
7236
|
run: style.getRunFormatting() as any,
|
|
7298
7237
|
paragraph: style.getParagraphFormatting() as any,
|
|
7299
7238
|
};
|
|
7300
7239
|
break;
|
|
7301
7240
|
|
|
7302
|
-
case
|
|
7241
|
+
case 'Normal':
|
|
7303
7242
|
options.normal = {
|
|
7304
7243
|
run: style.getRunFormatting() as any,
|
|
7305
7244
|
paragraph: style.getParagraphFormatting() as any,
|
|
7306
7245
|
};
|
|
7307
7246
|
break;
|
|
7308
7247
|
|
|
7309
|
-
case
|
|
7248
|
+
case 'ListParagraph':
|
|
7310
7249
|
options.listParagraph = {
|
|
7311
7250
|
run: style.getRunFormatting() as any,
|
|
7312
7251
|
paragraph: style.getParagraphFormatting() as any,
|
|
@@ -7346,8 +7285,8 @@ export class Document {
|
|
|
7346
7285
|
*/
|
|
7347
7286
|
public updateTableStyleShading(oldColor: string, newColor: string): number {
|
|
7348
7287
|
// Normalize colors (uppercase, no #)
|
|
7349
|
-
const normalizedOld = oldColor.replace(
|
|
7350
|
-
const normalizedNew = newColor.replace(
|
|
7288
|
+
const normalizedOld = oldColor.replace('#', '').toUpperCase();
|
|
7289
|
+
const normalizedNew = newColor.replace('#', '').toUpperCase();
|
|
7351
7290
|
|
|
7352
7291
|
if (normalizedOld === normalizedNew) {
|
|
7353
7292
|
return 0; // No change needed
|
|
@@ -7363,10 +7302,7 @@ export class Document {
|
|
|
7363
7302
|
|
|
7364
7303
|
// Pattern to match shading elements with fill attribute containing the old color
|
|
7365
7304
|
// Matches: w:fill="A5A5A5" (case-insensitive)
|
|
7366
|
-
const fillPattern = new RegExp(
|
|
7367
|
-
`(w:fill=["'])${normalizedOld}(["'])`,
|
|
7368
|
-
"gi"
|
|
7369
|
-
);
|
|
7305
|
+
const fillPattern = new RegExp(`(w:fill=["'])${normalizedOld}(["'])`, 'gi');
|
|
7370
7306
|
|
|
7371
7307
|
// Replace all occurrences
|
|
7372
7308
|
stylesXml = stylesXml.replace(fillPattern, (match, prefix, suffix) => {
|
|
@@ -7376,10 +7312,7 @@ export class Document {
|
|
|
7376
7312
|
|
|
7377
7313
|
// Also handle color attribute in shading elements
|
|
7378
7314
|
// Matches: w:color="A5A5A5" within shd elements
|
|
7379
|
-
const colorPattern = new RegExp(
|
|
7380
|
-
`(<w:shd[^>]*w:color=["'])${normalizedOld}(["'])`,
|
|
7381
|
-
"gi"
|
|
7382
|
-
);
|
|
7315
|
+
const colorPattern = new RegExp(`(<w:shd[^>]*w:color=["'])${normalizedOld}(["'])`, 'gi');
|
|
7383
7316
|
|
|
7384
7317
|
stylesXml = stylesXml.replace(colorPattern, (match, prefix, suffix) => {
|
|
7385
7318
|
updateCount++;
|
|
@@ -7425,28 +7358,22 @@ export class Document {
|
|
|
7425
7358
|
let totalUpdated = 0;
|
|
7426
7359
|
|
|
7427
7360
|
// Default colors commonly used in table styles
|
|
7428
|
-
const defaultReplaceColors = [
|
|
7361
|
+
const defaultReplaceColors = ['A5A5A5', 'C0C0C0', 'D9D9D9', 'E7E6E6'];
|
|
7429
7362
|
const colorsToReplace = settings.replaceColors || defaultReplaceColors;
|
|
7430
7363
|
|
|
7431
7364
|
// Replace header-type colors with header shading
|
|
7432
7365
|
if (settings.headerShading) {
|
|
7433
7366
|
for (const oldColor of colorsToReplace) {
|
|
7434
|
-
totalUpdated += this.updateTableStyleShading(
|
|
7435
|
-
oldColor,
|
|
7436
|
-
settings.headerShading
|
|
7437
|
-
);
|
|
7367
|
+
totalUpdated += this.updateTableStyleShading(oldColor, settings.headerShading);
|
|
7438
7368
|
}
|
|
7439
7369
|
}
|
|
7440
7370
|
|
|
7441
7371
|
// If data shading differs from header, apply it to lighter colors
|
|
7442
7372
|
if (settings.dataShading && settings.dataShading !== settings.headerShading) {
|
|
7443
7373
|
// Typically data cells use lighter shading
|
|
7444
|
-
const dataColors = [
|
|
7374
|
+
const dataColors = ['D9D9D9', 'E7E6E6', 'F2F2F2'];
|
|
7445
7375
|
for (const oldColor of dataColors) {
|
|
7446
|
-
totalUpdated += this.updateTableStyleShading(
|
|
7447
|
-
oldColor,
|
|
7448
|
-
settings.dataShading
|
|
7449
|
-
);
|
|
7376
|
+
totalUpdated += this.updateTableStyleShading(oldColor, settings.dataShading);
|
|
7450
7377
|
}
|
|
7451
7378
|
}
|
|
7452
7379
|
|
|
@@ -7518,8 +7445,8 @@ export class Document {
|
|
|
7518
7445
|
const {
|
|
7519
7446
|
bold = false,
|
|
7520
7447
|
fontSize = 24, // 12pt
|
|
7521
|
-
color =
|
|
7522
|
-
font =
|
|
7448
|
+
color = '000000',
|
|
7449
|
+
font = 'Arial',
|
|
7523
7450
|
} = options || {};
|
|
7524
7451
|
|
|
7525
7452
|
let listsUpdated = 0;
|
|
@@ -7529,14 +7456,13 @@ export class Document {
|
|
|
7529
7456
|
|
|
7530
7457
|
for (const instance of instances) {
|
|
7531
7458
|
const abstractNumId = instance.getAbstractNumId();
|
|
7532
|
-
const abstractNum =
|
|
7533
|
-
this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
7459
|
+
const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
7534
7460
|
|
|
7535
7461
|
if (!abstractNum) continue;
|
|
7536
7462
|
|
|
7537
7463
|
// Only process bullet lists (skip numbered lists)
|
|
7538
7464
|
const level0 = abstractNum.getLevel(0);
|
|
7539
|
-
if (level0?.getFormat() !==
|
|
7465
|
+
if (level0?.getFormat() !== 'bullet') continue;
|
|
7540
7466
|
|
|
7541
7467
|
// Update all 9 levels (0-8) with formatting only (preserve existing symbols)
|
|
7542
7468
|
for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
|
|
@@ -7597,8 +7523,8 @@ export class Document {
|
|
|
7597
7523
|
const {
|
|
7598
7524
|
bold = false,
|
|
7599
7525
|
fontSize = 24, // 12pt
|
|
7600
|
-
color =
|
|
7601
|
-
font =
|
|
7526
|
+
color = '000000',
|
|
7527
|
+
font = 'Verdana',
|
|
7602
7528
|
} = options || {};
|
|
7603
7529
|
|
|
7604
7530
|
let listsUpdated = 0;
|
|
@@ -7608,14 +7534,13 @@ export class Document {
|
|
|
7608
7534
|
|
|
7609
7535
|
for (const instance of instances) {
|
|
7610
7536
|
const abstractNumId = instance.getAbstractNumId();
|
|
7611
|
-
const abstractNum =
|
|
7612
|
-
this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
7537
|
+
const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
|
|
7613
7538
|
|
|
7614
7539
|
if (!abstractNum) continue;
|
|
7615
7540
|
|
|
7616
7541
|
// Only process numbered lists (skip bullet lists)
|
|
7617
7542
|
const level0 = abstractNum.getLevel(0);
|
|
7618
|
-
if (!level0 || level0.getFormat() ===
|
|
7543
|
+
if (!level0 || level0.getFormat() === 'bullet') continue;
|
|
7619
7544
|
|
|
7620
7545
|
// Update all 9 levels (0-8)
|
|
7621
7546
|
for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
|
|
@@ -7666,12 +7591,7 @@ export class Document {
|
|
|
7666
7591
|
color?: string;
|
|
7667
7592
|
underline?: boolean;
|
|
7668
7593
|
}): number {
|
|
7669
|
-
const {
|
|
7670
|
-
font = "Verdana",
|
|
7671
|
-
size = 12,
|
|
7672
|
-
color = "0000FF",
|
|
7673
|
-
underline = true,
|
|
7674
|
-
} = options || {};
|
|
7594
|
+
const { font = 'Verdana', size = 12, color = '0000FF', underline = true } = options || {};
|
|
7675
7595
|
|
|
7676
7596
|
const hyperlinks = this.getHyperlinks();
|
|
7677
7597
|
|
|
@@ -7680,14 +7600,13 @@ export class Document {
|
|
|
7680
7600
|
font: font,
|
|
7681
7601
|
size: size,
|
|
7682
7602
|
color: color,
|
|
7683
|
-
underline: underline ?
|
|
7603
|
+
underline: underline ? 'single' : false,
|
|
7684
7604
|
});
|
|
7685
7605
|
}
|
|
7686
7606
|
|
|
7687
7607
|
return hyperlinks.length;
|
|
7688
7608
|
}
|
|
7689
7609
|
|
|
7690
|
-
|
|
7691
7610
|
/**
|
|
7692
7611
|
* Applies Heading 1 style to paragraphs with H1-like style names
|
|
7693
7612
|
* @param options Optional style application options
|
|
@@ -7710,7 +7629,7 @@ export class Document {
|
|
|
7710
7629
|
* ```
|
|
7711
7630
|
*/
|
|
7712
7631
|
public applyH1(options?: StyleApplyOptions): number {
|
|
7713
|
-
return this.applyStyleToMatching(
|
|
7632
|
+
return this.applyStyleToMatching('Heading1', options, (style) =>
|
|
7714
7633
|
/^(heading\s*1|header\s*1|h1)$/i.test(style)
|
|
7715
7634
|
);
|
|
7716
7635
|
}
|
|
@@ -7727,7 +7646,7 @@ export class Document {
|
|
|
7727
7646
|
* ```
|
|
7728
7647
|
*/
|
|
7729
7648
|
public applyH2(options?: StyleApplyOptions): number {
|
|
7730
|
-
return this.applyStyleToMatching(
|
|
7649
|
+
return this.applyStyleToMatching('Heading2', options, (style) =>
|
|
7731
7650
|
/^(heading\s*2|header\s*2|h2)$/i.test(style)
|
|
7732
7651
|
);
|
|
7733
7652
|
}
|
|
@@ -7744,7 +7663,7 @@ export class Document {
|
|
|
7744
7663
|
* ```
|
|
7745
7664
|
*/
|
|
7746
7665
|
public applyH3(options?: StyleApplyOptions): number {
|
|
7747
|
-
return this.applyStyleToMatching(
|
|
7666
|
+
return this.applyStyleToMatching('Heading3', options, (style) =>
|
|
7748
7667
|
/^(heading\s*3|header\s*3|h3)$/i.test(style)
|
|
7749
7668
|
);
|
|
7750
7669
|
}
|
|
@@ -7759,16 +7678,13 @@ export class Document {
|
|
|
7759
7678
|
options?.paragraphs ||
|
|
7760
7679
|
this.getAllParagraphs().filter((p) => {
|
|
7761
7680
|
const style = p.getStyle();
|
|
7762
|
-
return (
|
|
7763
|
-
!style ||
|
|
7764
|
-
!/^(heading|header|h\d|list|toc|tod|caution|table)/i.test(style)
|
|
7765
|
-
);
|
|
7681
|
+
return !style || !/^(heading|header|h\d|list|toc|tod|caution|table)/i.test(style);
|
|
7766
7682
|
});
|
|
7767
7683
|
|
|
7768
7684
|
let count = 0;
|
|
7769
7685
|
for (const para of targets) {
|
|
7770
7686
|
if (para.isPreserved()) continue;
|
|
7771
|
-
para.setStyle(
|
|
7687
|
+
para.setStyle('Normal');
|
|
7772
7688
|
|
|
7773
7689
|
if (options?.keepProperties && options.keepProperties.length > 0) {
|
|
7774
7690
|
this.clearFormattingExcept(para, options.keepProperties);
|
|
@@ -7791,7 +7707,7 @@ export class Document {
|
|
|
7791
7707
|
* @returns Number of paragraphs updated
|
|
7792
7708
|
*/
|
|
7793
7709
|
public applyNumList(options?: StyleApplyOptions): number {
|
|
7794
|
-
return this.applyStyleToMatching(
|
|
7710
|
+
return this.applyStyleToMatching('ListParagraph', options, (style) =>
|
|
7795
7711
|
/^(list\s*number|numbered\s*list|list\s*paragraph)$/i.test(style)
|
|
7796
7712
|
);
|
|
7797
7713
|
}
|
|
@@ -7802,7 +7718,7 @@ export class Document {
|
|
|
7802
7718
|
* @returns Number of paragraphs updated
|
|
7803
7719
|
*/
|
|
7804
7720
|
public applyBulletList(options?: StyleApplyOptions): number {
|
|
7805
|
-
return this.applyStyleToMatching(
|
|
7721
|
+
return this.applyStyleToMatching('ListParagraph', options, (style) =>
|
|
7806
7722
|
/^(list\s*bullet|bullet\s*list|list\s*paragraph)$/i.test(style)
|
|
7807
7723
|
);
|
|
7808
7724
|
}
|
|
@@ -7813,7 +7729,7 @@ export class Document {
|
|
|
7813
7729
|
* @returns Number of paragraphs updated
|
|
7814
7730
|
*/
|
|
7815
7731
|
public applyTOC(options?: StyleApplyOptions): number {
|
|
7816
|
-
return this.applyStyleToMatching(
|
|
7732
|
+
return this.applyStyleToMatching('TOC', options, (style) =>
|
|
7817
7733
|
/^(toc|table\s*of\s*contents|toc\s*heading)$/i.test(style)
|
|
7818
7734
|
);
|
|
7819
7735
|
}
|
|
@@ -7824,7 +7740,7 @@ export class Document {
|
|
|
7824
7740
|
* @returns Number of paragraphs updated
|
|
7825
7741
|
*/
|
|
7826
7742
|
public applyTOD(options?: StyleApplyOptions): number {
|
|
7827
|
-
return this.applyStyleToMatching(
|
|
7743
|
+
return this.applyStyleToMatching('TopOfDocument', options, (style) =>
|
|
7828
7744
|
/^(tod|top\s*of\s*document|document\s*top)$/i.test(style)
|
|
7829
7745
|
);
|
|
7830
7746
|
}
|
|
@@ -7835,7 +7751,7 @@ export class Document {
|
|
|
7835
7751
|
* @returns Number of paragraphs updated
|
|
7836
7752
|
*/
|
|
7837
7753
|
public applyCaution(options?: StyleApplyOptions): number {
|
|
7838
|
-
return this.applyStyleToMatching(
|
|
7754
|
+
return this.applyStyleToMatching('Caution', options, (style) =>
|
|
7839
7755
|
/^(caution|warning|important|alert)$/i.test(style)
|
|
7840
7756
|
);
|
|
7841
7757
|
}
|
|
@@ -7856,7 +7772,7 @@ export class Document {
|
|
|
7856
7772
|
for (const cell of firstRow.getCells()) {
|
|
7857
7773
|
for (const para of cell.getParagraphs()) {
|
|
7858
7774
|
if (para.isPreserved()) continue;
|
|
7859
|
-
para.setStyle(
|
|
7775
|
+
para.setStyle('TableHeader');
|
|
7860
7776
|
|
|
7861
7777
|
if (options?.keepProperties && options.keepProperties.length > 0) {
|
|
7862
7778
|
this.clearFormattingExcept(para, options.keepProperties);
|
|
@@ -7905,9 +7821,9 @@ export class Document {
|
|
|
7905
7821
|
if (options.color) run.setColor(options.color);
|
|
7906
7822
|
if (options.emphasis) {
|
|
7907
7823
|
options.emphasis.forEach((emp) => {
|
|
7908
|
-
if (emp ===
|
|
7909
|
-
if (emp ===
|
|
7910
|
-
if (emp ===
|
|
7824
|
+
if (emp === 'bold') run.setBold(true);
|
|
7825
|
+
if (emp === 'italic') run.setItalic(true);
|
|
7826
|
+
if (emp === 'underline') run.setUnderline('single');
|
|
7911
7827
|
});
|
|
7912
7828
|
}
|
|
7913
7829
|
}
|
|
@@ -7960,10 +7876,7 @@ export class Document {
|
|
|
7960
7876
|
* Helper method to selectively clear formatting while preserving specific properties
|
|
7961
7877
|
* @private
|
|
7962
7878
|
*/
|
|
7963
|
-
private clearFormattingExcept(
|
|
7964
|
-
para: Paragraph,
|
|
7965
|
-
keepProperties: string[]
|
|
7966
|
-
): void {
|
|
7879
|
+
private clearFormattingExcept(para: Paragraph, keepProperties: string[]): void {
|
|
7967
7880
|
// Save properties to keep
|
|
7968
7881
|
const savedProps: Record<string, unknown> = {};
|
|
7969
7882
|
const formatting = para.formatting as Record<string, unknown>;
|
|
@@ -8000,21 +7913,15 @@ export class Document {
|
|
|
8000
7913
|
|
|
8001
7914
|
// Restore saved properties using appropriate setters
|
|
8002
7915
|
if (runSavedProps.bold !== undefined) run.setBold(runSavedProps.bold);
|
|
8003
|
-
if (runSavedProps.italic !== undefined)
|
|
8004
|
-
|
|
8005
|
-
if (runSavedProps.underline !== undefined)
|
|
8006
|
-
run.setUnderline(runSavedProps.underline);
|
|
7916
|
+
if (runSavedProps.italic !== undefined) run.setItalic(runSavedProps.italic);
|
|
7917
|
+
if (runSavedProps.underline !== undefined) run.setUnderline(runSavedProps.underline);
|
|
8007
7918
|
if (runSavedProps.color !== undefined) run.setColor(runSavedProps.color);
|
|
8008
7919
|
if (runSavedProps.font !== undefined) run.setFont(runSavedProps.font);
|
|
8009
7920
|
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);
|
|
7921
|
+
if (runSavedProps.highlight !== undefined) run.setHighlight(runSavedProps.highlight);
|
|
7922
|
+
if (runSavedProps.strike !== undefined) run.setStrike(runSavedProps.strike);
|
|
7923
|
+
if (runSavedProps.subscript !== undefined) run.setSubscript(runSavedProps.subscript);
|
|
7924
|
+
if (runSavedProps.superscript !== undefined) run.setSuperscript(runSavedProps.superscript);
|
|
8018
7925
|
}
|
|
8019
7926
|
}
|
|
8020
7927
|
|
|
@@ -8130,7 +8037,7 @@ export class Document {
|
|
|
8130
8037
|
|
|
8131
8038
|
for (const match of tMatches) {
|
|
8132
8039
|
hasTableSwitch = true;
|
|
8133
|
-
const content = (match[1] ||
|
|
8040
|
+
const content = (match[1] || '').trim();
|
|
8134
8041
|
if (!content) continue;
|
|
8135
8042
|
|
|
8136
8043
|
// --- Case 1: Range format "X-Y" ---
|
|
@@ -8147,7 +8054,7 @@ export class Document {
|
|
|
8147
8054
|
// --- Case 2: Style format "StyleName,Level," (e.g., "Heading 2,2,") ---
|
|
8148
8055
|
// Split by comma, expect pattern: styleName, level, [optional trailing comma]
|
|
8149
8056
|
const parts = content
|
|
8150
|
-
.split(
|
|
8057
|
+
.split(',')
|
|
8151
8058
|
.map((p) => p.trim())
|
|
8152
8059
|
.filter(Boolean);
|
|
8153
8060
|
for (let i = 0; i < parts.length; i += 2) {
|
|
@@ -8207,14 +8114,14 @@ export class Document {
|
|
|
8207
8114
|
|
|
8208
8115
|
// Decode XML entities in instruction
|
|
8209
8116
|
const fieldInstruction = match[1]
|
|
8210
|
-
.replace(/&/g,
|
|
8211
|
-
.replace(/</g,
|
|
8212
|
-
.replace(/>/g,
|
|
8117
|
+
.replace(/&/g, '&')
|
|
8118
|
+
.replace(/</g, '<')
|
|
8119
|
+
.replace(/>/g, '>')
|
|
8213
8120
|
.replace(/"/g, '"')
|
|
8214
8121
|
.replace(/'/g, "'");
|
|
8215
8122
|
|
|
8216
8123
|
// Check if instruction contains \t switches (style-specific TOC)
|
|
8217
|
-
if (!fieldInstruction.includes(
|
|
8124
|
+
if (!fieldInstruction.includes('\\t')) {
|
|
8218
8125
|
continue; // Outline-based TOC, no style names to sync
|
|
8219
8126
|
}
|
|
8220
8127
|
|
|
@@ -8225,11 +8132,11 @@ export class Document {
|
|
|
8225
8132
|
if (updatedInstruction !== fieldInstruction) {
|
|
8226
8133
|
// Re-encode for XML
|
|
8227
8134
|
const encodedInstruction = updatedInstruction
|
|
8228
|
-
.replace(/&/g,
|
|
8229
|
-
.replace(/</g,
|
|
8230
|
-
.replace(/>/g,
|
|
8231
|
-
.replace(/"/g,
|
|
8232
|
-
.replace(/'/g,
|
|
8135
|
+
.replace(/&/g, '&')
|
|
8136
|
+
.replace(/</g, '<')
|
|
8137
|
+
.replace(/>/g, '>')
|
|
8138
|
+
.replace(/"/g, '"')
|
|
8139
|
+
.replace(/'/g, ''');
|
|
8233
8140
|
|
|
8234
8141
|
// Replace the instruction
|
|
8235
8142
|
const updatedInstrXml = `<w:instrText xml:space="preserve">${encodedInstruction}</w:instrText>`;
|
|
@@ -8250,7 +8157,7 @@ export class Document {
|
|
|
8250
8157
|
} catch (error: unknown) {
|
|
8251
8158
|
// Log error but don't fail the save
|
|
8252
8159
|
this.logger.error(
|
|
8253
|
-
|
|
8160
|
+
'Error syncing TOC field instructions - document will save with original instructions',
|
|
8254
8161
|
error instanceof Error
|
|
8255
8162
|
? { message: error.message, stack: error.stack }
|
|
8256
8163
|
: { error: String(error) }
|
|
@@ -8263,9 +8170,7 @@ export class Document {
|
|
|
8263
8170
|
* Helper to sync TOC instruction within an XML fragment
|
|
8264
8171
|
* @private
|
|
8265
8172
|
*/
|
|
8266
|
-
private syncTOCInstructionInXml(
|
|
8267
|
-
xml: string
|
|
8268
|
-
): { xml: string; changed: boolean } {
|
|
8173
|
+
private syncTOCInstructionInXml(xml: string): { xml: string; changed: boolean } {
|
|
8269
8174
|
const instrMatch = /<w:instrText[^>]*>([\s\S]*?)<\/w:instrText>/.exec(xml);
|
|
8270
8175
|
if (!instrMatch?.[1]) {
|
|
8271
8176
|
return { xml, changed: false };
|
|
@@ -8273,14 +8178,14 @@ export class Document {
|
|
|
8273
8178
|
|
|
8274
8179
|
// Decode XML entities in instruction
|
|
8275
8180
|
const fieldInstruction = instrMatch[1]
|
|
8276
|
-
.replace(/&/g,
|
|
8277
|
-
.replace(/</g,
|
|
8278
|
-
.replace(/>/g,
|
|
8181
|
+
.replace(/&/g, '&')
|
|
8182
|
+
.replace(/</g, '<')
|
|
8183
|
+
.replace(/>/g, '>')
|
|
8279
8184
|
.replace(/"/g, '"')
|
|
8280
8185
|
.replace(/'/g, "'");
|
|
8281
8186
|
|
|
8282
8187
|
// Check if instruction contains \t switches (style-specific TOC)
|
|
8283
|
-
if (!fieldInstruction.includes(
|
|
8188
|
+
if (!fieldInstruction.includes('\\t')) {
|
|
8284
8189
|
return { xml, changed: false };
|
|
8285
8190
|
}
|
|
8286
8191
|
|
|
@@ -8293,11 +8198,11 @@ export class Document {
|
|
|
8293
8198
|
|
|
8294
8199
|
// Re-encode for XML
|
|
8295
8200
|
const encodedInstruction = updatedInstruction
|
|
8296
|
-
.replace(/&/g,
|
|
8297
|
-
.replace(/</g,
|
|
8298
|
-
.replace(/>/g,
|
|
8299
|
-
.replace(/"/g,
|
|
8300
|
-
.replace(/'/g,
|
|
8201
|
+
.replace(/&/g, '&')
|
|
8202
|
+
.replace(/</g, '<')
|
|
8203
|
+
.replace(/>/g, '>')
|
|
8204
|
+
.replace(/"/g, '"')
|
|
8205
|
+
.replace(/'/g, ''');
|
|
8301
8206
|
|
|
8302
8207
|
const updatedXml = xml.replace(
|
|
8303
8208
|
/<w:instrText[^>]*>[\s\S]*?<\/w:instrText>/,
|
|
@@ -8331,7 +8236,10 @@ export class Document {
|
|
|
8331
8236
|
|
|
8332
8237
|
// Parse style names and levels from the switch content
|
|
8333
8238
|
// Format: "StyleName,Level," or "StyleName,Level,StyleName2,Level2,..."
|
|
8334
|
-
const parts = content
|
|
8239
|
+
const parts = content
|
|
8240
|
+
.split(',')
|
|
8241
|
+
.map((p) => p.trim())
|
|
8242
|
+
.filter(Boolean);
|
|
8335
8243
|
const updatedParts: string[] = [];
|
|
8336
8244
|
|
|
8337
8245
|
for (let i = 0; i < parts.length; i += 2) {
|
|
@@ -8496,31 +8404,30 @@ export class Document {
|
|
|
8496
8404
|
docXml: string,
|
|
8497
8405
|
levels: number[]
|
|
8498
8406
|
): { level: number; text: string; bookmark: string }[] {
|
|
8499
|
-
const headings: { level: number; text: string; bookmark: string }[] =
|
|
8500
|
-
[];
|
|
8407
|
+
const headings: { level: number; text: string; bookmark: string }[] = [];
|
|
8501
8408
|
const levelSet = new Set(levels);
|
|
8502
8409
|
|
|
8503
8410
|
try {
|
|
8504
8411
|
// Parse document.xml to object structure
|
|
8505
8412
|
const parsed = XMLParser.parseToObject(docXml, { trimValues: false });
|
|
8506
|
-
const document = parsed[
|
|
8413
|
+
const document = parsed['w:document'];
|
|
8507
8414
|
if (!document) {
|
|
8508
8415
|
return headings;
|
|
8509
8416
|
}
|
|
8510
8417
|
|
|
8511
|
-
const body = (document as any)[
|
|
8418
|
+
const body = (document as any)['w:body'];
|
|
8512
8419
|
if (!body) {
|
|
8513
8420
|
return headings;
|
|
8514
8421
|
}
|
|
8515
8422
|
|
|
8516
8423
|
// Helper function to extract heading info from a parsed paragraph object
|
|
8517
8424
|
const extractHeading = (para: any): void => {
|
|
8518
|
-
const pPr = para[
|
|
8519
|
-
if (!pPr?.[
|
|
8425
|
+
const pPr = para['w:pPr'];
|
|
8426
|
+
if (!pPr?.['w:pStyle']) {
|
|
8520
8427
|
return;
|
|
8521
8428
|
}
|
|
8522
8429
|
|
|
8523
|
-
const styleVal = pPr[
|
|
8430
|
+
const styleVal = pPr['w:pStyle']['@_w:val'];
|
|
8524
8431
|
if (!styleVal) {
|
|
8525
8432
|
return;
|
|
8526
8433
|
}
|
|
@@ -8539,26 +8446,27 @@ export class Document {
|
|
|
8539
8446
|
}
|
|
8540
8447
|
|
|
8541
8448
|
// Extract bookmark (use any existing bookmark, prioritize "_heading" or "_Toc")
|
|
8542
|
-
let bookmark =
|
|
8543
|
-
const bookmarkStart = para[
|
|
8449
|
+
let bookmark = '';
|
|
8450
|
+
const bookmarkStart = para['w:bookmarkStart'];
|
|
8544
8451
|
if (bookmarkStart) {
|
|
8545
|
-
const bookmarkArray = Array.isArray(bookmarkStart)
|
|
8546
|
-
|
|
8547
|
-
: [bookmarkStart];
|
|
8548
|
-
|
|
8452
|
+
const bookmarkArray = Array.isArray(bookmarkStart) ? bookmarkStart : [bookmarkStart];
|
|
8453
|
+
|
|
8549
8454
|
// First try to find preferred bookmark types
|
|
8550
8455
|
for (const bm of bookmarkArray) {
|
|
8551
|
-
const bmName = bm[
|
|
8552
|
-
if (
|
|
8456
|
+
const bmName = bm['@_w:name'];
|
|
8457
|
+
if (
|
|
8458
|
+
bmName &&
|
|
8459
|
+
(bmName.toLowerCase().includes('_heading') || bmName.toLowerCase().includes('_toc'))
|
|
8460
|
+
) {
|
|
8553
8461
|
bookmark = bmName;
|
|
8554
8462
|
break;
|
|
8555
8463
|
}
|
|
8556
8464
|
}
|
|
8557
|
-
|
|
8465
|
+
|
|
8558
8466
|
// If no preferred bookmark found, use the first available bookmark
|
|
8559
8467
|
if (!bookmark && bookmarkArray.length > 0) {
|
|
8560
8468
|
const firstBm = bookmarkArray[0];
|
|
8561
|
-
const bmName = firstBm[
|
|
8469
|
+
const bmName = firstBm['@_w:name'];
|
|
8562
8470
|
if (bmName) {
|
|
8563
8471
|
bookmark = bmName;
|
|
8564
8472
|
}
|
|
@@ -8566,17 +8474,17 @@ export class Document {
|
|
|
8566
8474
|
}
|
|
8567
8475
|
|
|
8568
8476
|
// Extract text from runs
|
|
8569
|
-
let text =
|
|
8570
|
-
const runs = para[
|
|
8477
|
+
let text = '';
|
|
8478
|
+
const runs = para['w:r'];
|
|
8571
8479
|
if (runs) {
|
|
8572
8480
|
const runArray = Array.isArray(runs) ? runs : [runs];
|
|
8573
8481
|
for (const run of runArray) {
|
|
8574
|
-
const textElement = run[
|
|
8482
|
+
const textElement = run['w:t'];
|
|
8575
8483
|
if (textElement) {
|
|
8576
|
-
if (typeof textElement ===
|
|
8484
|
+
if (typeof textElement === 'string') {
|
|
8577
8485
|
text += textElement;
|
|
8578
|
-
} else if (textElement[
|
|
8579
|
-
text += textElement[
|
|
8486
|
+
} else if (textElement['#text']) {
|
|
8487
|
+
text += textElement['#text'];
|
|
8580
8488
|
}
|
|
8581
8489
|
}
|
|
8582
8490
|
}
|
|
@@ -8601,7 +8509,7 @@ export class Document {
|
|
|
8601
8509
|
};
|
|
8602
8510
|
|
|
8603
8511
|
// Search in direct paragraphs
|
|
8604
|
-
const paragraphs = body[
|
|
8512
|
+
const paragraphs = body['w:p'];
|
|
8605
8513
|
if (paragraphs) {
|
|
8606
8514
|
const paraArray = Array.isArray(paragraphs) ? paragraphs : [paragraphs];
|
|
8607
8515
|
for (const para of paraArray) {
|
|
@@ -8610,26 +8518,24 @@ export class Document {
|
|
|
8610
8518
|
}
|
|
8611
8519
|
|
|
8612
8520
|
// Search in tables (this is critical - many documents have headings in tables)
|
|
8613
|
-
const tables = body[
|
|
8521
|
+
const tables = body['w:tbl'];
|
|
8614
8522
|
if (tables) {
|
|
8615
8523
|
const tableArray = Array.isArray(tables) ? tables : [tables];
|
|
8616
8524
|
for (const table of tableArray) {
|
|
8617
|
-
const rows = table[
|
|
8525
|
+
const rows = table['w:tr'];
|
|
8618
8526
|
if (!rows) continue;
|
|
8619
8527
|
|
|
8620
8528
|
const rowArray = Array.isArray(rows) ? rows : [rows];
|
|
8621
8529
|
for (const row of rowArray) {
|
|
8622
|
-
const cells = row[
|
|
8530
|
+
const cells = row['w:tc'];
|
|
8623
8531
|
if (!cells) continue;
|
|
8624
8532
|
|
|
8625
8533
|
const cellArray = Array.isArray(cells) ? cells : [cells];
|
|
8626
8534
|
for (const cell of cellArray) {
|
|
8627
|
-
const cellParas = cell[
|
|
8535
|
+
const cellParas = cell['w:p'];
|
|
8628
8536
|
if (!cellParas) continue;
|
|
8629
8537
|
|
|
8630
|
-
const cellParaArray = Array.isArray(cellParas)
|
|
8631
|
-
? cellParas
|
|
8632
|
-
: [cellParas];
|
|
8538
|
+
const cellParaArray = Array.isArray(cellParas) ? cellParas : [cellParas];
|
|
8633
8539
|
for (const para of cellParaArray) {
|
|
8634
8540
|
extractHeading(para);
|
|
8635
8541
|
}
|
|
@@ -8639,7 +8545,7 @@ export class Document {
|
|
|
8639
8545
|
}
|
|
8640
8546
|
} catch (error: unknown) {
|
|
8641
8547
|
defaultLogger.error(
|
|
8642
|
-
|
|
8548
|
+
'Error parsing document.xml for headings:',
|
|
8643
8549
|
error instanceof Error
|
|
8644
8550
|
? { message: error.message, stack: error.stack }
|
|
8645
8551
|
: { error: String(error) }
|
|
@@ -8657,8 +8563,7 @@ export class Document {
|
|
|
8657
8563
|
private findHeadingsForTOC(
|
|
8658
8564
|
levels: number[]
|
|
8659
8565
|
): { level: number; text: string; bookmark: string }[] {
|
|
8660
|
-
const headings: { level: number; text: string; bookmark: string }[] =
|
|
8661
|
-
[];
|
|
8566
|
+
const headings: { level: number; text: string; bookmark: string }[] = [];
|
|
8662
8567
|
const levelSet = new Set(levels);
|
|
8663
8568
|
|
|
8664
8569
|
// Iterate through body elements
|
|
@@ -8679,8 +8584,7 @@ export class Document {
|
|
|
8679
8584
|
|
|
8680
8585
|
if (text) {
|
|
8681
8586
|
// Create or get bookmark for this heading
|
|
8682
|
-
const bookmark =
|
|
8683
|
-
this.bookmarkManager.createHeadingBookmark(text);
|
|
8587
|
+
const bookmark = this.bookmarkManager.createHeadingBookmark(text);
|
|
8684
8588
|
|
|
8685
8589
|
headings.push({
|
|
8686
8590
|
level: headingLevel,
|
|
@@ -8715,30 +8619,28 @@ export class Document {
|
|
|
8715
8619
|
): string {
|
|
8716
8620
|
const sdtId = Math.floor(Math.random() * 2000000000) - 1000000000;
|
|
8717
8621
|
|
|
8718
|
-
let tocXml =
|
|
8622
|
+
let tocXml = '<w:sdt>';
|
|
8719
8623
|
|
|
8720
8624
|
// SDT properties
|
|
8721
|
-
tocXml +=
|
|
8625
|
+
tocXml += '<w:sdtPr>';
|
|
8722
8626
|
tocXml += `<w:id w:val="${sdtId}"/>`;
|
|
8723
|
-
tocXml +=
|
|
8627
|
+
tocXml += '<w:docPartObj>';
|
|
8724
8628
|
tocXml += '<w:docPartGallery w:val="Table of Contents"/>';
|
|
8725
8629
|
tocXml += '<w:docPartUnique w:val="1"/>';
|
|
8726
|
-
tocXml +=
|
|
8727
|
-
tocXml +=
|
|
8630
|
+
tocXml += '</w:docPartObj>';
|
|
8631
|
+
tocXml += '</w:sdtPr>';
|
|
8728
8632
|
|
|
8729
8633
|
// SDT content
|
|
8730
|
-
tocXml +=
|
|
8634
|
+
tocXml += '<w:sdtContent>';
|
|
8731
8635
|
|
|
8732
8636
|
// Calculate minimum level for relative indentation
|
|
8733
8637
|
// 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;
|
|
8638
|
+
const minLevel = headings.length > 0 ? Math.min(...headings.map((h) => h.level)) : 1;
|
|
8736
8639
|
|
|
8737
8640
|
// 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"/>';
|
|
8641
|
+
tocXml += '<w:p>';
|
|
8642
|
+
tocXml += '<w:pPr>';
|
|
8643
|
+
tocXml += '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
|
|
8742
8644
|
|
|
8743
8645
|
// Add indentation for first entry relative to minimum level (0.25" per level)
|
|
8744
8646
|
if (headings.length > 0 && headings[0]) {
|
|
@@ -8748,17 +8650,17 @@ export class Document {
|
|
|
8748
8650
|
}
|
|
8749
8651
|
}
|
|
8750
8652
|
|
|
8751
|
-
tocXml +=
|
|
8653
|
+
tocXml += '</w:pPr>';
|
|
8752
8654
|
|
|
8753
8655
|
// Field begin
|
|
8754
8656
|
tocXml += '<w:r><w:fldChar w:fldCharType="begin"/></w:r>';
|
|
8755
8657
|
|
|
8756
8658
|
// Field instruction (preserve original switches)
|
|
8757
|
-
tocXml +=
|
|
8659
|
+
tocXml += '<w:r>';
|
|
8758
8660
|
tocXml += `<w:instrText xml:space="preserve">${this.escapeXml(
|
|
8759
8661
|
originalInstrText
|
|
8760
8662
|
)}</w:instrText>`;
|
|
8761
|
-
tocXml +=
|
|
8663
|
+
tocXml += '</w:r>';
|
|
8762
8664
|
|
|
8763
8665
|
// Field separator
|
|
8764
8666
|
tocXml += '<w:r><w:fldChar w:fldCharType="separate"/></w:r>';
|
|
@@ -8768,17 +8670,16 @@ export class Document {
|
|
|
8768
8670
|
tocXml += this.buildTOCEntryXML(headings[0]);
|
|
8769
8671
|
}
|
|
8770
8672
|
|
|
8771
|
-
tocXml +=
|
|
8673
|
+
tocXml += '</w:p>';
|
|
8772
8674
|
|
|
8773
8675
|
// Remaining entries (each in its own paragraph)
|
|
8774
8676
|
for (let i = 1; i < headings.length; i++) {
|
|
8775
8677
|
const heading = headings[i];
|
|
8776
8678
|
if (!heading) continue;
|
|
8777
8679
|
|
|
8778
|
-
tocXml +=
|
|
8779
|
-
tocXml +=
|
|
8780
|
-
tocXml +=
|
|
8781
|
-
'<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
|
|
8680
|
+
tocXml += '<w:p>';
|
|
8681
|
+
tocXml += '<w:pPr>';
|
|
8682
|
+
tocXml += '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
|
|
8782
8683
|
|
|
8783
8684
|
// Add indentation relative to minimum level (0.25" per level above minimum)
|
|
8784
8685
|
const indent = (heading.level - minLevel) * 360;
|
|
@@ -8786,22 +8687,21 @@ export class Document {
|
|
|
8786
8687
|
tocXml += `<w:ind w:left="${indent}"/>`;
|
|
8787
8688
|
}
|
|
8788
8689
|
|
|
8789
|
-
tocXml +=
|
|
8690
|
+
tocXml += '</w:pPr>';
|
|
8790
8691
|
tocXml += this.buildTOCEntryXML(heading);
|
|
8791
|
-
tocXml +=
|
|
8692
|
+
tocXml += '</w:p>';
|
|
8792
8693
|
}
|
|
8793
8694
|
|
|
8794
8695
|
// Final paragraph with field end
|
|
8795
|
-
tocXml +=
|
|
8796
|
-
tocXml +=
|
|
8797
|
-
tocXml +=
|
|
8798
|
-
|
|
8799
|
-
tocXml += "</w:pPr>";
|
|
8696
|
+
tocXml += '<w:p>';
|
|
8697
|
+
tocXml += '<w:pPr>';
|
|
8698
|
+
tocXml += '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
|
|
8699
|
+
tocXml += '</w:pPr>';
|
|
8800
8700
|
tocXml += '<w:r><w:fldChar w:fldCharType="end"/></w:r>';
|
|
8801
|
-
tocXml +=
|
|
8701
|
+
tocXml += '</w:p>';
|
|
8802
8702
|
|
|
8803
|
-
tocXml +=
|
|
8804
|
-
tocXml +=
|
|
8703
|
+
tocXml += '</w:sdtContent>';
|
|
8704
|
+
tocXml += '</w:sdt>';
|
|
8805
8705
|
|
|
8806
8706
|
return tocXml;
|
|
8807
8707
|
}
|
|
@@ -8812,27 +8712,22 @@ export class Document {
|
|
|
8812
8712
|
* @param heading Heading information
|
|
8813
8713
|
* @returns XML string for the TOC entry
|
|
8814
8714
|
*/
|
|
8815
|
-
private buildTOCEntryXML(heading: {
|
|
8816
|
-
level: number;
|
|
8817
|
-
text: string;
|
|
8818
|
-
bookmark: string;
|
|
8819
|
-
}): string {
|
|
8715
|
+
private buildTOCEntryXML(heading: { level: number; text: string; bookmark: string }): string {
|
|
8820
8716
|
const escapedText = this.escapeXml(heading.text);
|
|
8821
8717
|
|
|
8822
|
-
let xml =
|
|
8718
|
+
let xml = '';
|
|
8823
8719
|
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"/>';
|
|
8720
|
+
xml += '<w:r>';
|
|
8721
|
+
xml += '<w:rPr>';
|
|
8722
|
+
xml += '<w:rFonts w:ascii="Verdana" w:hAnsi="Verdana" w:cs="Verdana" w:eastAsia="Verdana"/>';
|
|
8828
8723
|
xml += '<w:color w:val="0000FF"/>';
|
|
8829
8724
|
xml += '<w:sz w:val="24"/>';
|
|
8830
8725
|
xml += '<w:szCs w:val="24"/>';
|
|
8831
8726
|
xml += '<w:u w:val="single"/>';
|
|
8832
|
-
xml +=
|
|
8727
|
+
xml += '</w:rPr>';
|
|
8833
8728
|
xml += `<w:t xml:space="preserve">${escapedText}</w:t>`;
|
|
8834
|
-
xml +=
|
|
8835
|
-
xml +=
|
|
8729
|
+
xml += '</w:r>';
|
|
8730
|
+
xml += '</w:hyperlink>';
|
|
8836
8731
|
|
|
8837
8732
|
return xml;
|
|
8838
8733
|
}
|
|
@@ -8845,11 +8740,11 @@ export class Document {
|
|
|
8845
8740
|
*/
|
|
8846
8741
|
private escapeXml(text: string): string {
|
|
8847
8742
|
return text
|
|
8848
|
-
.replace(/&/g,
|
|
8849
|
-
.replace(/</g,
|
|
8850
|
-
.replace(/>/g,
|
|
8851
|
-
.replace(/"/g,
|
|
8852
|
-
.replace(/'/g,
|
|
8743
|
+
.replace(/&/g, '&')
|
|
8744
|
+
.replace(/</g, '<')
|
|
8745
|
+
.replace(/>/g, '>')
|
|
8746
|
+
.replace(/"/g, '"')
|
|
8747
|
+
.replace(/'/g, ''');
|
|
8853
8748
|
}
|
|
8854
8749
|
|
|
8855
8750
|
/**
|
|
@@ -8866,7 +8761,7 @@ export class Document {
|
|
|
8866
8761
|
await handler.load(filePath);
|
|
8867
8762
|
|
|
8868
8763
|
// Get document.xml
|
|
8869
|
-
const docXml = handler.getFileAsString(
|
|
8764
|
+
const docXml = handler.getFileAsString('word/document.xml');
|
|
8870
8765
|
if (!docXml) {
|
|
8871
8766
|
return 0;
|
|
8872
8767
|
}
|
|
@@ -8876,7 +8771,7 @@ export class Document {
|
|
|
8876
8771
|
|
|
8877
8772
|
// Update and save if changes were made
|
|
8878
8773
|
if (modifiedXml !== docXml) {
|
|
8879
|
-
handler.updateFile(
|
|
8774
|
+
handler.updateFile('word/document.xml', modifiedXml);
|
|
8880
8775
|
await handler.save(filePath);
|
|
8881
8776
|
|
|
8882
8777
|
// Count TOCs that were populated
|
|
@@ -8913,9 +8808,9 @@ export class Document {
|
|
|
8913
8808
|
if (!instrMatch?.[1]) continue;
|
|
8914
8809
|
|
|
8915
8810
|
const fieldInstruction = instrMatch[1]
|
|
8916
|
-
.replace(/&/g,
|
|
8917
|
-
.replace(/</g,
|
|
8918
|
-
.replace(/>/g,
|
|
8811
|
+
.replace(/&/g, '&')
|
|
8812
|
+
.replace(/</g, '<')
|
|
8813
|
+
.replace(/>/g, '>')
|
|
8919
8814
|
.replace(/"/g, '"')
|
|
8920
8815
|
.replace(/'/g, "'");
|
|
8921
8816
|
|
|
@@ -8927,7 +8822,7 @@ export class Document {
|
|
|
8927
8822
|
modifiedXml = modifiedXml.replace(tocXml, newTocXml);
|
|
8928
8823
|
} catch (error: unknown) {
|
|
8929
8824
|
this.logger.error(
|
|
8930
|
-
|
|
8825
|
+
'Error populating SDT TOC',
|
|
8931
8826
|
error instanceof Error
|
|
8932
8827
|
? { message: error.message, stack: error.stack }
|
|
8933
8828
|
: { error: String(error) }
|
|
@@ -8952,9 +8847,9 @@ export class Document {
|
|
|
8952
8847
|
if (!instrText) continue;
|
|
8953
8848
|
|
|
8954
8849
|
const fieldInstruction = instrText
|
|
8955
|
-
.replace(/&/g,
|
|
8956
|
-
.replace(/</g,
|
|
8957
|
-
.replace(/>/g,
|
|
8850
|
+
.replace(/&/g, '&')
|
|
8851
|
+
.replace(/</g, '<')
|
|
8852
|
+
.replace(/>/g, '>')
|
|
8958
8853
|
.replace(/"/g, '"')
|
|
8959
8854
|
.replace(/'/g, "'");
|
|
8960
8855
|
|
|
@@ -8989,12 +8884,10 @@ export class Document {
|
|
|
8989
8884
|
const newTocXml = this.generateSimpleTOCXML(headings, fieldInstruction);
|
|
8990
8885
|
modifiedXml = modifiedXml.replace(fullTocXml, newTocXml);
|
|
8991
8886
|
|
|
8992
|
-
this.logger.info(
|
|
8993
|
-
`Populated simple TOC with ${headings.length} heading entries`
|
|
8994
|
-
);
|
|
8887
|
+
this.logger.info(`Populated simple TOC with ${headings.length} heading entries`);
|
|
8995
8888
|
} catch (error: unknown) {
|
|
8996
8889
|
this.logger.error(
|
|
8997
|
-
|
|
8890
|
+
'Error populating simple TOC',
|
|
8998
8891
|
error instanceof Error
|
|
8999
8892
|
? { message: error.message, stack: error.stack }
|
|
9000
8893
|
: { error: String(error) }
|
|
@@ -9029,29 +8922,29 @@ export class Document {
|
|
|
9029
8922
|
|
|
9030
8923
|
// Escape text for XML
|
|
9031
8924
|
const escapedText = heading.text
|
|
9032
|
-
.replace(/&/g,
|
|
9033
|
-
.replace(/</g,
|
|
9034
|
-
.replace(/>/g,
|
|
9035
|
-
.replace(/"/g,
|
|
8925
|
+
.replace(/&/g, '&')
|
|
8926
|
+
.replace(/</g, '<')
|
|
8927
|
+
.replace(/>/g, '>')
|
|
8928
|
+
.replace(/"/g, '"');
|
|
9036
8929
|
|
|
9037
8930
|
// Create hyperlinked TOC entry paragraph
|
|
9038
8931
|
entries.push(
|
|
9039
8932
|
`<w:p>` +
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
8933
|
+
`<w:pPr><w:pStyle w:val="${tocStyle}"/></w:pPr>` +
|
|
8934
|
+
`<w:hyperlink w:anchor="${heading.bookmark}" w:history="1">` +
|
|
8935
|
+
`<w:r><w:rPr><w:rStyle w:val="Hyperlink"/></w:rPr>` +
|
|
8936
|
+
`<w:t>${escapedText}</w:t></w:r>` +
|
|
8937
|
+
`</w:hyperlink>` +
|
|
8938
|
+
`</w:p>`
|
|
9046
8939
|
);
|
|
9047
8940
|
}
|
|
9048
8941
|
|
|
9049
8942
|
// Escape field instruction for XML
|
|
9050
8943
|
const escapedInstruction = fieldInstruction
|
|
9051
|
-
.replace(/&/g,
|
|
9052
|
-
.replace(/</g,
|
|
9053
|
-
.replace(/>/g,
|
|
9054
|
-
.replace(/"/g,
|
|
8944
|
+
.replace(/&/g, '&')
|
|
8945
|
+
.replace(/</g, '<')
|
|
8946
|
+
.replace(/>/g, '>')
|
|
8947
|
+
.replace(/"/g, '"');
|
|
9055
8948
|
|
|
9056
8949
|
// Build the complete TOC field structure
|
|
9057
8950
|
return (
|
|
@@ -9063,7 +8956,7 @@ export class Document {
|
|
|
9063
8956
|
`<w:r><w:fldChar w:fldCharType="separate"/></w:r>` +
|
|
9064
8957
|
`</w:p>` +
|
|
9065
8958
|
// TOC entry paragraphs (between separate and end)
|
|
9066
|
-
entries.join(
|
|
8959
|
+
entries.join('') +
|
|
9067
8960
|
// Last paragraph: fldChar end
|
|
9068
8961
|
`<w:p>` +
|
|
9069
8962
|
`<w:pPr><w:pStyle w:val="TOC2"/></w:pPr>` +
|
|
@@ -9127,11 +9020,7 @@ export class Document {
|
|
|
9127
9020
|
* @param orientation Page orientation
|
|
9128
9021
|
* @returns This document for chaining
|
|
9129
9022
|
*/
|
|
9130
|
-
setPageSize(
|
|
9131
|
-
width: number,
|
|
9132
|
-
height: number,
|
|
9133
|
-
orientation?: "portrait" | "landscape"
|
|
9134
|
-
): this {
|
|
9023
|
+
setPageSize(width: number, height: number, orientation?: 'portrait' | 'landscape'): this {
|
|
9135
9024
|
this.section.setPageSize(width, height, orientation);
|
|
9136
9025
|
return this;
|
|
9137
9026
|
}
|
|
@@ -9141,7 +9030,7 @@ export class Document {
|
|
|
9141
9030
|
* @param orientation Page orientation
|
|
9142
9031
|
* @returns This document for chaining
|
|
9143
9032
|
*/
|
|
9144
|
-
setPageOrientation(orientation:
|
|
9033
|
+
setPageOrientation(orientation: 'portrait' | 'landscape'): this {
|
|
9145
9034
|
this.section.setOrientation(orientation);
|
|
9146
9035
|
return this;
|
|
9147
9036
|
}
|
|
@@ -9171,15 +9060,13 @@ export class Document {
|
|
|
9171
9060
|
*/
|
|
9172
9061
|
setHeader(header: Header): this {
|
|
9173
9062
|
// Generate relationship for header
|
|
9174
|
-
const relationship = this.relationshipManager.addHeader(
|
|
9175
|
-
`${header.getFilename(1)}`
|
|
9176
|
-
);
|
|
9063
|
+
const relationship = this.relationshipManager.addHeader(`${header.getFilename(1)}`);
|
|
9177
9064
|
|
|
9178
9065
|
// Register with manager
|
|
9179
9066
|
this.headerFooterManager.registerHeader(header, relationship.getId());
|
|
9180
9067
|
|
|
9181
9068
|
// Link to section
|
|
9182
|
-
this.section.setHeaderReference(
|
|
9069
|
+
this.section.setHeaderReference('default', relationship.getId());
|
|
9183
9070
|
|
|
9184
9071
|
return this;
|
|
9185
9072
|
}
|
|
@@ -9202,7 +9089,7 @@ export class Document {
|
|
|
9202
9089
|
this.headerFooterManager.registerHeader(header, relationship.getId());
|
|
9203
9090
|
|
|
9204
9091
|
// Link to section
|
|
9205
|
-
this.section.setHeaderReference(
|
|
9092
|
+
this.section.setHeaderReference('first', relationship.getId());
|
|
9206
9093
|
|
|
9207
9094
|
return this;
|
|
9208
9095
|
}
|
|
@@ -9222,7 +9109,7 @@ export class Document {
|
|
|
9222
9109
|
this.headerFooterManager.registerHeader(header, relationship.getId());
|
|
9223
9110
|
|
|
9224
9111
|
// Link to section
|
|
9225
|
-
this.section.setHeaderReference(
|
|
9112
|
+
this.section.setHeaderReference('even', relationship.getId());
|
|
9226
9113
|
|
|
9227
9114
|
return this;
|
|
9228
9115
|
}
|
|
@@ -9234,15 +9121,13 @@ export class Document {
|
|
|
9234
9121
|
*/
|
|
9235
9122
|
setFooter(footer: Footer): this {
|
|
9236
9123
|
// Generate relationship for footer
|
|
9237
|
-
const relationship = this.relationshipManager.addFooter(
|
|
9238
|
-
`${footer.getFilename(1)}`
|
|
9239
|
-
);
|
|
9124
|
+
const relationship = this.relationshipManager.addFooter(`${footer.getFilename(1)}`);
|
|
9240
9125
|
|
|
9241
9126
|
// Register with manager
|
|
9242
9127
|
this.headerFooterManager.registerFooter(footer, relationship.getId());
|
|
9243
9128
|
|
|
9244
9129
|
// Link to section
|
|
9245
|
-
this.section.setFooterReference(
|
|
9130
|
+
this.section.setFooterReference('default', relationship.getId());
|
|
9246
9131
|
|
|
9247
9132
|
return this;
|
|
9248
9133
|
}
|
|
@@ -9265,7 +9150,7 @@ export class Document {
|
|
|
9265
9150
|
this.headerFooterManager.registerFooter(footer, relationship.getId());
|
|
9266
9151
|
|
|
9267
9152
|
// Link to section
|
|
9268
|
-
this.section.setFooterReference(
|
|
9153
|
+
this.section.setFooterReference('first', relationship.getId());
|
|
9269
9154
|
|
|
9270
9155
|
return this;
|
|
9271
9156
|
}
|
|
@@ -9285,7 +9170,7 @@ export class Document {
|
|
|
9285
9170
|
this.headerFooterManager.registerFooter(footer, relationship.getId());
|
|
9286
9171
|
|
|
9287
9172
|
// Link to section
|
|
9288
|
-
this.section.setFooterReference(
|
|
9173
|
+
this.section.setFooterReference('even', relationship.getId());
|
|
9289
9174
|
|
|
9290
9175
|
return this;
|
|
9291
9176
|
}
|
|
@@ -9305,7 +9190,7 @@ export class Document {
|
|
|
9305
9190
|
* @param type Header type to remove (default, first, even)
|
|
9306
9191
|
* @returns This document for chaining
|
|
9307
9192
|
*/
|
|
9308
|
-
removeHeader(type:
|
|
9193
|
+
removeHeader(type: 'default' | 'first' | 'even'): this {
|
|
9309
9194
|
const sectionProps = this.section.getProperties();
|
|
9310
9195
|
|
|
9311
9196
|
// Get the relationship ID from section properties
|
|
@@ -9351,7 +9236,7 @@ export class Document {
|
|
|
9351
9236
|
* @param type Footer type to remove (default, first, even)
|
|
9352
9237
|
* @returns This document for chaining
|
|
9353
9238
|
*/
|
|
9354
|
-
removeFooter(type:
|
|
9239
|
+
removeFooter(type: 'default' | 'first' | 'even'): this {
|
|
9355
9240
|
const sectionProps = this.section.getProperties();
|
|
9356
9241
|
|
|
9357
9242
|
// Get the relationship ID from section properties
|
|
@@ -9400,7 +9285,7 @@ export class Document {
|
|
|
9400
9285
|
|
|
9401
9286
|
// Remove each header type
|
|
9402
9287
|
if (sectionProps.headers) {
|
|
9403
|
-
const types = Object.keys(sectionProps.headers) as (
|
|
9288
|
+
const types = Object.keys(sectionProps.headers) as ('default' | 'first' | 'even')[];
|
|
9404
9289
|
for (const type of types) {
|
|
9405
9290
|
this.removeHeader(type);
|
|
9406
9291
|
}
|
|
@@ -9422,7 +9307,7 @@ export class Document {
|
|
|
9422
9307
|
|
|
9423
9308
|
// Remove each footer type
|
|
9424
9309
|
if (sectionProps.footers) {
|
|
9425
|
-
const types = Object.keys(sectionProps.footers) as (
|
|
9310
|
+
const types = Object.keys(sectionProps.footers) as ('default' | 'first' | 'even')[];
|
|
9426
9311
|
for (const type of types) {
|
|
9427
9312
|
this.removeFooter(type);
|
|
9428
9313
|
}
|
|
@@ -9441,9 +9326,7 @@ export class Document {
|
|
|
9441
9326
|
*/
|
|
9442
9327
|
addImage(image: Image): this {
|
|
9443
9328
|
// Generate relationship ID
|
|
9444
|
-
const target = `media/image${
|
|
9445
|
-
this.imageManager.getImageCount() + 1
|
|
9446
|
-
}.${image.getExtension()}`;
|
|
9329
|
+
const target = `media/image${this.imageManager.getImageCount() + 1}.${image.getExtension()}`;
|
|
9447
9330
|
const relationship = this.relationshipManager.addImage(target);
|
|
9448
9331
|
|
|
9449
9332
|
// Register image with manager
|
|
@@ -9555,7 +9438,7 @@ export class Document {
|
|
|
9555
9438
|
*/
|
|
9556
9439
|
private updateRelationships(): void {
|
|
9557
9440
|
const xml = this.relationshipManager.generateXml();
|
|
9558
|
-
this.zipHandler.updateFile(
|
|
9441
|
+
this.zipHandler.updateFile('word/_rels/document.xml.rels', xml);
|
|
9559
9442
|
}
|
|
9560
9443
|
|
|
9561
9444
|
/**
|
|
@@ -9566,26 +9449,29 @@ export class Document {
|
|
|
9566
9449
|
// Comments were modified — regenerate from in-memory model
|
|
9567
9450
|
if (this.commentManager.getCount() > 0) {
|
|
9568
9451
|
const xml = this.commentManager.generateCommentsXml();
|
|
9569
|
-
this.zipHandler.addFile(
|
|
9452
|
+
this.zipHandler.addFile('word/comments.xml', xml);
|
|
9570
9453
|
|
|
9571
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9454
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9455
|
+
RelationshipType.COMMENTS
|
|
9456
|
+
);
|
|
9572
9457
|
if (existingRels.length === 0) {
|
|
9573
9458
|
this.relationshipManager.addComments();
|
|
9574
9459
|
}
|
|
9575
9460
|
} else {
|
|
9576
9461
|
// All comments removed — clean up comments.xml and its relationship
|
|
9577
|
-
this.zipHandler.removeFile(
|
|
9578
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9462
|
+
this.zipHandler.removeFile('word/comments.xml');
|
|
9463
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9464
|
+
RelationshipType.COMMENTS
|
|
9465
|
+
);
|
|
9579
9466
|
for (const rel of existingRels) {
|
|
9580
9467
|
this.relationshipManager.removeRelationship(rel.getId());
|
|
9581
9468
|
}
|
|
9582
9469
|
}
|
|
9583
9470
|
// Remove companion files since regenerated comments lack w14:paraId
|
|
9584
9471
|
this.removeCommentCompanionFiles();
|
|
9585
|
-
|
|
9586
9472
|
} else if (this._originalCommentsXml) {
|
|
9587
9473
|
// Passthrough — preserve original comments.xml exactly
|
|
9588
|
-
this.zipHandler.addFile(
|
|
9474
|
+
this.zipHandler.addFile('word/comments.xml', this._originalCommentsXml);
|
|
9589
9475
|
|
|
9590
9476
|
// Preserve companion files as-is
|
|
9591
9477
|
for (const [path, content] of this._originalCommentCompanionFiles) {
|
|
@@ -9593,17 +9479,20 @@ export class Document {
|
|
|
9593
9479
|
}
|
|
9594
9480
|
|
|
9595
9481
|
// Ensure comments relationship exists
|
|
9596
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9482
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9483
|
+
RelationshipType.COMMENTS
|
|
9484
|
+
);
|
|
9597
9485
|
if (existingRels.length === 0) {
|
|
9598
9486
|
this.relationshipManager.addComments();
|
|
9599
9487
|
}
|
|
9600
|
-
|
|
9601
9488
|
} else if (this.commentManager.getCount() > 0) {
|
|
9602
9489
|
// New comments created on a document that had no original comments
|
|
9603
9490
|
const xml = this.commentManager.generateCommentsXml();
|
|
9604
|
-
this.zipHandler.addFile(
|
|
9491
|
+
this.zipHandler.addFile('word/comments.xml', xml);
|
|
9605
9492
|
|
|
9606
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9493
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9494
|
+
RelationshipType.COMMENTS
|
|
9495
|
+
);
|
|
9607
9496
|
if (existingRels.length === 0) {
|
|
9608
9497
|
this.relationshipManager.addComments();
|
|
9609
9498
|
}
|
|
@@ -9611,7 +9500,11 @@ export class Document {
|
|
|
9611
9500
|
}
|
|
9612
9501
|
|
|
9613
9502
|
private removeCommentCompanionFiles(): void {
|
|
9614
|
-
const companionPaths = [
|
|
9503
|
+
const companionPaths = [
|
|
9504
|
+
DOCX_PATHS.COMMENTS_EXTENDED,
|
|
9505
|
+
DOCX_PATHS.COMMENTS_IDS,
|
|
9506
|
+
DOCX_PATHS.COMMENTS_EXTENSIBLE,
|
|
9507
|
+
];
|
|
9615
9508
|
for (const filePath of companionPaths) {
|
|
9616
9509
|
this.zipHandler.removeFile(filePath);
|
|
9617
9510
|
const fileName = filePath.replace('word/', '');
|
|
@@ -9628,7 +9521,9 @@ export class Document {
|
|
|
9628
9521
|
const xml = this.footnoteManager.generateFootnotesXml();
|
|
9629
9522
|
this.zipHandler.addFile(DOCX_PATHS.FOOTNOTES, xml);
|
|
9630
9523
|
|
|
9631
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9524
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9525
|
+
RelationshipType.FOOTNOTES
|
|
9526
|
+
);
|
|
9632
9527
|
if (existingRels.length === 0) {
|
|
9633
9528
|
this.relationshipManager.addFootnotes();
|
|
9634
9529
|
}
|
|
@@ -9636,14 +9531,16 @@ export class Document {
|
|
|
9636
9531
|
// Per OOXML, relationships are part-scoped: hyperlinks in footnotes.xml
|
|
9637
9532
|
// need word/_rels/footnotes.xml.rels, not word/_rels/document.xml.rels
|
|
9638
9533
|
this.generatePartLevelHyperlinkRels(
|
|
9639
|
-
this.footnoteManager.getAllFootnotes().flatMap(fn => fn.getParagraphs()),
|
|
9534
|
+
this.footnoteManager.getAllFootnotes().flatMap((fn) => fn.getParagraphs()),
|
|
9640
9535
|
'word/_rels/footnotes.xml.rels'
|
|
9641
9536
|
);
|
|
9642
9537
|
} else if (this._originalFootnotesXml) {
|
|
9643
9538
|
// Passthrough — preserve original XML exactly
|
|
9644
9539
|
this.zipHandler.addFile(DOCX_PATHS.FOOTNOTES, this._originalFootnotesXml);
|
|
9645
9540
|
|
|
9646
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9541
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9542
|
+
RelationshipType.FOOTNOTES
|
|
9543
|
+
);
|
|
9647
9544
|
if (existingRels.length === 0) {
|
|
9648
9545
|
this.relationshipManager.addFootnotes();
|
|
9649
9546
|
}
|
|
@@ -9655,7 +9552,9 @@ export class Document {
|
|
|
9655
9552
|
const xml = this.endnoteManager.generateEndnotesXml();
|
|
9656
9553
|
this.zipHandler.addFile(DOCX_PATHS.ENDNOTES, xml);
|
|
9657
9554
|
|
|
9658
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9555
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9556
|
+
RelationshipType.ENDNOTES
|
|
9557
|
+
);
|
|
9659
9558
|
if (existingRels.length === 0) {
|
|
9660
9559
|
this.relationshipManager.addEndnotes();
|
|
9661
9560
|
}
|
|
@@ -9663,14 +9562,16 @@ export class Document {
|
|
|
9663
9562
|
// Per OOXML, relationships are part-scoped: hyperlinks in endnotes.xml
|
|
9664
9563
|
// need word/_rels/endnotes.xml.rels, not word/_rels/document.xml.rels
|
|
9665
9564
|
this.generatePartLevelHyperlinkRels(
|
|
9666
|
-
this.endnoteManager.getAllEndnotes().flatMap(en => en.getParagraphs()),
|
|
9565
|
+
this.endnoteManager.getAllEndnotes().flatMap((en) => en.getParagraphs()),
|
|
9667
9566
|
'word/_rels/endnotes.xml.rels'
|
|
9668
9567
|
);
|
|
9669
9568
|
} else if (this._originalEndnotesXml) {
|
|
9670
9569
|
// Passthrough — preserve original XML exactly
|
|
9671
9570
|
this.zipHandler.addFile(DOCX_PATHS.ENDNOTES, this._originalEndnotesXml);
|
|
9672
9571
|
|
|
9673
|
-
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9572
|
+
const existingRels = this.relationshipManager.getRelationshipsByType(
|
|
9573
|
+
RelationshipType.ENDNOTES
|
|
9574
|
+
);
|
|
9674
9575
|
if (existingRels.length === 0) {
|
|
9675
9576
|
this.relationshipManager.addEndnotes();
|
|
9676
9577
|
}
|
|
@@ -9733,7 +9634,8 @@ export class Document {
|
|
|
9733
9634
|
|
|
9734
9635
|
if (hyperlinkRels.length > 0) {
|
|
9735
9636
|
let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
|
|
9736
|
-
xml +=
|
|
9637
|
+
xml +=
|
|
9638
|
+
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n';
|
|
9737
9639
|
for (const rel of hyperlinkRels) {
|
|
9738
9640
|
const escapedId = XMLBuilder.escapeXmlAttribute(rel.id);
|
|
9739
9641
|
const escapedUrl = XMLBuilder.escapeXmlAttribute(rel.url);
|
|
@@ -9814,14 +9716,23 @@ export class Document {
|
|
|
9814
9716
|
if (missingAuthors.length === 0) return;
|
|
9815
9717
|
|
|
9816
9718
|
// Build person elements for missing authors
|
|
9817
|
-
const personElements = missingAuthors
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9719
|
+
const personElements = missingAuthors
|
|
9720
|
+
.map((author) => {
|
|
9721
|
+
const escapedAuthor = author
|
|
9722
|
+
.replace(/&/g, '&')
|
|
9723
|
+
.replace(/"/g, '"')
|
|
9724
|
+
.replace(/</g, '<')
|
|
9725
|
+
.replace(/>/g, '>');
|
|
9726
|
+
return `<w15:person w15:author="${escapedAuthor}"><w15:presenceInfo w15:providerId="None" w15:userId="${escapedAuthor}"/></w15:person>`;
|
|
9727
|
+
})
|
|
9728
|
+
.join('');
|
|
9821
9729
|
|
|
9822
9730
|
if (existingPeopleXml) {
|
|
9823
9731
|
// Insert new person elements before closing tag
|
|
9824
|
-
const updatedXml = existingPeopleXml.replace(
|
|
9732
|
+
const updatedXml = existingPeopleXml.replace(
|
|
9733
|
+
'</w15:people>',
|
|
9734
|
+
`${personElements}</w15:people>`
|
|
9735
|
+
);
|
|
9825
9736
|
this.zipHandler.updateFile('word/people.xml', updatedXml);
|
|
9826
9737
|
} else {
|
|
9827
9738
|
// Create new people.xml
|
|
@@ -9836,7 +9747,10 @@ export class Document {
|
|
|
9836
9747
|
this.relationshipManager.addPeople();
|
|
9837
9748
|
}
|
|
9838
9749
|
|
|
9839
|
-
this.logger.info('Updated people.xml with missing authors', {
|
|
9750
|
+
this.logger.info('Updated people.xml with missing authors', {
|
|
9751
|
+
added: missingAuthors.length,
|
|
9752
|
+
authors: missingAuthors,
|
|
9753
|
+
});
|
|
9840
9754
|
}
|
|
9841
9755
|
|
|
9842
9756
|
/**
|
|
@@ -9880,11 +9794,9 @@ export class Document {
|
|
|
9880
9794
|
this.properties.customProperties &&
|
|
9881
9795
|
Object.keys(this.properties.customProperties).length > 0
|
|
9882
9796
|
) {
|
|
9883
|
-
const customXml = this.generator.generateCustomProps(
|
|
9884
|
-
this.properties.customProperties
|
|
9885
|
-
);
|
|
9797
|
+
const customXml = this.generator.generateCustomProps(this.properties.customProperties);
|
|
9886
9798
|
if (customXml) {
|
|
9887
|
-
this.zipHandler.addFile(
|
|
9799
|
+
this.zipHandler.addFile('docProps/custom.xml', customXml);
|
|
9888
9800
|
}
|
|
9889
9801
|
}
|
|
9890
9802
|
}
|
|
@@ -9901,13 +9813,17 @@ export class Document {
|
|
|
9901
9813
|
const overrides = new Set<string>();
|
|
9902
9814
|
|
|
9903
9815
|
// Extract all <Default Extension="..." ContentType="..."/> entries
|
|
9904
|
-
const defaultMatches = xml.matchAll(
|
|
9816
|
+
const defaultMatches = xml.matchAll(
|
|
9817
|
+
/<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"\s*\/>/g
|
|
9818
|
+
);
|
|
9905
9819
|
for (const match of defaultMatches) {
|
|
9906
9820
|
defaults.add(`${match[1]}|${match[2]}`); // Store as "ext|mimetype"
|
|
9907
9821
|
}
|
|
9908
9822
|
|
|
9909
9823
|
// Extract all <Override PartName="..." ContentType="..."/> entries
|
|
9910
|
-
const overrideMatches = xml.matchAll(
|
|
9824
|
+
const overrideMatches = xml.matchAll(
|
|
9825
|
+
/<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"\s*\/>/g
|
|
9826
|
+
);
|
|
9911
9827
|
for (const match of overrideMatches) {
|
|
9912
9828
|
overrides.add(`${match[1]}|${match[2]}`); // Store as "path|mimetype"
|
|
9913
9829
|
}
|
|
@@ -9921,21 +9837,19 @@ export class Document {
|
|
|
9921
9837
|
*/
|
|
9922
9838
|
private updateContentTypesWithImagesHeadersFootersAndComments(): void {
|
|
9923
9839
|
const hasCustomProps =
|
|
9924
|
-
this.properties.customProperties &&
|
|
9925
|
-
|
|
9926
|
-
|
|
9927
|
-
|
|
9928
|
-
this.
|
|
9929
|
-
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
|
|
9936
|
-
|
|
9937
|
-
this.endnoteManager
|
|
9938
|
-
);
|
|
9840
|
+
this.properties.customProperties && Object.keys(this.properties.customProperties).length > 0;
|
|
9841
|
+
|
|
9842
|
+
const contentTypes = this.generator.generateContentTypesWithImagesHeadersFootersAndComments(
|
|
9843
|
+
this.imageManager,
|
|
9844
|
+
this.headerFooterManager,
|
|
9845
|
+
this.commentManager,
|
|
9846
|
+
this.zipHandler, // Pass zipHandler to check file existence
|
|
9847
|
+
undefined, // fontManager (optional)
|
|
9848
|
+
hasCustomProps, // Flag to include custom.xml override
|
|
9849
|
+
this._originalContentTypes, // Pass preserved original entries for round-trip fidelity
|
|
9850
|
+
this.footnoteManager,
|
|
9851
|
+
this.endnoteManager
|
|
9852
|
+
);
|
|
9939
9853
|
this.zipHandler.updateFile(DOCX_PATHS.CONTENT_TYPES, contentTypes);
|
|
9940
9854
|
}
|
|
9941
9855
|
|
|
@@ -10016,14 +9930,9 @@ export class Document {
|
|
|
10016
9930
|
* @param bookmarkOrName - Bookmark object or bookmark name
|
|
10017
9931
|
* @returns The bookmark that was added
|
|
10018
9932
|
*/
|
|
10019
|
-
addBookmarkToParagraph(
|
|
10020
|
-
paragraph: Paragraph,
|
|
10021
|
-
bookmarkOrName: Bookmark | string
|
|
10022
|
-
): Bookmark {
|
|
9933
|
+
addBookmarkToParagraph(paragraph: Paragraph, bookmarkOrName: Bookmark | string): Bookmark {
|
|
10023
9934
|
const bookmark =
|
|
10024
|
-
typeof bookmarkOrName ===
|
|
10025
|
-
? this.createBookmark(bookmarkOrName)
|
|
10026
|
-
: bookmarkOrName;
|
|
9935
|
+
typeof bookmarkOrName === 'string' ? this.createBookmark(bookmarkOrName) : bookmarkOrName;
|
|
10027
9936
|
|
|
10028
9937
|
paragraph.addBookmark(bookmark);
|
|
10029
9938
|
return bookmark;
|
|
@@ -10065,7 +9974,7 @@ export class Document {
|
|
|
10065
9974
|
anchor: string;
|
|
10066
9975
|
hyperlink: (text: string, formatting?: RunFormatting) => Hyperlink;
|
|
10067
9976
|
} {
|
|
10068
|
-
const BOOKMARK_NAME =
|
|
9977
|
+
const BOOKMARK_NAME = '_top';
|
|
10069
9978
|
|
|
10070
9979
|
// Check if _top bookmark already exists
|
|
10071
9980
|
let bookmark = this.getBookmark(BOOKMARK_NAME);
|
|
@@ -10223,10 +10132,10 @@ export class Document {
|
|
|
10223
10132
|
* }
|
|
10224
10133
|
* ```
|
|
10225
10134
|
*/
|
|
10226
|
-
validateAndFixRevisions(options?: {
|
|
10227
|
-
validation
|
|
10228
|
-
|
|
10229
|
-
}
|
|
10135
|
+
validateAndFixRevisions(options?: { validation?: ValidationOptions; autoFix?: AutoFixOptions }): {
|
|
10136
|
+
validation: ValidationResult;
|
|
10137
|
+
fix: AutoFixResult;
|
|
10138
|
+
} {
|
|
10230
10139
|
// First validate
|
|
10231
10140
|
const validation = this.validateRevisions(options?.validation);
|
|
10232
10141
|
|
|
@@ -10279,12 +10188,7 @@ export class Document {
|
|
|
10279
10188
|
* @param date - Optional date (defaults to now)
|
|
10280
10189
|
* @returns The created and registered revision
|
|
10281
10190
|
*/
|
|
10282
|
-
createRevisionFromText(
|
|
10283
|
-
type: RevisionType,
|
|
10284
|
-
author: string,
|
|
10285
|
-
text: string,
|
|
10286
|
-
date?: Date
|
|
10287
|
-
): Revision {
|
|
10191
|
+
createRevisionFromText(type: RevisionType, author: string, text: string, date?: Date): Revision {
|
|
10288
10192
|
const revision = Revision.fromText(type, author, text, date);
|
|
10289
10193
|
return this.revisionManager.register(revision);
|
|
10290
10194
|
}
|
|
@@ -10305,7 +10209,10 @@ export class Document {
|
|
|
10305
10209
|
* @param runIndex - Optional run index within the paragraph
|
|
10306
10210
|
* @returns RevisionLocation with paragraph index, or undefined if not found
|
|
10307
10211
|
*/
|
|
10308
|
-
private createRevisionLocation(
|
|
10212
|
+
private createRevisionLocation(
|
|
10213
|
+
paragraph: Paragraph,
|
|
10214
|
+
runIndex?: number
|
|
10215
|
+
): RevisionLocation | undefined {
|
|
10309
10216
|
const paragraphIndex = this.findParagraphIndex(paragraph);
|
|
10310
10217
|
if (paragraphIndex === -1) {
|
|
10311
10218
|
return undefined; // Paragraph not found in document
|
|
@@ -10325,13 +10232,8 @@ export class Document {
|
|
|
10325
10232
|
* @param date - Optional date (defaults to now)
|
|
10326
10233
|
* @returns The created revision with location set
|
|
10327
10234
|
*/
|
|
10328
|
-
trackInsertion(
|
|
10329
|
-
|
|
10330
|
-
author: string,
|
|
10331
|
-
text: string,
|
|
10332
|
-
date?: Date
|
|
10333
|
-
): Revision {
|
|
10334
|
-
const revision = this.createRevisionFromText("insert", author, text, date);
|
|
10235
|
+
trackInsertion(paragraph: Paragraph, author: string, text: string, date?: Date): Revision {
|
|
10236
|
+
const revision = this.createRevisionFromText('insert', author, text, date);
|
|
10335
10237
|
|
|
10336
10238
|
// Set location for changelog/tracking purposes
|
|
10337
10239
|
const location = this.createRevisionLocation(paragraph);
|
|
@@ -10353,13 +10255,8 @@ export class Document {
|
|
|
10353
10255
|
* @param date - Optional date (defaults to now)
|
|
10354
10256
|
* @returns The created revision with location set
|
|
10355
10257
|
*/
|
|
10356
|
-
trackDeletion(
|
|
10357
|
-
|
|
10358
|
-
author: string,
|
|
10359
|
-
text: string,
|
|
10360
|
-
date?: Date
|
|
10361
|
-
): Revision {
|
|
10362
|
-
const revision = this.createRevisionFromText("delete", author, text, date);
|
|
10258
|
+
trackDeletion(paragraph: Paragraph, author: string, text: string, date?: Date): Revision {
|
|
10259
|
+
const revision = this.createRevisionFromText('delete', author, text, date);
|
|
10363
10260
|
|
|
10364
10261
|
// Set location for changelog/tracking purposes
|
|
10365
10262
|
const location = this.createRevisionLocation(paragraph);
|
|
@@ -10424,11 +10321,7 @@ export class Document {
|
|
|
10424
10321
|
* doc.trackParagraphMarkDeletion(para, 'Alice');
|
|
10425
10322
|
* // In Word, this shows the ¶ symbol as deleted
|
|
10426
10323
|
*/
|
|
10427
|
-
trackParagraphMarkDeletion(
|
|
10428
|
-
paragraph: Paragraph,
|
|
10429
|
-
author: string,
|
|
10430
|
-
date?: Date
|
|
10431
|
-
): Paragraph {
|
|
10324
|
+
trackParagraphMarkDeletion(paragraph: Paragraph, author: string, date?: Date): Paragraph {
|
|
10432
10325
|
const revisionId = this.revisionManager.consumeNextId();
|
|
10433
10326
|
paragraph.markParagraphMarkAsDeleted(revisionId, author, date);
|
|
10434
10327
|
return paragraph;
|
|
@@ -10622,15 +10515,13 @@ export class Document {
|
|
|
10622
10515
|
this.trackFormatting = options.trackFormatting;
|
|
10623
10516
|
}
|
|
10624
10517
|
if (options.showInsertionsAndDeletions !== undefined) {
|
|
10625
|
-
this.revisionViewSettings.showInsertionsAndDeletions =
|
|
10626
|
-
options.showInsertionsAndDeletions;
|
|
10518
|
+
this.revisionViewSettings.showInsertionsAndDeletions = options.showInsertionsAndDeletions;
|
|
10627
10519
|
}
|
|
10628
10520
|
if (options.showFormatting !== undefined) {
|
|
10629
10521
|
this.revisionViewSettings.showFormatting = options.showFormatting;
|
|
10630
10522
|
}
|
|
10631
10523
|
if (options.showInkAnnotations !== undefined) {
|
|
10632
|
-
this.revisionViewSettings.showInkAnnotations =
|
|
10633
|
-
options.showInkAnnotations;
|
|
10524
|
+
this.revisionViewSettings.showInkAnnotations = options.showInkAnnotations;
|
|
10634
10525
|
}
|
|
10635
10526
|
}
|
|
10636
10527
|
|
|
@@ -10769,6 +10660,41 @@ export class Document {
|
|
|
10769
10660
|
}
|
|
10770
10661
|
// Bind tracking to section
|
|
10771
10662
|
this.bindTrackingToElement(this.section);
|
|
10663
|
+
|
|
10664
|
+
// Headers
|
|
10665
|
+
for (const entry of this.headerFooterManager.getAllHeaders()) {
|
|
10666
|
+
for (const element of entry.header.getElements()) {
|
|
10667
|
+
this.bindTrackingToElement(element);
|
|
10668
|
+
}
|
|
10669
|
+
}
|
|
10670
|
+
|
|
10671
|
+
// Footers
|
|
10672
|
+
for (const entry of this.headerFooterManager.getAllFooters()) {
|
|
10673
|
+
for (const element of entry.footer.getElements()) {
|
|
10674
|
+
this.bindTrackingToElement(element);
|
|
10675
|
+
}
|
|
10676
|
+
}
|
|
10677
|
+
|
|
10678
|
+
// Footnotes
|
|
10679
|
+
for (const footnote of this.footnoteManager.getAllFootnotes()) {
|
|
10680
|
+
for (const para of footnote.getParagraphs()) {
|
|
10681
|
+
this.bindTrackingToElement(para);
|
|
10682
|
+
}
|
|
10683
|
+
}
|
|
10684
|
+
|
|
10685
|
+
// Endnotes
|
|
10686
|
+
for (const endnote of this.endnoteManager.getAllEndnotes()) {
|
|
10687
|
+
for (const para of endnote.getParagraphs()) {
|
|
10688
|
+
this.bindTrackingToElement(para);
|
|
10689
|
+
}
|
|
10690
|
+
}
|
|
10691
|
+
|
|
10692
|
+
// Comments
|
|
10693
|
+
for (const comment of this.commentManager.getAllCommentsWithReplies()) {
|
|
10694
|
+
for (const run of comment.getRuns()) {
|
|
10695
|
+
this.bindTrackingToElement(run);
|
|
10696
|
+
}
|
|
10697
|
+
}
|
|
10772
10698
|
}
|
|
10773
10699
|
|
|
10774
10700
|
/**
|
|
@@ -10823,7 +10749,7 @@ export class Document {
|
|
|
10823
10749
|
setRsidRoot(rsidRoot: string): this {
|
|
10824
10750
|
// Validate RSID format (8 hex characters)
|
|
10825
10751
|
if (!/^[0-9A-Fa-f]{8}$/.test(rsidRoot)) {
|
|
10826
|
-
throw new Error(
|
|
10752
|
+
throw new Error('RSID must be an 8-character hexadecimal value');
|
|
10827
10753
|
}
|
|
10828
10754
|
this.rsidRoot = rsidRoot.toUpperCase();
|
|
10829
10755
|
this.rsids.add(this.rsidRoot);
|
|
@@ -10839,7 +10765,7 @@ export class Document {
|
|
|
10839
10765
|
addRsid(rsid: string): this {
|
|
10840
10766
|
// Validate RSID format
|
|
10841
10767
|
if (!/^[0-9A-Fa-f]{8}$/.test(rsid)) {
|
|
10842
|
-
throw new Error(
|
|
10768
|
+
throw new Error('RSID must be an 8-character hexadecimal value');
|
|
10843
10769
|
}
|
|
10844
10770
|
this.rsids.add(rsid.toUpperCase());
|
|
10845
10771
|
this._settingsModified = true;
|
|
@@ -10854,7 +10780,7 @@ export class Document {
|
|
|
10854
10780
|
const rsid = Math.floor(Math.random() * 0xffffffff)
|
|
10855
10781
|
.toString(16)
|
|
10856
10782
|
.toUpperCase()
|
|
10857
|
-
.padStart(8,
|
|
10783
|
+
.padStart(8, '0');
|
|
10858
10784
|
this.rsids.add(rsid);
|
|
10859
10785
|
this._settingsModified = true;
|
|
10860
10786
|
return rsid;
|
|
@@ -10881,7 +10807,7 @@ export class Document {
|
|
|
10881
10807
|
* @param protection - Document protection settings
|
|
10882
10808
|
*/
|
|
10883
10809
|
protectDocument(protection: {
|
|
10884
|
-
edit:
|
|
10810
|
+
edit: 'readOnly' | 'comments' | 'trackedChanges' | 'forms';
|
|
10885
10811
|
enforcement?: boolean;
|
|
10886
10812
|
password?: string;
|
|
10887
10813
|
cryptProviderType?: string;
|
|
@@ -10904,17 +10830,11 @@ export class Document {
|
|
|
10904
10830
|
// If password provided, generate hash and salt
|
|
10905
10831
|
if (protection.password) {
|
|
10906
10832
|
// For now, use a simple hash. In production, use proper cryptographic functions
|
|
10907
|
-
const crypto = require(
|
|
10908
|
-
const salt = crypto.randomBytes(16).toString(
|
|
10833
|
+
const crypto = require('crypto');
|
|
10834
|
+
const salt = crypto.randomBytes(16).toString('base64');
|
|
10909
10835
|
const hash = crypto
|
|
10910
|
-
.pbkdf2Sync(
|
|
10911
|
-
|
|
10912
|
-
salt,
|
|
10913
|
-
protection.cryptSpinCount || 100000,
|
|
10914
|
-
32,
|
|
10915
|
-
"sha512"
|
|
10916
|
-
)
|
|
10917
|
-
.toString("base64");
|
|
10836
|
+
.pbkdf2Sync(protection.password, salt, protection.cryptSpinCount || 100000, 32, 'sha512')
|
|
10837
|
+
.toString('base64');
|
|
10918
10838
|
|
|
10919
10839
|
this.documentProtection.hash = hash;
|
|
10920
10840
|
this.documentProtection.salt = salt;
|
|
@@ -10967,7 +10887,9 @@ export class Document {
|
|
|
10967
10887
|
* Gets the document background per ECMA-376 Part 1 §17.2.1
|
|
10968
10888
|
* @returns Background properties or undefined
|
|
10969
10889
|
*/
|
|
10970
|
-
getDocumentBackground():
|
|
10890
|
+
getDocumentBackground():
|
|
10891
|
+
| { color?: string; themeColor?: string; themeTint?: string; themeShade?: string }
|
|
10892
|
+
| undefined {
|
|
10971
10893
|
return this._documentBackground ? { ...this._documentBackground } : undefined;
|
|
10972
10894
|
}
|
|
10973
10895
|
|
|
@@ -10975,7 +10897,11 @@ export class Document {
|
|
|
10975
10897
|
* Sets the document background per ECMA-376 Part 1 §17.2.1
|
|
10976
10898
|
* @param background - Background properties (color, themeColor, etc.) or undefined to remove
|
|
10977
10899
|
*/
|
|
10978
|
-
setDocumentBackground(
|
|
10900
|
+
setDocumentBackground(
|
|
10901
|
+
background:
|
|
10902
|
+
| { color?: string; themeColor?: string; themeTint?: string; themeShade?: string }
|
|
10903
|
+
| undefined
|
|
10904
|
+
): void {
|
|
10979
10905
|
this._documentBackground = background ? { ...background } : undefined;
|
|
10980
10906
|
}
|
|
10981
10907
|
|
|
@@ -11196,12 +11122,14 @@ export class Document {
|
|
|
11196
11122
|
* @returns CompatibilityInfo with all parsed settings
|
|
11197
11123
|
*/
|
|
11198
11124
|
getCompatibilityInfo(): CompatibilityInfo {
|
|
11199
|
-
return
|
|
11200
|
-
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
|
|
11204
|
-
|
|
11125
|
+
return (
|
|
11126
|
+
this._compatInfo ?? {
|
|
11127
|
+
mode: CompatibilityMode.Word2007,
|
|
11128
|
+
isLegacyMode: true,
|
|
11129
|
+
compatSettings: [],
|
|
11130
|
+
legacyFlags: [],
|
|
11131
|
+
}
|
|
11132
|
+
);
|
|
11205
11133
|
}
|
|
11206
11134
|
|
|
11207
11135
|
/**
|
|
@@ -11281,7 +11209,7 @@ export class Document {
|
|
|
11281
11209
|
previousMode: currentMode,
|
|
11282
11210
|
newMode: 15,
|
|
11283
11211
|
removedFlags: [],
|
|
11284
|
-
addedSettings: MODERN_COMPAT_SETTINGS.map(s => s.name),
|
|
11212
|
+
addedSettings: MODERN_COMPAT_SETTINGS.map((s) => s.name),
|
|
11285
11213
|
namespacesExpanded: nsResult.expanded,
|
|
11286
11214
|
changed: currentMode !== CompatibilityMode.Word2013Plus,
|
|
11287
11215
|
};
|
|
@@ -11301,12 +11229,7 @@ export class Document {
|
|
|
11301
11229
|
previousProperties: Record<string, any>,
|
|
11302
11230
|
date?: Date
|
|
11303
11231
|
): Revision {
|
|
11304
|
-
const revision = Revision.createRunPropertiesChange(
|
|
11305
|
-
author,
|
|
11306
|
-
content,
|
|
11307
|
-
previousProperties,
|
|
11308
|
-
date
|
|
11309
|
-
);
|
|
11232
|
+
const revision = Revision.createRunPropertiesChange(author, content, previousProperties, date);
|
|
11310
11233
|
return this.revisionManager.register(revision);
|
|
11311
11234
|
}
|
|
11312
11235
|
|
|
@@ -11364,12 +11287,7 @@ export class Document {
|
|
|
11364
11287
|
* @param date - Optional date (defaults to now)
|
|
11365
11288
|
* @returns The created and registered revision
|
|
11366
11289
|
*/
|
|
11367
|
-
createMoveFrom(
|
|
11368
|
-
author: string,
|
|
11369
|
-
content: Run | Run[],
|
|
11370
|
-
moveId: string,
|
|
11371
|
-
date?: Date
|
|
11372
|
-
): Revision {
|
|
11290
|
+
createMoveFrom(author: string, content: Run | Run[], moveId: string, date?: Date): Revision {
|
|
11373
11291
|
const revision = Revision.createMoveFrom(author, content, moveId, date);
|
|
11374
11292
|
return this.revisionManager.register(revision);
|
|
11375
11293
|
}
|
|
@@ -11382,12 +11300,7 @@ export class Document {
|
|
|
11382
11300
|
* @param date - Optional date (defaults to now)
|
|
11383
11301
|
* @returns The created and registered revision
|
|
11384
11302
|
*/
|
|
11385
|
-
createMoveTo(
|
|
11386
|
-
author: string,
|
|
11387
|
-
content: Run | Run[],
|
|
11388
|
-
moveId: string,
|
|
11389
|
-
date?: Date
|
|
11390
|
-
): Revision {
|
|
11303
|
+
createMoveTo(author: string, content: Run | Run[], moveId: string, date?: Date): Revision {
|
|
11391
11304
|
const revision = Revision.createMoveTo(author, content, moveId, date);
|
|
11392
11305
|
return this.revisionManager.register(revision);
|
|
11393
11306
|
}
|
|
@@ -11413,9 +11326,7 @@ export class Document {
|
|
|
11413
11326
|
moveToRangeEnd: RangeMarker;
|
|
11414
11327
|
} {
|
|
11415
11328
|
// Generate unique move ID and name
|
|
11416
|
-
const moveId = `move${Date.now()}_${Math.random()
|
|
11417
|
-
.toString(36)
|
|
11418
|
-
.substr(2, 9)}`;
|
|
11329
|
+
const moveId = `move${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
11419
11330
|
const moveName = `move${Date.now()}`;
|
|
11420
11331
|
|
|
11421
11332
|
// Get unique IDs for range markers (use revision manager's next ID)
|
|
@@ -11431,12 +11342,7 @@ export class Document {
|
|
|
11431
11342
|
const moveFromRangeEnd = RangeMarker.createMoveFromEnd(rangeIdStart);
|
|
11432
11343
|
|
|
11433
11344
|
// Create range markers for moveTo
|
|
11434
|
-
const moveToRangeStart = RangeMarker.createMoveToStart(
|
|
11435
|
-
rangeIdStart,
|
|
11436
|
-
moveName,
|
|
11437
|
-
author,
|
|
11438
|
-
date
|
|
11439
|
-
);
|
|
11345
|
+
const moveToRangeStart = RangeMarker.createMoveToStart(rangeIdStart, moveName, author, date);
|
|
11440
11346
|
const moveToRangeEnd = RangeMarker.createMoveToEnd(rangeIdStart);
|
|
11441
11347
|
|
|
11442
11348
|
// Create the actual move revisions
|
|
@@ -11461,11 +11367,7 @@ export class Document {
|
|
|
11461
11367
|
* @param date - Optional date (defaults to now)
|
|
11462
11368
|
* @returns The created and registered revision
|
|
11463
11369
|
*/
|
|
11464
|
-
createTableCellInsert(
|
|
11465
|
-
author: string,
|
|
11466
|
-
content: Run | Run[],
|
|
11467
|
-
date?: Date
|
|
11468
|
-
): Revision {
|
|
11370
|
+
createTableCellInsert(author: string, content: Run | Run[], date?: Date): Revision {
|
|
11469
11371
|
const revision = Revision.createTableCellInsert(author, content, date);
|
|
11470
11372
|
return this.revisionManager.register(revision);
|
|
11471
11373
|
}
|
|
@@ -11477,11 +11379,7 @@ export class Document {
|
|
|
11477
11379
|
* @param date - Optional date (defaults to now)
|
|
11478
11380
|
* @returns The created and registered revision
|
|
11479
11381
|
*/
|
|
11480
|
-
createTableCellDelete(
|
|
11481
|
-
author: string,
|
|
11482
|
-
content: Run | Run[],
|
|
11483
|
-
date?: Date
|
|
11484
|
-
): Revision {
|
|
11382
|
+
createTableCellDelete(author: string, content: Run | Run[], date?: Date): Revision {
|
|
11485
11383
|
const revision = Revision.createTableCellDelete(author, content, date);
|
|
11486
11384
|
return this.revisionManager.register(revision);
|
|
11487
11385
|
}
|
|
@@ -11493,11 +11391,7 @@ export class Document {
|
|
|
11493
11391
|
* @param date - Optional date (defaults to now)
|
|
11494
11392
|
* @returns The created and registered revision
|
|
11495
11393
|
*/
|
|
11496
|
-
createTableCellMerge(
|
|
11497
|
-
author: string,
|
|
11498
|
-
content: Run | Run[],
|
|
11499
|
-
date?: Date
|
|
11500
|
-
): Revision {
|
|
11394
|
+
createTableCellMerge(author: string, content: Run | Run[], date?: Date): Revision {
|
|
11501
11395
|
const revision = Revision.createTableCellMerge(author, content, date);
|
|
11502
11396
|
return this.revisionManager.register(revision);
|
|
11503
11397
|
}
|
|
@@ -11516,12 +11410,7 @@ export class Document {
|
|
|
11516
11410
|
previousProperties: Record<string, any>,
|
|
11517
11411
|
date?: Date
|
|
11518
11412
|
): Revision {
|
|
11519
|
-
const revision = Revision.createNumberingChange(
|
|
11520
|
-
author,
|
|
11521
|
-
content,
|
|
11522
|
-
previousProperties,
|
|
11523
|
-
date
|
|
11524
|
-
);
|
|
11413
|
+
const revision = Revision.createNumberingChange(author, content, previousProperties, date);
|
|
11525
11414
|
return this.revisionManager.register(revision);
|
|
11526
11415
|
}
|
|
11527
11416
|
|
|
@@ -11575,11 +11464,7 @@ export class Document {
|
|
|
11575
11464
|
* const comment = doc.createComment('Alice', run, 'A');
|
|
11576
11465
|
* ```
|
|
11577
11466
|
*/
|
|
11578
|
-
createComment(
|
|
11579
|
-
author: string,
|
|
11580
|
-
content: string | Run | Run[],
|
|
11581
|
-
initials?: string
|
|
11582
|
-
): Comment {
|
|
11467
|
+
createComment(author: string, content: string | Run | Run[], initials?: string): Comment {
|
|
11583
11468
|
this._commentsModified = true;
|
|
11584
11469
|
return this.commentManager.createComment(author, content, initials);
|
|
11585
11470
|
}
|
|
@@ -11599,12 +11484,7 @@ export class Document {
|
|
|
11599
11484
|
initials?: string
|
|
11600
11485
|
): Comment {
|
|
11601
11486
|
this._commentsModified = true;
|
|
11602
|
-
return this.commentManager.createReply(
|
|
11603
|
-
parentCommentId,
|
|
11604
|
-
author,
|
|
11605
|
-
content,
|
|
11606
|
-
initials
|
|
11607
|
-
);
|
|
11487
|
+
return this.commentManager.createReply(parentCommentId, author, content, initials);
|
|
11608
11488
|
}
|
|
11609
11489
|
|
|
11610
11490
|
/**
|
|
@@ -11676,7 +11556,7 @@ export class Document {
|
|
|
11676
11556
|
initials?: string
|
|
11677
11557
|
): Comment {
|
|
11678
11558
|
const comment =
|
|
11679
|
-
typeof commentOrAuthor ===
|
|
11559
|
+
typeof commentOrAuthor === 'string'
|
|
11680
11560
|
? this.createComment(commentOrAuthor, content!, initials)
|
|
11681
11561
|
: commentOrAuthor;
|
|
11682
11562
|
|
|
@@ -11719,9 +11599,7 @@ export class Document {
|
|
|
11719
11599
|
* @param commentId - ID of the top-level comment
|
|
11720
11600
|
* @returns Object with the comment and its replies, or undefined if not found
|
|
11721
11601
|
*/
|
|
11722
|
-
getCommentThread(
|
|
11723
|
-
commentId: number
|
|
11724
|
-
): { comment: Comment; replies: Comment[] } | undefined {
|
|
11602
|
+
getCommentThread(commentId: number): { comment: Comment; replies: Comment[] } | undefined {
|
|
11725
11603
|
return this.commentManager.getCommentThread(commentId);
|
|
11726
11604
|
}
|
|
11727
11605
|
|
|
@@ -12000,16 +11878,16 @@ export class Document {
|
|
|
12000
11878
|
|
|
12001
11879
|
/**
|
|
12002
11880
|
* Strips all tracked changes from the document
|
|
12003
|
-
*
|
|
11881
|
+
*
|
|
12004
11882
|
* 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
|
|
11883
|
+
* and cleans up related metadata. This effectively "accepts" all changes without using Word's
|
|
12006
11884
|
* built-in Accept Changes feature.
|
|
12007
|
-
*
|
|
11885
|
+
*
|
|
12008
11886
|
* **IMPORTANT**: This operation:
|
|
12009
11887
|
* 1. Modifies the raw XML in the ZIP package to remove all tracked changes
|
|
12010
11888
|
* 2. Clears Revision objects from the in-memory object model to prevent re-serialization
|
|
12011
11889
|
* 3. Sets flag to prevent XML regeneration on save (preserves the cleaned XML)
|
|
12012
|
-
*
|
|
11890
|
+
*
|
|
12013
11891
|
* What gets removed:
|
|
12014
11892
|
* - All insertion markers (<w:ins>) - content is kept, wrapper removed
|
|
12015
11893
|
* - All deletion markers (<w:del>) - entire element including content removed
|
|
@@ -12019,22 +11897,22 @@ export class Document {
|
|
|
12019
11897
|
* - Revision authors from word/people.xml
|
|
12020
11898
|
* - Track changes settings from word/settings.xml
|
|
12021
11899
|
* - Revision count from docProps/core.xml
|
|
12022
|
-
*
|
|
11900
|
+
*
|
|
12023
11901
|
* @returns This document instance for method chaining
|
|
12024
|
-
*
|
|
11902
|
+
*
|
|
12025
11903
|
* @example
|
|
12026
11904
|
* ```typescript
|
|
12027
11905
|
* // Load document with tracked changes
|
|
12028
11906
|
* const doc = await Document.load('document-with-revisions.docx');
|
|
12029
|
-
*
|
|
11907
|
+
*
|
|
12030
11908
|
* // Strip all tracked changes
|
|
12031
11909
|
* await doc.stripTrackedChanges();
|
|
12032
|
-
*
|
|
11910
|
+
*
|
|
12033
11911
|
* // Now process the document as normal
|
|
12034
11912
|
* doc.applyStyles();
|
|
12035
11913
|
* await doc.save('cleaned.docx');
|
|
12036
11914
|
* ```
|
|
12037
|
-
*
|
|
11915
|
+
*
|
|
12038
11916
|
* @example
|
|
12039
11917
|
* ```typescript
|
|
12040
11918
|
* // Check for tracked changes first
|
|
@@ -12045,7 +11923,7 @@ export class Document {
|
|
|
12045
11923
|
* }
|
|
12046
11924
|
* await doc.save('output.docx');
|
|
12047
11925
|
* ```
|
|
12048
|
-
*
|
|
11926
|
+
*
|
|
12049
11927
|
* @deprecated Use {@link acceptAllRevisions} instead - this method will be removed in a future version
|
|
12050
11928
|
*/
|
|
12051
11929
|
async stripTrackedChanges(): Promise<this> {
|
|
@@ -12055,7 +11933,7 @@ export class Document {
|
|
|
12055
11933
|
|
|
12056
11934
|
/**
|
|
12057
11935
|
* Accepts all tracked changes in the document
|
|
12058
|
-
*
|
|
11936
|
+
*
|
|
12059
11937
|
* Processes all revision markup following Microsoft's official OpenXML SDK approach:
|
|
12060
11938
|
* - Insertions (<w:ins>): Keep the inserted content, remove wrapper tags
|
|
12061
11939
|
* - Deletions (<w:del>): Remove entirely (content was deleted, so discard it)
|
|
@@ -12063,32 +11941,32 @@ export class Document {
|
|
|
12063
11941
|
* - Move To (<w:moveTo>): Keep content, remove wrapper (destination of moved content)
|
|
12064
11942
|
* - Property changes: Remove all tracking elements
|
|
12065
11943
|
* - Range markers: Remove all boundary markers
|
|
12066
|
-
*
|
|
11944
|
+
*
|
|
12067
11945
|
* Also cleans up metadata:
|
|
12068
11946
|
* - Revision authors from word/people.xml
|
|
12069
11947
|
* - Track changes settings from word/settings.xml
|
|
12070
11948
|
* - Revision count from docProps/core.xml
|
|
12071
|
-
*
|
|
11949
|
+
*
|
|
12072
11950
|
* **IMPORTANT**: This operation:
|
|
12073
11951
|
* 1. Modifies the raw XML in the ZIP package
|
|
12074
11952
|
* 2. Clears Revision objects from the in-memory object model
|
|
12075
11953
|
* 3. Sets flag to prevent XML regeneration on save (preserves the cleaned XML)
|
|
12076
|
-
*
|
|
11954
|
+
*
|
|
12077
11955
|
* @returns This document instance for method chaining
|
|
12078
|
-
*
|
|
11956
|
+
*
|
|
12079
11957
|
* @example
|
|
12080
11958
|
* ```typescript
|
|
12081
11959
|
* // Load document with tracked changes
|
|
12082
11960
|
* const doc = await Document.load('document-with-revisions.docx');
|
|
12083
|
-
*
|
|
11961
|
+
*
|
|
12084
11962
|
* // Accept all tracked changes
|
|
12085
11963
|
* await doc.acceptAllRevisions();
|
|
12086
|
-
*
|
|
11964
|
+
*
|
|
12087
11965
|
* // Now process the document as normal
|
|
12088
11966
|
* doc.applyStyles();
|
|
12089
11967
|
* await doc.save('cleaned.docx');
|
|
12090
11968
|
* ```
|
|
12091
|
-
*
|
|
11969
|
+
*
|
|
12092
11970
|
* @example
|
|
12093
11971
|
* ```typescript
|
|
12094
11972
|
* // Check for tracked changes first
|
|
@@ -12099,7 +11977,7 @@ export class Document {
|
|
|
12099
11977
|
* }
|
|
12100
11978
|
* await doc.save('output.docx');
|
|
12101
11979
|
* ```
|
|
12102
|
-
*
|
|
11980
|
+
*
|
|
12103
11981
|
* @see https://learn.microsoft.com/en-us/office/open-xml/how-to-accept-all-revisions
|
|
12104
11982
|
*/
|
|
12105
11983
|
async acceptAllRevisions(): Promise<this> {
|
|
@@ -12172,7 +12050,10 @@ export class Document {
|
|
|
12172
12050
|
* await doc.save('output.docx');
|
|
12173
12051
|
* ```
|
|
12174
12052
|
*/
|
|
12175
|
-
consolidateAllRevisions(timeWindowMs = 1000): {
|
|
12053
|
+
consolidateAllRevisions(timeWindowMs = 1000): {
|
|
12054
|
+
paragraphsProcessed: number;
|
|
12055
|
+
revisionsConsolidated: number;
|
|
12056
|
+
} {
|
|
12176
12057
|
let paragraphsProcessed = 0;
|
|
12177
12058
|
let totalConsolidated = 0;
|
|
12178
12059
|
|
|
@@ -12295,16 +12176,16 @@ export class Document {
|
|
|
12295
12176
|
*/
|
|
12296
12177
|
private clearRevisionsFromAllParagraphs(): void {
|
|
12297
12178
|
let clearedCount = 0;
|
|
12298
|
-
|
|
12179
|
+
|
|
12299
12180
|
// Clear revisions from all paragraphs in the document
|
|
12300
12181
|
for (const para of this.getAllParagraphs()) {
|
|
12301
12182
|
const revisions = para.getRevisions();
|
|
12302
|
-
|
|
12183
|
+
|
|
12303
12184
|
if (revisions.length > 0) {
|
|
12304
12185
|
// Filter out all Revision objects from paragraph content
|
|
12305
12186
|
const content = para.getContent();
|
|
12306
|
-
const nonRevisionContent = content.filter(item => !(item instanceof Revision));
|
|
12307
|
-
|
|
12187
|
+
const nonRevisionContent = content.filter((item) => !(item instanceof Revision));
|
|
12188
|
+
|
|
12308
12189
|
// Replace paragraph content with filtered version
|
|
12309
12190
|
para.clearContent();
|
|
12310
12191
|
for (const item of nonRevisionContent) {
|
|
@@ -12316,11 +12197,11 @@ export class Document {
|
|
|
12316
12197
|
para.addField(item);
|
|
12317
12198
|
}
|
|
12318
12199
|
}
|
|
12319
|
-
|
|
12200
|
+
|
|
12320
12201
|
clearedCount += revisions.length;
|
|
12321
12202
|
}
|
|
12322
12203
|
}
|
|
12323
|
-
|
|
12204
|
+
|
|
12324
12205
|
if (clearedCount > 0) {
|
|
12325
12206
|
this.logger.info(`Cleared ${clearedCount} Revision object(s) from in-memory document model`);
|
|
12326
12207
|
}
|
|
@@ -12563,7 +12444,7 @@ export class Document {
|
|
|
12563
12444
|
// ZipWriter stores all content as Buffer internally, but DocumentPart expects string for text
|
|
12564
12445
|
let content: string | Buffer = file.content;
|
|
12565
12446
|
if (!file.isBinary && Buffer.isBuffer(file.content)) {
|
|
12566
|
-
content = file.content.toString(
|
|
12447
|
+
content = file.content.toString('utf-8');
|
|
12567
12448
|
}
|
|
12568
12449
|
|
|
12569
12450
|
return {
|
|
@@ -12641,7 +12522,7 @@ export class Document {
|
|
|
12641
12522
|
}
|
|
12642
12523
|
}
|
|
12643
12524
|
// Remove relationships targeting this part from _rels/.rels
|
|
12644
|
-
const relsXml = this.zipHandler.getFileAsString(
|
|
12525
|
+
const relsXml = this.zipHandler.getFileAsString('_rels/.rels');
|
|
12645
12526
|
if (relsXml) {
|
|
12646
12527
|
const target = partName.replace(/^\//, '');
|
|
12647
12528
|
const relPattern = new RegExp(
|
|
@@ -12650,7 +12531,7 @@ export class Document {
|
|
|
12650
12531
|
);
|
|
12651
12532
|
const cleaned = relsXml.replace(relPattern, '');
|
|
12652
12533
|
if (cleaned !== relsXml) {
|
|
12653
|
-
this.zipHandler.updateFile(
|
|
12534
|
+
this.zipHandler.updateFile('_rels/.rels', cleaned);
|
|
12654
12535
|
}
|
|
12655
12536
|
}
|
|
12656
12537
|
// Track removed parts to skip regeneration during save
|
|
@@ -12716,17 +12597,14 @@ export class Document {
|
|
|
12716
12597
|
const contentTypes = new Map<string, string>();
|
|
12717
12598
|
|
|
12718
12599
|
try {
|
|
12719
|
-
const contentTypesXml = this.zipHandler.getFileAsString(
|
|
12720
|
-
"[Content_Types].xml"
|
|
12721
|
-
);
|
|
12600
|
+
const contentTypesXml = this.zipHandler.getFileAsString('[Content_Types].xml');
|
|
12722
12601
|
if (!contentTypesXml) {
|
|
12723
12602
|
return contentTypes;
|
|
12724
12603
|
}
|
|
12725
12604
|
|
|
12726
12605
|
// Parse content types XML
|
|
12727
12606
|
// Match Default elements (by extension)
|
|
12728
|
-
const defaultPattern =
|
|
12729
|
-
/<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"/g;
|
|
12607
|
+
const defaultPattern = /<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"/g;
|
|
12730
12608
|
let match;
|
|
12731
12609
|
while ((match = defaultPattern.exec(contentTypesXml)) !== null) {
|
|
12732
12610
|
if (match[1] && match[2]) {
|
|
@@ -12735,8 +12613,7 @@ export class Document {
|
|
|
12735
12613
|
}
|
|
12736
12614
|
|
|
12737
12615
|
// Match Override elements (by part name)
|
|
12738
|
-
const overridePattern =
|
|
12739
|
-
/<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"/g;
|
|
12616
|
+
const overridePattern = /<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"/g;
|
|
12740
12617
|
while ((match = overridePattern.exec(contentTypesXml)) !== null) {
|
|
12741
12618
|
if (match[1] && match[2]) {
|
|
12742
12619
|
contentTypes.set(match[1], match[2]);
|
|
@@ -12783,13 +12660,13 @@ export class Document {
|
|
|
12783
12660
|
}
|
|
12784
12661
|
|
|
12785
12662
|
// If already a string, return as-is
|
|
12786
|
-
if (typeof part.content ===
|
|
12663
|
+
if (typeof part.content === 'string') {
|
|
12787
12664
|
return part.content;
|
|
12788
12665
|
}
|
|
12789
12666
|
|
|
12790
12667
|
// If Buffer, decode as UTF-8 (standard for XML files)
|
|
12791
12668
|
if (Buffer.isBuffer(part.content)) {
|
|
12792
|
-
return part.content.toString(
|
|
12669
|
+
return part.content.toString('utf8');
|
|
12793
12670
|
}
|
|
12794
12671
|
|
|
12795
12672
|
return null;
|
|
@@ -12876,8 +12753,8 @@ export class Document {
|
|
|
12876
12753
|
* ```
|
|
12877
12754
|
*/
|
|
12878
12755
|
async setRawXml(partName: string, xmlContent: string): Promise<void> {
|
|
12879
|
-
if (typeof xmlContent !==
|
|
12880
|
-
throw new Error(
|
|
12756
|
+
if (typeof xmlContent !== 'string') {
|
|
12757
|
+
throw new Error('XML content must be a string');
|
|
12881
12758
|
}
|
|
12882
12759
|
|
|
12883
12760
|
// Use setPart to update the part (handles both string and binary detection)
|
|
@@ -12903,19 +12780,14 @@ export class Document {
|
|
|
12903
12780
|
* await doc.addContentType('.json', 'application/json');
|
|
12904
12781
|
* ```
|
|
12905
12782
|
*/
|
|
12906
|
-
async addContentType(
|
|
12907
|
-
partNameOrExtension: string,
|
|
12908
|
-
contentType: string
|
|
12909
|
-
): Promise<boolean> {
|
|
12783
|
+
async addContentType(partNameOrExtension: string, contentType: string): Promise<boolean> {
|
|
12910
12784
|
try {
|
|
12911
|
-
let contentTypesXml = this.zipHandler.getFileAsString(
|
|
12912
|
-
"[Content_Types].xml"
|
|
12913
|
-
);
|
|
12785
|
+
let contentTypesXml = this.zipHandler.getFileAsString('[Content_Types].xml');
|
|
12914
12786
|
if (!contentTypesXml) {
|
|
12915
12787
|
return false;
|
|
12916
12788
|
}
|
|
12917
12789
|
|
|
12918
|
-
const isExtension = partNameOrExtension.startsWith(
|
|
12790
|
+
const isExtension = partNameOrExtension.startsWith('.');
|
|
12919
12791
|
|
|
12920
12792
|
if (isExtension) {
|
|
12921
12793
|
// Add as Default element (for extensions)
|
|
@@ -12924,7 +12796,7 @@ export class Document {
|
|
|
12924
12796
|
// Check if already exists
|
|
12925
12797
|
const existingPattern = new RegExp(
|
|
12926
12798
|
`<Default\\s+Extension="${extension}"\\s+ContentType="[^"]+"/?>`,
|
|
12927
|
-
|
|
12799
|
+
'g'
|
|
12928
12800
|
);
|
|
12929
12801
|
if (existingPattern.test(contentTypesXml)) {
|
|
12930
12802
|
// Update existing
|
|
@@ -12935,13 +12807,13 @@ export class Document {
|
|
|
12935
12807
|
} else {
|
|
12936
12808
|
// Add new before closing tag
|
|
12937
12809
|
contentTypesXml = contentTypesXml.replace(
|
|
12938
|
-
|
|
12810
|
+
'</Types>',
|
|
12939
12811
|
` <Default Extension="${extension}" ContentType="${contentType}"/>\n</Types>`
|
|
12940
12812
|
);
|
|
12941
12813
|
}
|
|
12942
12814
|
} else {
|
|
12943
12815
|
// Add as Override element (for specific parts)
|
|
12944
|
-
const partName = partNameOrExtension.startsWith(
|
|
12816
|
+
const partName = partNameOrExtension.startsWith('/')
|
|
12945
12817
|
? partNameOrExtension
|
|
12946
12818
|
: `/${partNameOrExtension}`;
|
|
12947
12819
|
|
|
@@ -12949,9 +12821,9 @@ export class Document {
|
|
|
12949
12821
|
const existingPattern = new RegExp(
|
|
12950
12822
|
`<Override\\s+PartName="${partName.replace(
|
|
12951
12823
|
/[.*+?^${}()|[\]\\]/g,
|
|
12952
|
-
|
|
12824
|
+
'\\$&'
|
|
12953
12825
|
)}"\\s+ContentType="[^"]+"/?>`,
|
|
12954
|
-
|
|
12826
|
+
'g'
|
|
12955
12827
|
);
|
|
12956
12828
|
if (existingPattern.test(contentTypesXml)) {
|
|
12957
12829
|
// Update existing
|
|
@@ -12962,14 +12834,14 @@ export class Document {
|
|
|
12962
12834
|
} else {
|
|
12963
12835
|
// Add new before closing tag
|
|
12964
12836
|
contentTypesXml = contentTypesXml.replace(
|
|
12965
|
-
|
|
12837
|
+
'</Types>',
|
|
12966
12838
|
` <Override PartName="${partName}" ContentType="${contentType}"/>\n</Types>`
|
|
12967
12839
|
);
|
|
12968
12840
|
}
|
|
12969
12841
|
}
|
|
12970
12842
|
|
|
12971
12843
|
// Update the content types file
|
|
12972
|
-
this.zipHandler.updateFile(
|
|
12844
|
+
this.zipHandler.updateFile('[Content_Types].xml', contentTypesXml);
|
|
12973
12845
|
return true;
|
|
12974
12846
|
} catch (error: unknown) {
|
|
12975
12847
|
return false;
|
|
@@ -12997,9 +12869,7 @@ export class Document {
|
|
|
12997
12869
|
|
|
12998
12870
|
try {
|
|
12999
12871
|
// Get all .rels files
|
|
13000
|
-
const relsPaths = this.zipHandler
|
|
13001
|
-
.getFilePaths()
|
|
13002
|
-
.filter((path) => path.endsWith(".rels"));
|
|
12872
|
+
const relsPaths = this.zipHandler.getFilePaths().filter((path) => path.endsWith('.rels'));
|
|
13003
12873
|
|
|
13004
12874
|
for (const relsPath of relsPaths) {
|
|
13005
12875
|
const relsContent = this.zipHandler.getFileAsString(relsPath);
|
|
@@ -13014,22 +12884,16 @@ export class Document {
|
|
|
13014
12884
|
const rels: ParsedRelationship[] = [];
|
|
13015
12885
|
|
|
13016
12886
|
// Use XMLParser to extract all Relationship elements
|
|
13017
|
-
const relationshipElements = XMLParser.extractElements(
|
|
13018
|
-
relsContent,
|
|
13019
|
-
"Relationship"
|
|
13020
|
-
);
|
|
12887
|
+
const relationshipElements = XMLParser.extractElements(relsContent, 'Relationship');
|
|
13021
12888
|
|
|
13022
12889
|
for (const relElement of relationshipElements) {
|
|
13023
12890
|
const rel: ParsedRelationship = {};
|
|
13024
12891
|
|
|
13025
12892
|
// 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
|
-
);
|
|
12893
|
+
const id = XMLParser.extractAttribute(relElement, 'Id');
|
|
12894
|
+
const type = XMLParser.extractAttribute(relElement, 'Type');
|
|
12895
|
+
const target = XMLParser.extractAttribute(relElement, 'Target');
|
|
12896
|
+
const targetMode = XMLParser.extractAttribute(relElement, 'TargetMode');
|
|
13033
12897
|
|
|
13034
12898
|
if (id) rel.id = id;
|
|
13035
12899
|
if (type) rel.type = type;
|
|
@@ -13078,19 +12942,15 @@ export class Document {
|
|
|
13078
12942
|
*/
|
|
13079
12943
|
async getRelationships(
|
|
13080
12944
|
partName: string
|
|
13081
|
-
): Promise<
|
|
13082
|
-
{ id?: string; type?: string; target?: string; targetMode?: string }[]
|
|
13083
|
-
> {
|
|
12945
|
+
): Promise<{ id?: string; type?: string; target?: string; targetMode?: string }[]> {
|
|
13084
12946
|
try {
|
|
13085
12947
|
// Construct the .rels path from the part name
|
|
13086
12948
|
// For 'word/document.xml' -> 'word/_rels/document.xml.rels'
|
|
13087
|
-
const lastSlash = partName.lastIndexOf(
|
|
12949
|
+
const lastSlash = partName.lastIndexOf('/');
|
|
13088
12950
|
const relsPath =
|
|
13089
12951
|
lastSlash === -1
|
|
13090
12952
|
? `_rels/${partName}.rels`
|
|
13091
|
-
: `${partName.substring(0, lastSlash)}/_rels/${partName.substring(
|
|
13092
|
-
lastSlash + 1
|
|
13093
|
-
)}.rels`;
|
|
12953
|
+
: `${partName.substring(0, lastSlash)}/_rels/${partName.substring(lastSlash + 1)}.rels`;
|
|
13094
12954
|
|
|
13095
12955
|
const relsContent = this.zipHandler.getFileAsString(relsPath);
|
|
13096
12956
|
if (!relsContent) {
|
|
@@ -13107,19 +12967,16 @@ export class Document {
|
|
|
13107
12967
|
const relationships: ParsedRelationship[] = [];
|
|
13108
12968
|
|
|
13109
12969
|
// Use XMLParser to extract all Relationship elements
|
|
13110
|
-
const relationshipElements = XMLParser.extractElements(
|
|
13111
|
-
relsContent,
|
|
13112
|
-
"Relationship"
|
|
13113
|
-
);
|
|
12970
|
+
const relationshipElements = XMLParser.extractElements(relsContent, 'Relationship');
|
|
13114
12971
|
|
|
13115
12972
|
for (const relElement of relationshipElements) {
|
|
13116
12973
|
const rel: ParsedRelationship = {};
|
|
13117
12974
|
|
|
13118
12975
|
// 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,
|
|
12976
|
+
const id = XMLParser.extractAttribute(relElement, 'Id');
|
|
12977
|
+
const type = XMLParser.extractAttribute(relElement, 'Type');
|
|
12978
|
+
const target = XMLParser.extractAttribute(relElement, 'Target');
|
|
12979
|
+
const targetMode = XMLParser.extractAttribute(relElement, 'TargetMode');
|
|
13123
12980
|
|
|
13124
12981
|
if (id) rel.id = id;
|
|
13125
12982
|
if (type) rel.type = type;
|
|
@@ -13142,9 +12999,7 @@ export class Document {
|
|
|
13142
12999
|
*/
|
|
13143
13000
|
private getContentTypeForPart(partName: string): string | undefined {
|
|
13144
13001
|
try {
|
|
13145
|
-
const contentTypesXml = this.zipHandler.getFileAsString(
|
|
13146
|
-
"[Content_Types].xml"
|
|
13147
|
-
);
|
|
13002
|
+
const contentTypesXml = this.zipHandler.getFileAsString('[Content_Types].xml');
|
|
13148
13003
|
if (!contentTypesXml) {
|
|
13149
13004
|
return undefined;
|
|
13150
13005
|
}
|
|
@@ -13153,9 +13008,9 @@ export class Document {
|
|
|
13153
13008
|
const overridePattern = new RegExp(
|
|
13154
13009
|
`<Override\\s+PartName="${partName.replace(
|
|
13155
13010
|
/[.*+?^${}()|[\]\\]/g,
|
|
13156
|
-
|
|
13011
|
+
'\\$&'
|
|
13157
13012
|
)}"\\s+ContentType="([^"]+)"`,
|
|
13158
|
-
|
|
13013
|
+
'i'
|
|
13159
13014
|
);
|
|
13160
13015
|
const overrideMatch = contentTypesXml.match(overridePattern);
|
|
13161
13016
|
if (overrideMatch) {
|
|
@@ -13163,13 +13018,11 @@ export class Document {
|
|
|
13163
13018
|
}
|
|
13164
13019
|
|
|
13165
13020
|
// Check for extension default
|
|
13166
|
-
const ext = partName.substring(partName.lastIndexOf(
|
|
13021
|
+
const ext = partName.substring(partName.lastIndexOf('.'));
|
|
13167
13022
|
if (ext) {
|
|
13168
13023
|
const defaultPattern = new RegExp(
|
|
13169
|
-
`<Default\\s+Extension="${ext.substring(
|
|
13170
|
-
|
|
13171
|
-
)}"\\s+ContentType="([^"]+)"`,
|
|
13172
|
-
"i"
|
|
13024
|
+
`<Default\\s+Extension="${ext.substring(1)}"\\s+ContentType="([^"]+)"`,
|
|
13025
|
+
'i'
|
|
13173
13026
|
);
|
|
13174
13027
|
const defaultMatch = contentTypesXml.match(defaultPattern);
|
|
13175
13028
|
if (defaultMatch) {
|
|
@@ -13261,8 +13114,8 @@ export class Document {
|
|
|
13261
13114
|
if (wholeWord) {
|
|
13262
13115
|
// Create word boundary regex
|
|
13263
13116
|
const wordPattern = new RegExp(
|
|
13264
|
-
`\\b${searchText.replace(/[.*+?^${}()|[\]\\]/g,
|
|
13265
|
-
caseSensitive ?
|
|
13117
|
+
`\\b${searchText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`,
|
|
13118
|
+
caseSensitive ? 'g' : 'gi'
|
|
13266
13119
|
);
|
|
13267
13120
|
let match;
|
|
13268
13121
|
while ((match = wordPattern.exec(runText)) !== null) {
|
|
@@ -13278,9 +13131,7 @@ export class Document {
|
|
|
13278
13131
|
} else {
|
|
13279
13132
|
// Simple substring search
|
|
13280
13133
|
let startIndex = 0;
|
|
13281
|
-
while (
|
|
13282
|
-
(startIndex = compareText.indexOf(searchText, startIndex)) !== -1
|
|
13283
|
-
) {
|
|
13134
|
+
while ((startIndex = compareText.indexOf(searchText, startIndex)) !== -1) {
|
|
13284
13135
|
results.push({
|
|
13285
13136
|
paragraph,
|
|
13286
13137
|
paragraphIndex: pIndex,
|
|
@@ -13317,18 +13168,13 @@ export class Document {
|
|
|
13317
13168
|
const run = runs[rIndex];
|
|
13318
13169
|
if (!run) continue;
|
|
13319
13170
|
const runText = run.getText();
|
|
13320
|
-
const compareText = caseSensitive
|
|
13321
|
-
? runText
|
|
13322
|
-
: runText.toLowerCase();
|
|
13171
|
+
const compareText = caseSensitive ? runText : runText.toLowerCase();
|
|
13323
13172
|
|
|
13324
13173
|
if (wholeWord) {
|
|
13325
13174
|
// Create word boundary regex
|
|
13326
13175
|
const wordPattern = new RegExp(
|
|
13327
|
-
`\\b${searchText.replace(
|
|
13328
|
-
|
|
13329
|
-
"\\$&"
|
|
13330
|
-
)}\\b`,
|
|
13331
|
-
caseSensitive ? "g" : "gi"
|
|
13176
|
+
`\\b${searchText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`,
|
|
13177
|
+
caseSensitive ? 'g' : 'gi'
|
|
13332
13178
|
);
|
|
13333
13179
|
let match;
|
|
13334
13180
|
while ((match = wordPattern.exec(runText)) !== null) {
|
|
@@ -13344,12 +13190,7 @@ export class Document {
|
|
|
13344
13190
|
} else {
|
|
13345
13191
|
// Simple substring search
|
|
13346
13192
|
let startIndex = 0;
|
|
13347
|
-
while (
|
|
13348
|
-
(startIndex = compareText.indexOf(
|
|
13349
|
-
searchText,
|
|
13350
|
-
startIndex
|
|
13351
|
-
)) !== -1
|
|
13352
|
-
) {
|
|
13193
|
+
while ((startIndex = compareText.indexOf(searchText, startIndex)) !== -1) {
|
|
13353
13194
|
results.push({
|
|
13354
13195
|
paragraph,
|
|
13355
13196
|
paragraphIndex: -1, // Not in main body, in table
|
|
@@ -13392,12 +13233,9 @@ export class Document {
|
|
|
13392
13233
|
* }
|
|
13393
13234
|
* ```
|
|
13394
13235
|
*/
|
|
13395
|
-
findParagraphsByText(
|
|
13396
|
-
pattern: string | RegExp
|
|
13397
|
-
): { paragraph: Paragraph; matches: string[] }[] {
|
|
13236
|
+
findParagraphsByText(pattern: string | RegExp): { paragraph: Paragraph; matches: string[] }[] {
|
|
13398
13237
|
const results: { paragraph: Paragraph; matches: string[] }[] = [];
|
|
13399
|
-
const regex =
|
|
13400
|
-
typeof pattern === "string" ? new RegExp(pattern, "gi") : pattern;
|
|
13238
|
+
const regex = typeof pattern === 'string' ? new RegExp(pattern, 'gi') : pattern;
|
|
13401
13239
|
|
|
13402
13240
|
for (const paragraph of this.getAllParagraphs()) {
|
|
13403
13241
|
const text = paragraph.getText();
|
|
@@ -13457,13 +13295,13 @@ export class Document {
|
|
|
13457
13295
|
getRunsByColor(color: string): Run[] {
|
|
13458
13296
|
const results: Run[] = [];
|
|
13459
13297
|
// Normalize color - remove # and convert to uppercase
|
|
13460
|
-
const normalizedColor = color.replace(/^#/,
|
|
13298
|
+
const normalizedColor = color.replace(/^#/, '').toUpperCase();
|
|
13461
13299
|
|
|
13462
13300
|
for (const paragraph of this.getAllParagraphs()) {
|
|
13463
13301
|
for (const run of paragraph.getRuns()) {
|
|
13464
13302
|
const formatting = run.getFormatting();
|
|
13465
13303
|
if (formatting.color) {
|
|
13466
|
-
const runColor = formatting.color.replace(/^#/,
|
|
13304
|
+
const runColor = formatting.color.replace(/^#/, '').toUpperCase();
|
|
13467
13305
|
if (runColor === normalizedColor) {
|
|
13468
13306
|
results.push(run);
|
|
13469
13307
|
}
|
|
@@ -13636,7 +13474,7 @@ export class Document {
|
|
|
13636
13474
|
sizes.set(formatting.size, (sizes.get(formatting.size) || 0) + 1);
|
|
13637
13475
|
}
|
|
13638
13476
|
if (formatting.color) {
|
|
13639
|
-
const normalizedColor = formatting.color.toUpperCase().replace(/^#/,
|
|
13477
|
+
const normalizedColor = formatting.color.toUpperCase().replace(/^#/, '');
|
|
13640
13478
|
colors.set(normalizedColor, (colors.get(normalizedColor) || 0) + 1);
|
|
13641
13479
|
}
|
|
13642
13480
|
}
|
|
@@ -13736,8 +13574,8 @@ export class Document {
|
|
|
13736
13574
|
if (wholeWord) {
|
|
13737
13575
|
// Use word boundary regex for whole word replacement
|
|
13738
13576
|
const wordPattern = new RegExp(
|
|
13739
|
-
`\\b${find.replace(/[.*+?^${}()|[\]\\]/g,
|
|
13740
|
-
caseSensitive ?
|
|
13577
|
+
`\\b${find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`,
|
|
13578
|
+
caseSensitive ? 'g' : 'gi'
|
|
13741
13579
|
);
|
|
13742
13580
|
const matches = originalText.match(wordPattern);
|
|
13743
13581
|
if (matches) {
|
|
@@ -13747,8 +13585,8 @@ export class Document {
|
|
|
13747
13585
|
} else {
|
|
13748
13586
|
// Simple substring replacement
|
|
13749
13587
|
const searchPattern = new RegExp(
|
|
13750
|
-
find.replace(/[.*+?^${}()|[\]\\]/g,
|
|
13751
|
-
caseSensitive ?
|
|
13588
|
+
find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'),
|
|
13589
|
+
caseSensitive ? 'g' : 'gi'
|
|
13752
13590
|
);
|
|
13753
13591
|
const matches = originalText.match(searchPattern);
|
|
13754
13592
|
if (matches) {
|
|
@@ -13818,7 +13656,7 @@ export class Document {
|
|
|
13818
13656
|
caseSensitive = false,
|
|
13819
13657
|
wholeWord = false,
|
|
13820
13658
|
trackChanges = false,
|
|
13821
|
-
author =
|
|
13659
|
+
author = 'Unknown',
|
|
13822
13660
|
} = options || {};
|
|
13823
13661
|
|
|
13824
13662
|
let count = 0;
|
|
@@ -13826,17 +13664,15 @@ export class Document {
|
|
|
13826
13664
|
|
|
13827
13665
|
// Convert pattern to RegExp if it's a string
|
|
13828
13666
|
let regex: RegExp;
|
|
13829
|
-
if (typeof pattern ===
|
|
13667
|
+
if (typeof pattern === 'string') {
|
|
13830
13668
|
// Escape special regex characters
|
|
13831
|
-
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g,
|
|
13669
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
13832
13670
|
const boundaryPattern = wholeWord ? `\\b${escaped}\\b` : escaped;
|
|
13833
|
-
const flags = caseSensitive ?
|
|
13671
|
+
const flags = caseSensitive ? 'g' : 'gi';
|
|
13834
13672
|
regex = new RegExp(boundaryPattern, flags);
|
|
13835
13673
|
} else {
|
|
13836
13674
|
// Use provided RegExp, ensure global flag
|
|
13837
|
-
const flags = pattern.flags.includes(
|
|
13838
|
-
? pattern.flags
|
|
13839
|
-
: pattern.flags + "g";
|
|
13675
|
+
const flags = pattern.flags.includes('g') ? pattern.flags : pattern.flags + 'g';
|
|
13840
13676
|
regex = new RegExp(pattern.source, flags);
|
|
13841
13677
|
}
|
|
13842
13678
|
|
|
@@ -13864,15 +13700,23 @@ export class Document {
|
|
|
13864
13700
|
|
|
13865
13701
|
if (useGranular) {
|
|
13866
13702
|
for (const seg of segments) {
|
|
13867
|
-
if (seg.type ===
|
|
13703
|
+
if (seg.type === 'equal') {
|
|
13868
13704
|
newContent.push(new Run(seg.text, formatting));
|
|
13869
|
-
} else if (seg.type ===
|
|
13870
|
-
const delRev = Revision.createDeletion(
|
|
13705
|
+
} else if (seg.type === 'delete') {
|
|
13706
|
+
const delRev = Revision.createDeletion(
|
|
13707
|
+
author,
|
|
13708
|
+
new Run(seg.text, formatting),
|
|
13709
|
+
now
|
|
13710
|
+
);
|
|
13871
13711
|
this.revisionManager.register(delRev);
|
|
13872
13712
|
revisions.push(delRev);
|
|
13873
13713
|
newContent.push(delRev);
|
|
13874
|
-
} else if (seg.type ===
|
|
13875
|
-
const insRev = Revision.createInsertion(
|
|
13714
|
+
} else if (seg.type === 'insert') {
|
|
13715
|
+
const insRev = Revision.createInsertion(
|
|
13716
|
+
author,
|
|
13717
|
+
new Run(seg.text, formatting),
|
|
13718
|
+
now
|
|
13719
|
+
);
|
|
13876
13720
|
this.revisionManager.register(insRev);
|
|
13877
13721
|
revisions.push(insRev);
|
|
13878
13722
|
newContent.push(insRev);
|
|
@@ -13880,7 +13724,11 @@ export class Document {
|
|
|
13880
13724
|
}
|
|
13881
13725
|
} else {
|
|
13882
13726
|
// Whole-run delete + insert
|
|
13883
|
-
const deletion = Revision.createDeletion(
|
|
13727
|
+
const deletion = Revision.createDeletion(
|
|
13728
|
+
author,
|
|
13729
|
+
new Run(originalText, formatting),
|
|
13730
|
+
now
|
|
13731
|
+
);
|
|
13884
13732
|
this.revisionManager.register(deletion);
|
|
13885
13733
|
revisions.push(deletion);
|
|
13886
13734
|
newContent.push(deletion);
|
|
@@ -14006,7 +13854,7 @@ export class Document {
|
|
|
14006
13854
|
if (includeSpaces) {
|
|
14007
13855
|
totalChars += text.length;
|
|
14008
13856
|
} else {
|
|
14009
|
-
totalChars += text.replace(/\s/g,
|
|
13857
|
+
totalChars += text.replace(/\s/g, '').length;
|
|
14010
13858
|
}
|
|
14011
13859
|
}
|
|
14012
13860
|
|
|
@@ -14029,7 +13877,7 @@ export class Document {
|
|
|
14029
13877
|
if (includeSpaces) {
|
|
14030
13878
|
totalChars += text.length;
|
|
14031
13879
|
} else {
|
|
14032
|
-
totalChars += text.replace(/\s/g,
|
|
13880
|
+
totalChars += text.replace(/\s/g, '').length;
|
|
14033
13881
|
}
|
|
14034
13882
|
}
|
|
14035
13883
|
}
|
|
@@ -14047,7 +13895,7 @@ export class Document {
|
|
|
14047
13895
|
removeParagraph(paragraphOrIndex: Paragraph | number): boolean {
|
|
14048
13896
|
let index: number;
|
|
14049
13897
|
|
|
14050
|
-
if (typeof paragraphOrIndex ===
|
|
13898
|
+
if (typeof paragraphOrIndex === 'number') {
|
|
14051
13899
|
index = paragraphOrIndex;
|
|
14052
13900
|
} else {
|
|
14053
13901
|
// Find the index of the paragraph
|
|
@@ -14084,7 +13932,7 @@ export class Document {
|
|
|
14084
13932
|
removeTable(tableOrIndex: Table | number): boolean {
|
|
14085
13933
|
let index: number;
|
|
14086
13934
|
|
|
14087
|
-
if (typeof tableOrIndex ===
|
|
13935
|
+
if (typeof tableOrIndex === 'number') {
|
|
14088
13936
|
// If number provided, find the nth table
|
|
14089
13937
|
const tables = this.getTables();
|
|
14090
13938
|
if (tableOrIndex >= 0 && tableOrIndex < tables.length) {
|
|
@@ -14118,9 +13966,7 @@ export class Document {
|
|
|
14118
13966
|
private validateParagraph(paragraph: Paragraph): void {
|
|
14119
13967
|
// Type validation
|
|
14120
13968
|
if (!(paragraph instanceof Paragraph)) {
|
|
14121
|
-
throw new Error(
|
|
14122
|
-
"insertParagraphAt: parameter must be a Paragraph instance"
|
|
14123
|
-
);
|
|
13969
|
+
throw new Error('insertParagraphAt: parameter must be a Paragraph instance');
|
|
14124
13970
|
}
|
|
14125
13971
|
|
|
14126
13972
|
// Check for duplicate paragraph IDs
|
|
@@ -14163,19 +14009,19 @@ export class Document {
|
|
|
14163
14009
|
private validateTable(table: Table): void {
|
|
14164
14010
|
// Type validation
|
|
14165
14011
|
if (!(table instanceof Table)) {
|
|
14166
|
-
throw new Error(
|
|
14012
|
+
throw new Error('insertTableAt: parameter must be a Table instance');
|
|
14167
14013
|
}
|
|
14168
14014
|
|
|
14169
14015
|
// Content validation - table must have rows
|
|
14170
14016
|
const rows = table.getRows();
|
|
14171
14017
|
if (rows.length === 0) {
|
|
14172
|
-
throw new Error(
|
|
14018
|
+
throw new Error('insertTableAt: table must have at least one row');
|
|
14173
14019
|
}
|
|
14174
14020
|
|
|
14175
14021
|
// Check first row has cells (rows.length > 0 already checked above)
|
|
14176
14022
|
const firstRow = rows[0];
|
|
14177
14023
|
if (firstRow?.getCells().length === 0) {
|
|
14178
|
-
throw new Error(
|
|
14024
|
+
throw new Error('insertTableAt: table rows must have at least one cell');
|
|
14179
14025
|
}
|
|
14180
14026
|
|
|
14181
14027
|
// Warn about missing table styles
|
|
@@ -14195,27 +14041,25 @@ export class Document {
|
|
|
14195
14041
|
private validateToc(toc: TableOfContentsElement): void {
|
|
14196
14042
|
// Type validation
|
|
14197
14043
|
if (!(toc instanceof TableOfContentsElement)) {
|
|
14198
|
-
throw new Error(
|
|
14199
|
-
"insertTocAt: parameter must be a TableOfContentsElement instance"
|
|
14200
|
-
);
|
|
14044
|
+
throw new Error('insertTocAt: parameter must be a TableOfContentsElement instance');
|
|
14201
14045
|
}
|
|
14202
14046
|
|
|
14203
14047
|
// Check if document has heading styles for TOC to reference
|
|
14204
14048
|
const hasHeadings = [
|
|
14205
|
-
|
|
14206
|
-
|
|
14207
|
-
|
|
14208
|
-
|
|
14209
|
-
|
|
14210
|
-
|
|
14211
|
-
|
|
14212
|
-
|
|
14213
|
-
|
|
14049
|
+
'Heading1',
|
|
14050
|
+
'Heading2',
|
|
14051
|
+
'Heading3',
|
|
14052
|
+
'Heading4',
|
|
14053
|
+
'Heading5',
|
|
14054
|
+
'Heading6',
|
|
14055
|
+
'Heading7',
|
|
14056
|
+
'Heading8',
|
|
14057
|
+
'Heading9',
|
|
14214
14058
|
].some((style) => this.stylesManager.hasStyle(style));
|
|
14215
14059
|
|
|
14216
14060
|
if (!hasHeadings) {
|
|
14217
14061
|
defaultLogger.warn(
|
|
14218
|
-
|
|
14062
|
+
'No heading styles found in document. Table of Contents may not display entries correctly.'
|
|
14219
14063
|
);
|
|
14220
14064
|
}
|
|
14221
14065
|
}
|
|
@@ -14544,8 +14388,7 @@ export class Document {
|
|
|
14544
14388
|
* ```
|
|
14545
14389
|
*/
|
|
14546
14390
|
getHyperlinks(): { hyperlink: Hyperlink; paragraph: Paragraph }[] {
|
|
14547
|
-
const hyperlinks: { hyperlink: Hyperlink; paragraph: Paragraph }[] =
|
|
14548
|
-
[];
|
|
14391
|
+
const hyperlinks: { hyperlink: Hyperlink; paragraph: Paragraph }[] = [];
|
|
14549
14392
|
|
|
14550
14393
|
// Helper function to extract hyperlinks from paragraph content,
|
|
14551
14394
|
// including those inside Revision elements (w:ins, w:del, etc.)
|
|
@@ -14573,8 +14416,7 @@ export class Document {
|
|
|
14573
14416
|
for (const row of table.getRows()) {
|
|
14574
14417
|
for (const cell of row.getCells()) {
|
|
14575
14418
|
// TableCell has getParagraphs method
|
|
14576
|
-
const cellParagraphs =
|
|
14577
|
-
cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
14419
|
+
const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
14578
14420
|
for (const para of cellParagraphs) {
|
|
14579
14421
|
extractHyperlinksFromParagraph(para);
|
|
14580
14422
|
}
|
|
@@ -14611,7 +14453,9 @@ export class Document {
|
|
|
14611
14453
|
}
|
|
14612
14454
|
};
|
|
14613
14455
|
|
|
14614
|
-
const scanElement = (
|
|
14456
|
+
const scanElement = (
|
|
14457
|
+
element: BodyElement | Paragraph | Table | StructuredDocumentTag
|
|
14458
|
+
): void => {
|
|
14615
14459
|
if (element instanceof Paragraph) {
|
|
14616
14460
|
scanParagraph(element);
|
|
14617
14461
|
} else if (element instanceof Table) {
|
|
@@ -14697,8 +14541,7 @@ export class Document {
|
|
|
14697
14541
|
resetFormatting?: boolean;
|
|
14698
14542
|
cleanupRelationships?: boolean;
|
|
14699
14543
|
}): number {
|
|
14700
|
-
const { resetFormatting = false, cleanupRelationships = false } =
|
|
14701
|
-
options || {};
|
|
14544
|
+
const { resetFormatting = false, cleanupRelationships = false } = options || {};
|
|
14702
14545
|
|
|
14703
14546
|
// Guard: Skip when track changes is enabled - prevents field structure corruption
|
|
14704
14547
|
// The mergeConsecutiveHyperlinks() method uses clearContent() + addHyperlink()
|
|
@@ -14707,7 +14550,7 @@ export class Document {
|
|
|
14707
14550
|
if (this.trackChangesEnabled) {
|
|
14708
14551
|
defaultLogger.warn(
|
|
14709
14552
|
'defragmentHyperlinks skipped: track changes is enabled. ' +
|
|
14710
|
-
|
|
14553
|
+
'Call defragmentHyperlinks before enableTrackChanges() to avoid field corruption.'
|
|
14711
14554
|
);
|
|
14712
14555
|
return 0;
|
|
14713
14556
|
}
|
|
@@ -14736,8 +14579,7 @@ export class Document {
|
|
|
14736
14579
|
for (const table of this.getTables()) {
|
|
14737
14580
|
for (const row of table.getRows()) {
|
|
14738
14581
|
for (const cell of row.getCells()) {
|
|
14739
|
-
const cellParagraphs =
|
|
14740
|
-
cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
14582
|
+
const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
14741
14583
|
for (const para of cellParagraphs) {
|
|
14742
14584
|
const originalContent = para.getContent();
|
|
14743
14585
|
|
|
@@ -14759,12 +14601,9 @@ export class Document {
|
|
|
14759
14601
|
const referencedIds = this.collectAllReferencedHyperlinkIds();
|
|
14760
14602
|
|
|
14761
14603
|
// Remove orphaned hyperlink relationships
|
|
14762
|
-
const removedCount =
|
|
14763
|
-
this.relationshipManager.removeOrphanedHyperlinks(referencedIds);
|
|
14604
|
+
const removedCount = this.relationshipManager.removeOrphanedHyperlinks(referencedIds);
|
|
14764
14605
|
if (removedCount > 0) {
|
|
14765
|
-
defaultLogger.info(
|
|
14766
|
-
`Cleaned up ${removedCount} orphaned hyperlink relationship(s)`
|
|
14767
|
-
);
|
|
14606
|
+
defaultLogger.info(`Cleaned up ${removedCount} orphaned hyperlink relationship(s)`);
|
|
14768
14607
|
}
|
|
14769
14608
|
}
|
|
14770
14609
|
|
|
@@ -14848,8 +14687,7 @@ export class Document {
|
|
|
14848
14687
|
for (const table of this.getTables()) {
|
|
14849
14688
|
for (const row of table.getRows()) {
|
|
14850
14689
|
for (const cell of row.getCells()) {
|
|
14851
|
-
const cellParagraphs =
|
|
14852
|
-
cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
14690
|
+
const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
14853
14691
|
for (const para of cellParagraphs) {
|
|
14854
14692
|
for (const field of para.getFields()) {
|
|
14855
14693
|
results.push({ field, paragraph: para, table });
|
|
@@ -14910,7 +14748,10 @@ export class Document {
|
|
|
14910
14748
|
await this.imageManager.loadAllImageData();
|
|
14911
14749
|
|
|
14912
14750
|
// 2. Group images by filename (avoid processing same file twice)
|
|
14913
|
-
const imagesByFilename = new Map<
|
|
14751
|
+
const imagesByFilename = new Map<
|
|
14752
|
+
string,
|
|
14753
|
+
{ image: Image; relationshipId: string; filename: string }[]
|
|
14754
|
+
>();
|
|
14914
14755
|
for (const entry of this.imageManager.getAllImages()) {
|
|
14915
14756
|
const group = imagesByFilename.get(entry.filename) || [];
|
|
14916
14757
|
group.push(entry);
|
|
@@ -14968,7 +14809,11 @@ export class Document {
|
|
|
14968
14809
|
* Handles both document body and header/footer relationships.
|
|
14969
14810
|
* @private
|
|
14970
14811
|
*/
|
|
14971
|
-
private updateImageRelationshipTarget(
|
|
14812
|
+
private updateImageRelationshipTarget(
|
|
14813
|
+
relId: string,
|
|
14814
|
+
oldFilename: string,
|
|
14815
|
+
newFilename: string
|
|
14816
|
+
): void {
|
|
14972
14817
|
// Try document body relationship manager first
|
|
14973
14818
|
const rel = this.relationshipManager.getRelationship(relId);
|
|
14974
14819
|
if (rel) {
|
|
@@ -15029,8 +14874,7 @@ export class Document {
|
|
|
15029
14874
|
for (const table of this.getTables()) {
|
|
15030
14875
|
for (const row of table.getRows()) {
|
|
15031
14876
|
for (const cell of row.getCells()) {
|
|
15032
|
-
const cellParagraphs =
|
|
15033
|
-
cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
14877
|
+
const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
15034
14878
|
for (const para of cellParagraphs) {
|
|
15035
14879
|
runs.push(...para.getRuns());
|
|
15036
14880
|
}
|
|
@@ -15070,9 +14914,10 @@ export class Document {
|
|
|
15070
14914
|
* });
|
|
15071
14915
|
* ```
|
|
15072
14916
|
*/
|
|
15073
|
-
hyperlinkEmails(options?: {
|
|
15074
|
-
|
|
15075
|
-
|
|
14917
|
+
hyperlinkEmails(options?: { formatting?: RunFormatting }): {
|
|
14918
|
+
emailsLinked: number;
|
|
14919
|
+
paragraphsModified: number;
|
|
14920
|
+
} {
|
|
15076
14921
|
// Default formatting: Verdana 12pt, Underline, no bold, #0000FF
|
|
15077
14922
|
const defaultFormatting: RunFormatting = {
|
|
15078
14923
|
font: 'Verdana',
|
|
@@ -15189,23 +15034,23 @@ export class Document {
|
|
|
15189
15034
|
*/
|
|
15190
15035
|
removeFormattingFromAll(
|
|
15191
15036
|
type:
|
|
15192
|
-
|
|
|
15193
|
-
|
|
|
15194
|
-
|
|
|
15195
|
-
|
|
|
15196
|
-
|
|
|
15197
|
-
|
|
|
15198
|
-
|
|
|
15199
|
-
|
|
|
15200
|
-
|
|
|
15201
|
-
|
|
|
15202
|
-
|
|
|
15203
|
-
|
|
|
15204
|
-
|
|
|
15205
|
-
|
|
|
15206
|
-
|
|
|
15207
|
-
|
|
|
15208
|
-
|
|
|
15037
|
+
| 'bold'
|
|
15038
|
+
| 'italic'
|
|
15039
|
+
| 'underline'
|
|
15040
|
+
| 'strike'
|
|
15041
|
+
| 'dstrike'
|
|
15042
|
+
| 'highlight'
|
|
15043
|
+
| 'color'
|
|
15044
|
+
| 'font'
|
|
15045
|
+
| 'size'
|
|
15046
|
+
| 'subscript'
|
|
15047
|
+
| 'superscript'
|
|
15048
|
+
| 'smallCaps'
|
|
15049
|
+
| 'allCaps'
|
|
15050
|
+
| 'outline'
|
|
15051
|
+
| 'shadow'
|
|
15052
|
+
| 'emboss'
|
|
15053
|
+
| 'imprint'
|
|
15209
15054
|
): number {
|
|
15210
15055
|
let modifiedCount = 0;
|
|
15211
15056
|
|
|
@@ -15274,9 +15119,7 @@ export class Document {
|
|
|
15274
15119
|
* });
|
|
15275
15120
|
* ```
|
|
15276
15121
|
*/
|
|
15277
|
-
updateAllHyperlinks(
|
|
15278
|
-
formatter: (hyperlink: Hyperlink, paragraph: Paragraph) => void
|
|
15279
|
-
): number {
|
|
15122
|
+
updateAllHyperlinks(formatter: (hyperlink: Hyperlink, paragraph: Paragraph) => void): number {
|
|
15280
15123
|
// Get all hyperlinks with their containing paragraphs
|
|
15281
15124
|
const hyperlinks = this.getHyperlinks();
|
|
15282
15125
|
|
|
@@ -15349,7 +15192,7 @@ export class Document {
|
|
|
15349
15192
|
|
|
15350
15193
|
this.bodyElements.forEach((element, index) => {
|
|
15351
15194
|
if (element instanceof Paragraph) {
|
|
15352
|
-
const isEmpty = element.getText().trim() ===
|
|
15195
|
+
const isEmpty = element.getText().trim() === '';
|
|
15353
15196
|
if (isEmpty && lastWasEmpty) {
|
|
15354
15197
|
toRemove.push(index);
|
|
15355
15198
|
}
|
|
@@ -15380,7 +15223,7 @@ export class Document {
|
|
|
15380
15223
|
}
|
|
15381
15224
|
|
|
15382
15225
|
if (standardLineSpacing !== undefined) {
|
|
15383
|
-
para.setLineSpacing(standardLineSpacing,
|
|
15226
|
+
para.setLineSpacing(standardLineSpacing, 'auto');
|
|
15384
15227
|
normalized++;
|
|
15385
15228
|
}
|
|
15386
15229
|
|
|
@@ -15405,8 +15248,7 @@ export class Document {
|
|
|
15405
15248
|
for (const table of this.getTables()) {
|
|
15406
15249
|
for (const row of table.getRows()) {
|
|
15407
15250
|
for (const cell of row.getCells()) {
|
|
15408
|
-
const cellParagraphs =
|
|
15409
|
-
cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
15251
|
+
const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
|
|
15410
15252
|
for (const para of cellParagraphs) {
|
|
15411
15253
|
if (standardParagraphSpacing) {
|
|
15412
15254
|
if (standardParagraphSpacing.before !== undefined) {
|
|
@@ -15420,7 +15262,7 @@ export class Document {
|
|
|
15420
15262
|
}
|
|
15421
15263
|
|
|
15422
15264
|
if (standardLineSpacing !== undefined) {
|
|
15423
|
-
para.setLineSpacing(standardLineSpacing,
|
|
15265
|
+
para.setLineSpacing(standardLineSpacing, 'auto');
|
|
15424
15266
|
normalized++;
|
|
15425
15267
|
}
|
|
15426
15268
|
|
|
@@ -15509,13 +15351,13 @@ export class Document {
|
|
|
15509
15351
|
|
|
15510
15352
|
/**
|
|
15511
15353
|
* Rebuilds all Table of Contents in the document
|
|
15512
|
-
*
|
|
15354
|
+
*
|
|
15513
15355
|
* Analyzes each TOC in the document, parses its field instructions to determine
|
|
15514
15356
|
* which heading levels to include, searches for matching headings (including those
|
|
15515
15357
|
* in nested tables), and returns a summary of TOC instructions and heading counts.
|
|
15516
|
-
*
|
|
15358
|
+
*
|
|
15517
15359
|
* **NEW: This method now also populates the TOCs with hyperlinked entries automatically!**
|
|
15518
|
-
*
|
|
15360
|
+
*
|
|
15519
15361
|
* The method:
|
|
15520
15362
|
* 1. Removes SDT wrappers around tables if found (uses clearCustom helper)
|
|
15521
15363
|
* 2. Ensures `_top` bookmark exists at document start for TOC linking
|
|
@@ -15528,7 +15370,7 @@ export class Document {
|
|
|
15528
15370
|
* 9. **Updates document.xml with the populated TOC**
|
|
15529
15371
|
* 10. Retains field instructions so TOCs can be manually updated later
|
|
15530
15372
|
* 11. Returns summary: [instruction, [h1Count, h2Count, h3Count, ...]]
|
|
15531
|
-
*
|
|
15373
|
+
*
|
|
15532
15374
|
* **Key Features:**
|
|
15533
15375
|
* - No arguments required - analyzes the current document state
|
|
15534
15376
|
* - Searches nested tables when counting headings
|
|
@@ -15539,19 +15381,19 @@ export class Document {
|
|
|
15539
15381
|
* - **Field instructions preserved for manual updates**
|
|
15540
15382
|
* - **No page numbers displayed (pure hyperlink navigation)**
|
|
15541
15383
|
* - Returns summary data for diagnostics and verification
|
|
15542
|
-
*
|
|
15384
|
+
*
|
|
15543
15385
|
* **Output Format:**
|
|
15544
15386
|
* Returns a 2D array where each row contains:
|
|
15545
15387
|
* - Index 0: The TOC field instruction text (e.g., "TOC \\o \"1-3\"")
|
|
15546
15388
|
* - Index 1: Array of heading counts by level (e.g., [5, 12, 8] = 5 H1s, 12 H2s, 8 H3s)
|
|
15547
|
-
*
|
|
15389
|
+
*
|
|
15548
15390
|
* @returns Two-dimensional array of [instruction, headingCounts[]] for each TOC
|
|
15549
|
-
*
|
|
15391
|
+
*
|
|
15550
15392
|
* @example
|
|
15551
15393
|
* ```typescript
|
|
15552
15394
|
* const doc = await Document.load('document.docx');
|
|
15553
15395
|
* const tocInfo = doc.rebuildTOCs();
|
|
15554
|
-
*
|
|
15396
|
+
*
|
|
15555
15397
|
* console.log(`Found ${tocInfo.length} Table(s) of Contents`);
|
|
15556
15398
|
* for (const [instruction, counts] of tocInfo) {
|
|
15557
15399
|
* console.log(`TOC Instruction: ${instruction}`);
|
|
@@ -15561,59 +15403,59 @@ export class Document {
|
|
|
15561
15403
|
* }
|
|
15562
15404
|
* });
|
|
15563
15405
|
* }
|
|
15564
|
-
*
|
|
15406
|
+
*
|
|
15565
15407
|
* // TOCs are now populated with hyperlinks - save the document
|
|
15566
15408
|
* await doc.save('output.docx');
|
|
15567
15409
|
* // When opened in Word, TOCs will display with clickable links, no manual update needed
|
|
15568
15410
|
* ```
|
|
15569
|
-
*
|
|
15411
|
+
*
|
|
15570
15412
|
* @example
|
|
15571
15413
|
* ```typescript
|
|
15572
15414
|
* // Rebuild TOCs and save with populated entries
|
|
15573
15415
|
* const doc = await Document.load('input.docx');
|
|
15574
15416
|
* const tocSummary = doc.rebuildTOCs();
|
|
15575
15417
|
* await doc.save('output.docx');
|
|
15576
|
-
*
|
|
15418
|
+
*
|
|
15577
15419
|
* console.log(`Processed ${tocSummary.length} TOCs with hyperlinked entries`);
|
|
15578
15420
|
* ```
|
|
15579
15421
|
*/
|
|
15580
15422
|
public rebuildTOCs(): [string, number[]][] {
|
|
15581
15423
|
const results: [string, number[]][] = [];
|
|
15582
|
-
|
|
15424
|
+
|
|
15583
15425
|
// Step 1: Remove SDT wrappers around tables if found (helper already exists)
|
|
15584
15426
|
this.clearCustom();
|
|
15585
|
-
|
|
15427
|
+
|
|
15586
15428
|
// Step 2: Ensure _top bookmark exists at document start
|
|
15587
15429
|
this.addTopBookmark();
|
|
15588
|
-
|
|
15430
|
+
|
|
15589
15431
|
// Step 3: Get document.xml to scan for TOC elements
|
|
15590
15432
|
const docXml = this.zipHandler.getFileAsString('word/document.xml');
|
|
15591
15433
|
if (!docXml) {
|
|
15592
15434
|
return results;
|
|
15593
15435
|
}
|
|
15594
|
-
|
|
15436
|
+
|
|
15595
15437
|
// Step 4: Find all TOC SDT elements
|
|
15596
15438
|
const tocRegex = /<w:sdt>[\s\S]*?<w:docPartGallery w:val="Table of Contents"[\s\S]*?<\/w:sdt>/g;
|
|
15597
15439
|
const tocMatches = Array.from(docXml.matchAll(tocRegex));
|
|
15598
|
-
|
|
15440
|
+
|
|
15599
15441
|
if (tocMatches.length === 0) {
|
|
15600
15442
|
return results;
|
|
15601
15443
|
}
|
|
15602
|
-
|
|
15444
|
+
|
|
15603
15445
|
// Step 5: For each TOC, parse instructions and count headings
|
|
15604
15446
|
for (const match of tocMatches) {
|
|
15605
15447
|
try {
|
|
15606
15448
|
const tocXml = match[0];
|
|
15607
|
-
|
|
15449
|
+
|
|
15608
15450
|
// Extract field instruction
|
|
15609
15451
|
const instrMatch = /<w:instrText[^>]*>([\s\S]*?)<\/w:instrText>/.exec(tocXml);
|
|
15610
15452
|
if (!instrMatch?.[1]) {
|
|
15611
15453
|
continue;
|
|
15612
15454
|
}
|
|
15613
|
-
|
|
15455
|
+
|
|
15614
15456
|
// TypeScript type narrowing: assign to const variable
|
|
15615
15457
|
const instrText = instrMatch[1];
|
|
15616
|
-
|
|
15458
|
+
|
|
15617
15459
|
// Decode XML entities
|
|
15618
15460
|
const fieldInstruction = instrText
|
|
15619
15461
|
.replace(/&/g, '&')
|
|
@@ -15621,23 +15463,23 @@ export class Document {
|
|
|
15621
15463
|
.replace(/>/g, '>')
|
|
15622
15464
|
.replace(/"/g, '"')
|
|
15623
15465
|
.replace(/'/g, "'");
|
|
15624
|
-
|
|
15466
|
+
|
|
15625
15467
|
// Parse the instruction to get heading levels
|
|
15626
15468
|
const levels = this.parseTOCFieldInstruction(fieldInstruction);
|
|
15627
|
-
|
|
15469
|
+
|
|
15628
15470
|
// Find all headings in document (including nested tables)
|
|
15629
15471
|
const headings = this.findHeadingsForTOCFromXML(docXml, levels);
|
|
15630
|
-
|
|
15472
|
+
|
|
15631
15473
|
// Count headings by level (create array with counts for each level 1-9)
|
|
15632
15474
|
const headingCounts: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0]; // Indices 0-8 for levels 1-9
|
|
15633
|
-
|
|
15475
|
+
|
|
15634
15476
|
for (const heading of headings) {
|
|
15635
15477
|
if (heading.level >= 1 && heading.level <= 9) {
|
|
15636
15478
|
const index = heading.level - 1;
|
|
15637
15479
|
headingCounts[index] = (headingCounts[index] || 0) + 1;
|
|
15638
15480
|
}
|
|
15639
15481
|
}
|
|
15640
|
-
|
|
15482
|
+
|
|
15641
15483
|
// Add to results: [instruction, counts]
|
|
15642
15484
|
results.push([fieldInstruction, headingCounts]);
|
|
15643
15485
|
} catch (error: unknown) {
|
|
@@ -15651,20 +15493,18 @@ export class Document {
|
|
|
15651
15493
|
continue;
|
|
15652
15494
|
}
|
|
15653
15495
|
}
|
|
15654
|
-
|
|
15496
|
+
|
|
15655
15497
|
// Step 6: Populate all TOCs in the document with hyperlinked entries
|
|
15656
15498
|
// This modifies the XML to include pre-populated TOC entries with hyperlinks
|
|
15657
15499
|
const populatedXml = this.populateAllTOCsInXML(docXml);
|
|
15658
|
-
|
|
15500
|
+
|
|
15659
15501
|
// Step 7: Update document.xml with the populated TOCs
|
|
15660
15502
|
if (populatedXml !== docXml) {
|
|
15661
15503
|
this.zipHandler.updateFile('word/document.xml', populatedXml);
|
|
15662
|
-
|
|
15663
|
-
this.logger.info(
|
|
15664
|
-
`Successfully populated ${results.length} TOC(s) with hyperlinked entries`
|
|
15665
|
-
);
|
|
15504
|
+
|
|
15505
|
+
this.logger.info(`Successfully populated ${results.length} TOC(s) with hyperlinked entries`);
|
|
15666
15506
|
}
|
|
15667
|
-
|
|
15507
|
+
|
|
15668
15508
|
return results;
|
|
15669
15509
|
}
|
|
15670
15510
|
|
|
@@ -15696,14 +15536,14 @@ export class Document {
|
|
|
15696
15536
|
* ```
|
|
15697
15537
|
*/
|
|
15698
15538
|
normalizeTableBorders(options?: {
|
|
15699
|
-
style?:
|
|
15539
|
+
style?: 'single' | 'double' | 'dotted' | 'dashed' | 'thick' | 'none';
|
|
15700
15540
|
size?: number;
|
|
15701
15541
|
color?: string;
|
|
15702
15542
|
}): number {
|
|
15703
15543
|
const border: TableBorder = {
|
|
15704
|
-
style: options?.style ??
|
|
15544
|
+
style: options?.style ?? 'single',
|
|
15705
15545
|
size: options?.size ?? 4,
|
|
15706
|
-
color: options?.color ??
|
|
15546
|
+
color: options?.color ?? '000000',
|
|
15707
15547
|
};
|
|
15708
15548
|
|
|
15709
15549
|
return this.applyBordersToAllTables(border);
|
|
@@ -15754,8 +15594,8 @@ export class Document {
|
|
|
15754
15594
|
|
|
15755
15595
|
// Create regex pattern
|
|
15756
15596
|
const pattern = matchCase
|
|
15757
|
-
? new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g,
|
|
15758
|
-
: new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g,
|
|
15597
|
+
? new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
|
|
15598
|
+
: new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
|
|
15759
15599
|
|
|
15760
15600
|
// Process body paragraphs
|
|
15761
15601
|
for (const para of this.getAllParagraphs()) {
|
|
@@ -15839,36 +15679,36 @@ export class Document {
|
|
|
15839
15679
|
|
|
15840
15680
|
// [Content_Types].xml - minimal
|
|
15841
15681
|
zipHandler.addFile(
|
|
15842
|
-
|
|
15682
|
+
'[Content_Types].xml',
|
|
15843
15683
|
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
15844
15684
|
'<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">\n' +
|
|
15845
15685
|
' <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>\n' +
|
|
15846
15686
|
' <Default Extension="xml" ContentType="application/xml"/>\n' +
|
|
15847
15687
|
' <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>\n' +
|
|
15848
|
-
|
|
15688
|
+
'</Types>'
|
|
15849
15689
|
);
|
|
15850
15690
|
|
|
15851
15691
|
// _rels/.rels - only reference parts that actually exist
|
|
15852
15692
|
zipHandler.addFile(
|
|
15853
|
-
|
|
15693
|
+
'_rels/.rels',
|
|
15854
15694
|
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
15855
15695
|
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n' +
|
|
15856
15696
|
' <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>\n' +
|
|
15857
|
-
|
|
15697
|
+
'</Relationships>'
|
|
15858
15698
|
);
|
|
15859
15699
|
|
|
15860
15700
|
// word/document.xml - empty body
|
|
15861
15701
|
zipHandler.addFile(
|
|
15862
|
-
|
|
15702
|
+
'word/document.xml',
|
|
15863
15703
|
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
15864
15704
|
'<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">\n' +
|
|
15865
|
-
|
|
15866
|
-
|
|
15705
|
+
' <w:body/>\n' +
|
|
15706
|
+
'</w:document>'
|
|
15867
15707
|
);
|
|
15868
15708
|
|
|
15869
15709
|
// word/_rels/document.xml.rels - empty relationships
|
|
15870
15710
|
zipHandler.addFile(
|
|
15871
|
-
|
|
15711
|
+
'word/_rels/document.xml.rels',
|
|
15872
15712
|
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
15873
15713
|
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"/>'
|
|
15874
15714
|
);
|