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
|
@@ -1,428 +1,429 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RelationshipManager - Manages collections of relationships
|
|
3
|
-
*
|
|
4
|
-
* Handles relationship creation, tracking, and XML generation for various
|
|
5
|
-
* document parts (document.xml, header.xml, footer.xml, etc.)
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { Relationship, RelationshipType } from './Relationship';
|
|
9
|
-
import { XMLParser } from '../xml/XMLParser';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Manages relationships for a document or document part
|
|
13
|
-
*/
|
|
14
|
-
export class RelationshipManager {
|
|
15
|
-
private relationships: Map<string, Relationship>;
|
|
16
|
-
private nextId: number;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Creates a new relationship manager
|
|
20
|
-
*/
|
|
21
|
-
constructor() {
|
|
22
|
-
this.relationships = new Map();
|
|
23
|
-
this.nextId = 1;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Adds a relationship
|
|
28
|
-
* @param relationship The relationship to add
|
|
29
|
-
* @returns The relationship that was added
|
|
30
|
-
*/
|
|
31
|
-
addRelationship(relationship: Relationship): Relationship {
|
|
32
|
-
this.relationships.set(relationship.getId(), relationship);
|
|
33
|
-
|
|
34
|
-
// Update next ID if necessary
|
|
35
|
-
const idMatch = /^rId(\d+)$/.exec(relationship.getId());
|
|
36
|
-
if (idMatch?.[1]) {
|
|
37
|
-
const idNum = parseInt(idMatch[1], 10);
|
|
38
|
-
if (idNum >= this.nextId) {
|
|
39
|
-
this.nextId = idNum + 1;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return relationship;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Gets a relationship by ID
|
|
48
|
-
* @param id The relationship ID
|
|
49
|
-
*/
|
|
50
|
-
getRelationship(id: string): Relationship | undefined {
|
|
51
|
-
return this.relationships.get(id);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Gets all relationships
|
|
56
|
-
*/
|
|
57
|
-
getAllRelationships(): Relationship[] {
|
|
58
|
-
return Array.from(this.relationships.values());
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Gets relationships of a specific type
|
|
63
|
-
* @param type The relationship type
|
|
64
|
-
*/
|
|
65
|
-
getRelationshipsByType(type: string | RelationshipType): Relationship[] {
|
|
66
|
-
return this.getAllRelationships().filter(rel => rel.getType() === type);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Checks if a relationship exists
|
|
71
|
-
* @param id The relationship ID
|
|
72
|
-
*/
|
|
73
|
-
hasRelationship(id: string): boolean {
|
|
74
|
-
return this.relationships.has(id);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Removes a relationship
|
|
79
|
-
* @param id The relationship ID
|
|
80
|
-
* @returns True if removed, false if not found
|
|
81
|
-
*/
|
|
82
|
-
removeRelationship(id: string): boolean {
|
|
83
|
-
return this.relationships.delete(id);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Gets the number of relationships
|
|
88
|
-
*/
|
|
89
|
-
getCount(): number {
|
|
90
|
-
return this.relationships.size;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Clears all relationships
|
|
95
|
-
*/
|
|
96
|
-
clear(): this {
|
|
97
|
-
this.relationships.clear();
|
|
98
|
-
this.nextId = 1;
|
|
99
|
-
return this;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Generates a new unique relationship ID
|
|
104
|
-
* @returns New relationship ID (e.g., 'rId1', 'rId2')
|
|
105
|
-
*/
|
|
106
|
-
generateId(): string {
|
|
107
|
-
return `rId${this.nextId++}`;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Adds a styles relationship
|
|
112
|
-
* @returns The created relationship
|
|
113
|
-
*/
|
|
114
|
-
addStyles(): Relationship {
|
|
115
|
-
const id = this.generateId();
|
|
116
|
-
return this.addRelationship(Relationship.createStyles(id));
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Adds a numbering relationship
|
|
121
|
-
* @returns The created relationship
|
|
122
|
-
*/
|
|
123
|
-
addNumbering(): Relationship {
|
|
124
|
-
const id = this.generateId();
|
|
125
|
-
return this.addRelationship(Relationship.createNumbering(id));
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Adds a fontTable relationship
|
|
130
|
-
* @returns The created relationship
|
|
131
|
-
*/
|
|
132
|
-
addFontTable(): Relationship {
|
|
133
|
-
const id = this.generateId();
|
|
134
|
-
return this.addRelationship(Relationship.createFontTable(id));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Adds a settings relationship
|
|
139
|
-
* @returns The created relationship
|
|
140
|
-
*/
|
|
141
|
-
addSettings(): Relationship {
|
|
142
|
-
const id = this.generateId();
|
|
143
|
-
return this.addRelationship(Relationship.createSettings(id));
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Adds a webSettings relationship
|
|
148
|
-
* @returns The created relationship
|
|
149
|
-
*/
|
|
150
|
-
addWebSettings(): Relationship {
|
|
151
|
-
const id = this.generateId();
|
|
152
|
-
return this.addRelationship(Relationship.createWebSettings(id));
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Adds a theme relationship
|
|
157
|
-
* @returns The created relationship
|
|
158
|
-
*/
|
|
159
|
-
addTheme(): Relationship {
|
|
160
|
-
const id = this.generateId();
|
|
161
|
-
return this.addRelationship(Relationship.createTheme(id));
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Adds an image relationship
|
|
166
|
-
* @param target Image path relative to the part (e.g., 'media/image1.png')
|
|
167
|
-
* @returns The created relationship
|
|
168
|
-
*/
|
|
169
|
-
addImage(target: string): Relationship {
|
|
170
|
-
const id = this.generateId();
|
|
171
|
-
return this.addRelationship(Relationship.createImage(id, target));
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Adds a header relationship
|
|
176
|
-
* @param target Header file path (e.g., 'header1.xml')
|
|
177
|
-
* @returns The created relationship
|
|
178
|
-
*/
|
|
179
|
-
addHeader(target: string): Relationship {
|
|
180
|
-
const id = this.generateId();
|
|
181
|
-
return this.addRelationship(Relationship.createHeader(id, target));
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Adds a footer relationship
|
|
186
|
-
* @param target Footer file path (e.g., 'footer1.xml')
|
|
187
|
-
* @returns The created relationship
|
|
188
|
-
*/
|
|
189
|
-
addFooter(target: string): Relationship {
|
|
190
|
-
const id = this.generateId();
|
|
191
|
-
return this.addRelationship(Relationship.createFooter(id, target));
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Adds a hyperlink relationship
|
|
196
|
-
* @param url The hyperlink URL
|
|
197
|
-
* @returns The created relationship
|
|
198
|
-
*/
|
|
199
|
-
addHyperlink(url: string): Relationship {
|
|
200
|
-
const id = this.generateId();
|
|
201
|
-
return this.addRelationship(Relationship.createHyperlink(id, url));
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Updates the target URL of an existing hyperlink relationship
|
|
206
|
-
*
|
|
207
|
-
* This method modifies an existing relationship's target in-place, maintaining
|
|
208
|
-
* the same relationship ID. This is crucial for proper OpenXML compliance
|
|
209
|
-
* per ECMA-376 §17.16.22, as it prevents orphaned relationships.
|
|
210
|
-
*
|
|
211
|
-
* @param relationshipId The ID of the relationship to update
|
|
212
|
-
* @param newUrl The new URL to set
|
|
213
|
-
* @returns True if updated, false if relationship not found
|
|
214
|
-
*/
|
|
215
|
-
updateHyperlinkTarget(relationshipId: string, newUrl: string): boolean {
|
|
216
|
-
const relationship = this.getRelationship(relationshipId);
|
|
217
|
-
if (!relationship) {
|
|
218
|
-
return false;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Verify this is a hyperlink relationship
|
|
222
|
-
if (relationship.getType() !== RelationshipType.HYPERLINK) {
|
|
223
|
-
throw new Error(
|
|
224
|
-
`Relationship ${relationshipId} is not a hyperlink relationship. ` +
|
|
225
|
-
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Update the target URL
|
|
230
|
-
relationship.setTarget(newUrl);
|
|
231
|
-
return true;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Finds a hyperlink relationship by its target URL
|
|
236
|
-
*
|
|
237
|
-
* @param targetUrl The URL to search for
|
|
238
|
-
* @returns The matching relationship, or undefined if not found
|
|
239
|
-
*/
|
|
240
|
-
findHyperlinkByTarget(targetUrl: string): Relationship | undefined {
|
|
241
|
-
return this.getAllRelationships().find(
|
|
242
|
-
rel => rel.getType() === RelationshipType.HYPERLINK && rel.getTarget() === targetUrl
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Gets or creates a hyperlink relationship for the given URL
|
|
248
|
-
*
|
|
249
|
-
* This method ensures we don't create duplicate relationships for the same URL.
|
|
250
|
-
* If a relationship already exists for the URL, it returns the existing one.
|
|
251
|
-
* Otherwise, it creates a new relationship.
|
|
252
|
-
*
|
|
253
|
-
* @param url The hyperlink URL
|
|
254
|
-
* @returns The existing or newly created relationship
|
|
255
|
-
*/
|
|
256
|
-
getOrCreateHyperlink(url: string): Relationship {
|
|
257
|
-
// Check if relationship already exists for this URL
|
|
258
|
-
const existing = this.findHyperlinkByTarget(url);
|
|
259
|
-
if (existing) {
|
|
260
|
-
return existing;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Create new relationship
|
|
264
|
-
return this.addHyperlink(url);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Removes orphaned hyperlink relationships
|
|
269
|
-
*
|
|
270
|
-
* This method removes hyperlink relationships that are no longer referenced
|
|
271
|
-
* by any hyperlink in the document. Call this after updating URLs to clean
|
|
272
|
-
* up any orphaned relationships.
|
|
273
|
-
*
|
|
274
|
-
* @param referencedIds Set of relationship IDs that are still in use
|
|
275
|
-
* @returns Number of relationships removed
|
|
276
|
-
*/
|
|
277
|
-
removeOrphanedHyperlinks(referencedIds: Set<string>): number {
|
|
278
|
-
let removed = 0;
|
|
279
|
-
const toRemove: string[] = [];
|
|
280
|
-
|
|
281
|
-
// Find orphaned relationships
|
|
282
|
-
for (const rel of this.getAllRelationships()) {
|
|
283
|
-
if (rel.getType() === RelationshipType.HYPERLINK && !referencedIds.has(rel.getId())) {
|
|
284
|
-
toRemove.push(rel.getId());
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Remove orphaned relationships
|
|
289
|
-
for (const id of toRemove) {
|
|
290
|
-
if (this.removeRelationship(id)) {
|
|
291
|
-
removed++;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return removed;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Adds a comments relationship
|
|
300
|
-
* @returns The created relationship
|
|
301
|
-
*/
|
|
302
|
-
addComments(): Relationship {
|
|
303
|
-
const id = this.generateId();
|
|
304
|
-
return this.addRelationship(Relationship.createComments(id));
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Adds a footnotes relationship
|
|
309
|
-
* @returns The created relationship
|
|
310
|
-
*/
|
|
311
|
-
addFootnotes(): Relationship {
|
|
312
|
-
const id = this.generateId();
|
|
313
|
-
return this.addRelationship(Relationship.createFootnotes(id));
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Adds an endnotes relationship
|
|
318
|
-
* @returns The created relationship
|
|
319
|
-
*/
|
|
320
|
-
addEndnotes(): Relationship {
|
|
321
|
-
const id = this.generateId();
|
|
322
|
-
return this.addRelationship(Relationship.createEndnotes(id));
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Adds a people relationship (track changes authors)
|
|
327
|
-
* @returns The created relationship
|
|
328
|
-
*/
|
|
329
|
-
addPeople(): Relationship {
|
|
330
|
-
const id = this.generateId();
|
|
331
|
-
return this.addRelationship(Relationship.createPeople(id));
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Generates the relationships XML file content
|
|
336
|
-
* @returns Complete XML string for .rels file
|
|
337
|
-
*/
|
|
338
|
-
generateXml(): string {
|
|
339
|
-
const relationships = this.getAllRelationships();
|
|
340
|
-
|
|
341
|
-
let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
|
|
342
|
-
xml += '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n';
|
|
343
|
-
|
|
344
|
-
for (const rel of relationships) {
|
|
345
|
-
xml += rel.toXML() + '\n';
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
xml += '</Relationships>';
|
|
349
|
-
|
|
350
|
-
return xml;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Creates a new relationship manager with common document relationships
|
|
355
|
-
* @returns RelationshipManager with styles, numbering, fontTable, settings, and theme relationships
|
|
356
|
-
*/
|
|
357
|
-
static createForDocument(): RelationshipManager {
|
|
358
|
-
const manager = new RelationshipManager();
|
|
359
|
-
manager.addStyles();
|
|
360
|
-
manager.addNumbering();
|
|
361
|
-
manager.addFontTable();
|
|
362
|
-
manager.addSettings();
|
|
363
|
-
manager.addWebSettings();
|
|
364
|
-
manager.addTheme();
|
|
365
|
-
return manager;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Creates an empty relationship manager
|
|
370
|
-
* @returns Empty RelationshipManager
|
|
371
|
-
*/
|
|
372
|
-
static create(): RelationshipManager {
|
|
373
|
-
return new RelationshipManager();
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Parses relationships from XML string and creates a populated manager
|
|
378
|
-
* @param xml The relationships XML content (.rels file)
|
|
379
|
-
* @returns RelationshipManager with parsed relationships
|
|
380
|
-
*/
|
|
381
|
-
static fromXml(xml: string): RelationshipManager {
|
|
382
|
-
const manager = new RelationshipManager();
|
|
383
|
-
|
|
384
|
-
// Prevent ReDoS: validate input size (typical .rels files are < 10KB)
|
|
385
|
-
if (xml.length > 100000) {
|
|
386
|
-
throw new Error(
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* RelationshipManager - Manages collections of relationships
|
|
3
|
+
*
|
|
4
|
+
* Handles relationship creation, tracking, and XML generation for various
|
|
5
|
+
* document parts (document.xml, header.xml, footer.xml, etc.)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Relationship, RelationshipType } from './Relationship';
|
|
9
|
+
import { XMLParser } from '../xml/XMLParser';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Manages relationships for a document or document part
|
|
13
|
+
*/
|
|
14
|
+
export class RelationshipManager {
|
|
15
|
+
private relationships: Map<string, Relationship>;
|
|
16
|
+
private nextId: number;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new relationship manager
|
|
20
|
+
*/
|
|
21
|
+
constructor() {
|
|
22
|
+
this.relationships = new Map();
|
|
23
|
+
this.nextId = 1;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Adds a relationship
|
|
28
|
+
* @param relationship The relationship to add
|
|
29
|
+
* @returns The relationship that was added
|
|
30
|
+
*/
|
|
31
|
+
addRelationship(relationship: Relationship): Relationship {
|
|
32
|
+
this.relationships.set(relationship.getId(), relationship);
|
|
33
|
+
|
|
34
|
+
// Update next ID if necessary
|
|
35
|
+
const idMatch = /^rId(\d+)$/.exec(relationship.getId());
|
|
36
|
+
if (idMatch?.[1]) {
|
|
37
|
+
const idNum = parseInt(idMatch[1], 10);
|
|
38
|
+
if (idNum >= this.nextId) {
|
|
39
|
+
this.nextId = idNum + 1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return relationship;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Gets a relationship by ID
|
|
48
|
+
* @param id The relationship ID
|
|
49
|
+
*/
|
|
50
|
+
getRelationship(id: string): Relationship | undefined {
|
|
51
|
+
return this.relationships.get(id);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Gets all relationships
|
|
56
|
+
*/
|
|
57
|
+
getAllRelationships(): Relationship[] {
|
|
58
|
+
return Array.from(this.relationships.values());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Gets relationships of a specific type
|
|
63
|
+
* @param type The relationship type
|
|
64
|
+
*/
|
|
65
|
+
getRelationshipsByType(type: string | RelationshipType): Relationship[] {
|
|
66
|
+
return this.getAllRelationships().filter((rel) => rel.getType() === type);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Checks if a relationship exists
|
|
71
|
+
* @param id The relationship ID
|
|
72
|
+
*/
|
|
73
|
+
hasRelationship(id: string): boolean {
|
|
74
|
+
return this.relationships.has(id);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Removes a relationship
|
|
79
|
+
* @param id The relationship ID
|
|
80
|
+
* @returns True if removed, false if not found
|
|
81
|
+
*/
|
|
82
|
+
removeRelationship(id: string): boolean {
|
|
83
|
+
return this.relationships.delete(id);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Gets the number of relationships
|
|
88
|
+
*/
|
|
89
|
+
getCount(): number {
|
|
90
|
+
return this.relationships.size;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Clears all relationships
|
|
95
|
+
*/
|
|
96
|
+
clear(): this {
|
|
97
|
+
this.relationships.clear();
|
|
98
|
+
this.nextId = 1;
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Generates a new unique relationship ID
|
|
104
|
+
* @returns New relationship ID (e.g., 'rId1', 'rId2')
|
|
105
|
+
*/
|
|
106
|
+
generateId(): string {
|
|
107
|
+
return `rId${this.nextId++}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Adds a styles relationship
|
|
112
|
+
* @returns The created relationship
|
|
113
|
+
*/
|
|
114
|
+
addStyles(): Relationship {
|
|
115
|
+
const id = this.generateId();
|
|
116
|
+
return this.addRelationship(Relationship.createStyles(id));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Adds a numbering relationship
|
|
121
|
+
* @returns The created relationship
|
|
122
|
+
*/
|
|
123
|
+
addNumbering(): Relationship {
|
|
124
|
+
const id = this.generateId();
|
|
125
|
+
return this.addRelationship(Relationship.createNumbering(id));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Adds a fontTable relationship
|
|
130
|
+
* @returns The created relationship
|
|
131
|
+
*/
|
|
132
|
+
addFontTable(): Relationship {
|
|
133
|
+
const id = this.generateId();
|
|
134
|
+
return this.addRelationship(Relationship.createFontTable(id));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Adds a settings relationship
|
|
139
|
+
* @returns The created relationship
|
|
140
|
+
*/
|
|
141
|
+
addSettings(): Relationship {
|
|
142
|
+
const id = this.generateId();
|
|
143
|
+
return this.addRelationship(Relationship.createSettings(id));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Adds a webSettings relationship
|
|
148
|
+
* @returns The created relationship
|
|
149
|
+
*/
|
|
150
|
+
addWebSettings(): Relationship {
|
|
151
|
+
const id = this.generateId();
|
|
152
|
+
return this.addRelationship(Relationship.createWebSettings(id));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Adds a theme relationship
|
|
157
|
+
* @returns The created relationship
|
|
158
|
+
*/
|
|
159
|
+
addTheme(): Relationship {
|
|
160
|
+
const id = this.generateId();
|
|
161
|
+
return this.addRelationship(Relationship.createTheme(id));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Adds an image relationship
|
|
166
|
+
* @param target Image path relative to the part (e.g., 'media/image1.png')
|
|
167
|
+
* @returns The created relationship
|
|
168
|
+
*/
|
|
169
|
+
addImage(target: string): Relationship {
|
|
170
|
+
const id = this.generateId();
|
|
171
|
+
return this.addRelationship(Relationship.createImage(id, target));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Adds a header relationship
|
|
176
|
+
* @param target Header file path (e.g., 'header1.xml')
|
|
177
|
+
* @returns The created relationship
|
|
178
|
+
*/
|
|
179
|
+
addHeader(target: string): Relationship {
|
|
180
|
+
const id = this.generateId();
|
|
181
|
+
return this.addRelationship(Relationship.createHeader(id, target));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Adds a footer relationship
|
|
186
|
+
* @param target Footer file path (e.g., 'footer1.xml')
|
|
187
|
+
* @returns The created relationship
|
|
188
|
+
*/
|
|
189
|
+
addFooter(target: string): Relationship {
|
|
190
|
+
const id = this.generateId();
|
|
191
|
+
return this.addRelationship(Relationship.createFooter(id, target));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Adds a hyperlink relationship
|
|
196
|
+
* @param url The hyperlink URL
|
|
197
|
+
* @returns The created relationship
|
|
198
|
+
*/
|
|
199
|
+
addHyperlink(url: string): Relationship {
|
|
200
|
+
const id = this.generateId();
|
|
201
|
+
return this.addRelationship(Relationship.createHyperlink(id, url));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Updates the target URL of an existing hyperlink relationship
|
|
206
|
+
*
|
|
207
|
+
* This method modifies an existing relationship's target in-place, maintaining
|
|
208
|
+
* the same relationship ID. This is crucial for proper OpenXML compliance
|
|
209
|
+
* per ECMA-376 §17.16.22, as it prevents orphaned relationships.
|
|
210
|
+
*
|
|
211
|
+
* @param relationshipId The ID of the relationship to update
|
|
212
|
+
* @param newUrl The new URL to set
|
|
213
|
+
* @returns True if updated, false if relationship not found
|
|
214
|
+
*/
|
|
215
|
+
updateHyperlinkTarget(relationshipId: string, newUrl: string): boolean {
|
|
216
|
+
const relationship = this.getRelationship(relationshipId);
|
|
217
|
+
if (!relationship) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Verify this is a hyperlink relationship
|
|
222
|
+
if (relationship.getType() !== RelationshipType.HYPERLINK) {
|
|
223
|
+
throw new Error(
|
|
224
|
+
`Relationship ${relationshipId} is not a hyperlink relationship. ` +
|
|
225
|
+
`Type is ${relationship.getType()}, expected ${RelationshipType.HYPERLINK}`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Update the target URL
|
|
230
|
+
relationship.setTarget(newUrl);
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Finds a hyperlink relationship by its target URL
|
|
236
|
+
*
|
|
237
|
+
* @param targetUrl The URL to search for
|
|
238
|
+
* @returns The matching relationship, or undefined if not found
|
|
239
|
+
*/
|
|
240
|
+
findHyperlinkByTarget(targetUrl: string): Relationship | undefined {
|
|
241
|
+
return this.getAllRelationships().find(
|
|
242
|
+
(rel) => rel.getType() === RelationshipType.HYPERLINK && rel.getTarget() === targetUrl
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Gets or creates a hyperlink relationship for the given URL
|
|
248
|
+
*
|
|
249
|
+
* This method ensures we don't create duplicate relationships for the same URL.
|
|
250
|
+
* If a relationship already exists for the URL, it returns the existing one.
|
|
251
|
+
* Otherwise, it creates a new relationship.
|
|
252
|
+
*
|
|
253
|
+
* @param url The hyperlink URL
|
|
254
|
+
* @returns The existing or newly created relationship
|
|
255
|
+
*/
|
|
256
|
+
getOrCreateHyperlink(url: string): Relationship {
|
|
257
|
+
// Check if relationship already exists for this URL
|
|
258
|
+
const existing = this.findHyperlinkByTarget(url);
|
|
259
|
+
if (existing) {
|
|
260
|
+
return existing;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Create new relationship
|
|
264
|
+
return this.addHyperlink(url);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Removes orphaned hyperlink relationships
|
|
269
|
+
*
|
|
270
|
+
* This method removes hyperlink relationships that are no longer referenced
|
|
271
|
+
* by any hyperlink in the document. Call this after updating URLs to clean
|
|
272
|
+
* up any orphaned relationships.
|
|
273
|
+
*
|
|
274
|
+
* @param referencedIds Set of relationship IDs that are still in use
|
|
275
|
+
* @returns Number of relationships removed
|
|
276
|
+
*/
|
|
277
|
+
removeOrphanedHyperlinks(referencedIds: Set<string>): number {
|
|
278
|
+
let removed = 0;
|
|
279
|
+
const toRemove: string[] = [];
|
|
280
|
+
|
|
281
|
+
// Find orphaned relationships
|
|
282
|
+
for (const rel of this.getAllRelationships()) {
|
|
283
|
+
if (rel.getType() === RelationshipType.HYPERLINK && !referencedIds.has(rel.getId())) {
|
|
284
|
+
toRemove.push(rel.getId());
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Remove orphaned relationships
|
|
289
|
+
for (const id of toRemove) {
|
|
290
|
+
if (this.removeRelationship(id)) {
|
|
291
|
+
removed++;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return removed;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Adds a comments relationship
|
|
300
|
+
* @returns The created relationship
|
|
301
|
+
*/
|
|
302
|
+
addComments(): Relationship {
|
|
303
|
+
const id = this.generateId();
|
|
304
|
+
return this.addRelationship(Relationship.createComments(id));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Adds a footnotes relationship
|
|
309
|
+
* @returns The created relationship
|
|
310
|
+
*/
|
|
311
|
+
addFootnotes(): Relationship {
|
|
312
|
+
const id = this.generateId();
|
|
313
|
+
return this.addRelationship(Relationship.createFootnotes(id));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Adds an endnotes relationship
|
|
318
|
+
* @returns The created relationship
|
|
319
|
+
*/
|
|
320
|
+
addEndnotes(): Relationship {
|
|
321
|
+
const id = this.generateId();
|
|
322
|
+
return this.addRelationship(Relationship.createEndnotes(id));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Adds a people relationship (track changes authors)
|
|
327
|
+
* @returns The created relationship
|
|
328
|
+
*/
|
|
329
|
+
addPeople(): Relationship {
|
|
330
|
+
const id = this.generateId();
|
|
331
|
+
return this.addRelationship(Relationship.createPeople(id));
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Generates the relationships XML file content
|
|
336
|
+
* @returns Complete XML string for .rels file
|
|
337
|
+
*/
|
|
338
|
+
generateXml(): string {
|
|
339
|
+
const relationships = this.getAllRelationships();
|
|
340
|
+
|
|
341
|
+
let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
|
|
342
|
+
xml += '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n';
|
|
343
|
+
|
|
344
|
+
for (const rel of relationships) {
|
|
345
|
+
xml += rel.toXML() + '\n';
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
xml += '</Relationships>';
|
|
349
|
+
|
|
350
|
+
return xml;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Creates a new relationship manager with common document relationships
|
|
355
|
+
* @returns RelationshipManager with styles, numbering, fontTable, settings, and theme relationships
|
|
356
|
+
*/
|
|
357
|
+
static createForDocument(): RelationshipManager {
|
|
358
|
+
const manager = new RelationshipManager();
|
|
359
|
+
manager.addStyles();
|
|
360
|
+
manager.addNumbering();
|
|
361
|
+
manager.addFontTable();
|
|
362
|
+
manager.addSettings();
|
|
363
|
+
manager.addWebSettings();
|
|
364
|
+
manager.addTheme();
|
|
365
|
+
return manager;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Creates an empty relationship manager
|
|
370
|
+
* @returns Empty RelationshipManager
|
|
371
|
+
*/
|
|
372
|
+
static create(): RelationshipManager {
|
|
373
|
+
return new RelationshipManager();
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Parses relationships from XML string and creates a populated manager
|
|
378
|
+
* @param xml The relationships XML content (.rels file)
|
|
379
|
+
* @returns RelationshipManager with parsed relationships
|
|
380
|
+
*/
|
|
381
|
+
static fromXml(xml: string): RelationshipManager {
|
|
382
|
+
const manager = new RelationshipManager();
|
|
383
|
+
|
|
384
|
+
// Prevent ReDoS: validate input size (typical .rels files are < 10KB)
|
|
385
|
+
if (xml.length > 100000) {
|
|
386
|
+
throw new Error(
|
|
387
|
+
'Relationships XML file too large (>100KB). Possible malicious input or corrupted file.'
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Use XMLParser to extract all Relationship elements
|
|
392
|
+
const relationshipElements = XMLParser.extractElements(xml, 'Relationship');
|
|
393
|
+
|
|
394
|
+
// Prevent infinite loops: check relationship count
|
|
395
|
+
if (relationshipElements.length > 1000) {
|
|
396
|
+
throw new Error('Too many relationships in XML file (>1000). Possible malicious input.');
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Process each relationship element
|
|
400
|
+
for (const relationshipElement of relationshipElements) {
|
|
401
|
+
// Extract attributes using XMLParser
|
|
402
|
+
const id = XMLParser.extractAttribute(relationshipElement, 'Id');
|
|
403
|
+
const type = XMLParser.extractAttribute(relationshipElement, 'Type');
|
|
404
|
+
const target = XMLParser.extractAttribute(relationshipElement, 'Target');
|
|
405
|
+
const targetMode = XMLParser.extractAttribute(relationshipElement, 'TargetMode');
|
|
406
|
+
|
|
407
|
+
// Only create relationship if all required attributes present
|
|
408
|
+
if (id && type && target) {
|
|
409
|
+
// Validate targetMode before type assertion
|
|
410
|
+
const validatedTargetMode =
|
|
411
|
+
targetMode === 'Internal' || targetMode === 'External' || targetMode === undefined
|
|
412
|
+
? targetMode
|
|
413
|
+
: undefined;
|
|
414
|
+
|
|
415
|
+
// Create and add relationship
|
|
416
|
+
const relationship = Relationship.create({
|
|
417
|
+
id,
|
|
418
|
+
type,
|
|
419
|
+
target,
|
|
420
|
+
targetMode: validatedTargetMode || 'Internal',
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
manager.addRelationship(relationship);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return manager;
|
|
428
|
+
}
|
|
429
|
+
}
|