@docx4j/core-ts 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/NOTICE +4 -0
  2. package/README.md +34 -3
  3. package/dist/html/convert.mjs +40 -15
  4. package/dist/html/elements.mjs +2 -1
  5. package/dist/model/color/ColorMath.d.mts +29 -0
  6. package/dist/model/color/ColorMath.mjs +146 -0
  7. package/dist/model/color/ColorTables.d.mts +6 -0
  8. package/dist/model/color/ColorTables.mjs +83 -0
  9. package/dist/model/color/DrawingMLColors.d.mts +52 -0
  10. package/dist/model/color/DrawingMLColors.mjs +374 -0
  11. package/dist/model/color/ResolvedColor.d.mts +36 -0
  12. package/dist/model/color/ResolvedColor.mjs +65 -0
  13. package/dist/model/color/SpreadsheetColors.d.mts +36 -0
  14. package/dist/model/color/SpreadsheetColors.mjs +84 -0
  15. package/dist/model/color/StyleFill.d.mts +28 -0
  16. package/dist/model/color/StyleFill.mjs +21 -0
  17. package/dist/model/color/ThemeColors.d.mts +86 -0
  18. package/dist/model/color/ThemeColors.mjs +304 -0
  19. package/dist/model/color/WordColors.d.mts +71 -0
  20. package/dist/model/color/WordColors.mjs +121 -0
  21. package/dist/model/color/index.d.mts +6 -0
  22. package/dist/model/color/index.mjs +30 -0
  23. package/dist/model/content/Body.d.mts +153 -19
  24. package/dist/model/content/Body.mjs +570 -131
  25. package/dist/model/content/Comment.d.mts +21 -3
  26. package/dist/model/content/Comment.mjs +202 -19
  27. package/dist/model/content/ContentControl.d.mts +197 -18
  28. package/dist/model/content/ContentControl.mjs +664 -95
  29. package/dist/model/content/Font.d.mts +67 -17
  30. package/dist/model/content/Font.mjs +114 -35
  31. package/dist/model/content/InlinePicture.d.mts +82 -7
  32. package/dist/model/content/InlinePicture.mjs +221 -28
  33. package/dist/model/content/List.mjs +13 -1
  34. package/dist/model/content/Paragraph.d.mts +213 -14
  35. package/dist/model/content/Paragraph.mjs +936 -100
  36. package/dist/model/content/Range.d.mts +242 -11
  37. package/dist/model/content/Range.mjs +975 -119
  38. package/dist/model/content/ResolvedView.d.mts +104 -0
  39. package/dist/model/content/ResolvedView.mjs +596 -0
  40. package/dist/model/content/Table.d.mts +29 -3
  41. package/dist/model/content/Table.mjs +109 -33
  42. package/dist/model/content/TrackedChange.d.mts +301 -16
  43. package/dist/model/content/TrackedChange.mjs +1284 -158
  44. package/dist/model/content/binder.d.mts +2 -1
  45. package/dist/model/content/binder.mjs +48 -1
  46. package/dist/model/content/comments.d.mts +20 -6
  47. package/dist/model/content/comments.mjs +67 -57
  48. package/dist/model/content/generalException.d.mts +18 -0
  49. package/dist/model/content/generalException.mjs +24 -0
  50. package/dist/model/content/index.d.mts +8 -3
  51. package/dist/model/content/index.mjs +7 -2
  52. package/dist/model/content/mathText.d.mts +21 -0
  53. package/dist/model/content/mathText.mjs +267 -0
  54. package/dist/model/content/officeText.d.mts +27 -0
  55. package/dist/model/content/officeText.mjs +74 -0
  56. package/dist/model/content/outline.d.mts +5 -0
  57. package/dist/model/content/outline.mjs +13 -0
  58. package/dist/model/content/placeholders.d.mts +110 -0
  59. package/dist/model/content/placeholders.mjs +253 -0
  60. package/dist/model/content/relationships.d.mts +30 -0
  61. package/dist/model/content/relationships.mjs +133 -0
  62. package/dist/model/content/search.d.mts +72 -0
  63. package/dist/model/content/search.mjs +204 -2
  64. package/dist/model/content/spanEdits.d.mts +159 -0
  65. package/dist/model/content/spanEdits.mjs +728 -0
  66. package/dist/model/content/story.d.mts +44 -0
  67. package/dist/model/content/story.mjs +92 -0
  68. package/dist/model/content/tableColumns.d.mts +1 -1
  69. package/dist/model/content/tableColumns.mjs +108 -80
  70. package/dist/model/content/tableRevisions.d.mts +11 -3
  71. package/dist/model/content/tableRevisions.mjs +72 -42
  72. package/dist/model/content/tableRules.d.mts +30 -0
  73. package/dist/model/content/tableRules.mjs +100 -0
  74. package/dist/model/content/textBoxes.d.mts +18 -0
  75. package/dist/model/content/textBoxes.mjs +105 -0
  76. package/dist/model/content/trackedChangeFilter.d.mts +39 -0
  77. package/dist/model/content/trackedChangeFilter.mjs +81 -0
  78. package/dist/model/content/tracking.d.mts +28 -0
  79. package/dist/model/content/tracking.mjs +149 -10
  80. package/dist/model/content/tree.d.mts +212 -13
  81. package/dist/model/content/tree.mjs +356 -35
  82. package/dist/model/customxml/CustomXmlPartCollection.d.mts +8 -0
  83. package/dist/model/customxml/CustomXmlPartCollection.mjs +34 -11
  84. package/dist/model/customxml/XmlMapping.d.mts +1 -1
  85. package/dist/model/customxml/XmlMapping.mjs +11 -0
  86. package/dist/model/customxml/bindings.d.mts +21 -2
  87. package/dist/model/customxml/bindings.mjs +76 -32
  88. package/dist/model/customxml/kinds.d.mts +8 -3
  89. package/dist/model/customxml/kinds.mjs +26 -5
  90. package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
  91. package/dist/model/customxml/picturePlaceholder.mjs +32 -0
  92. package/dist/model/customxml/pictures.d.mts +9 -5
  93. package/dist/model/customxml/pictures.mjs +31 -13
  94. package/dist/model/customxml/xhtml.mjs +8 -2
  95. package/dist/model/fonts/families.generated.mjs +2 -2
  96. package/dist/model/fonts/registry.mjs +7 -1
  97. package/dist/model/fonts/substitutions.generated.mjs +2 -2
  98. package/dist/model/fonts/themes.generated.mjs +1 -1
  99. package/dist/model/index.d.mts +2 -0
  100. package/dist/model/index.mjs +4 -0
  101. package/dist/model/listnumbering/Emulator.d.mts +37 -5
  102. package/dist/model/listnumbering/Emulator.mjs +63 -9
  103. package/dist/model/listnumbering/definitions.mjs +18 -0
  104. package/dist/model/properties/PropertyResolver.d.mts +87 -7
  105. package/dist/model/properties/PropertyResolver.mjs +197 -17
  106. package/dist/model/properties/catalogue.d.mts +32 -4
  107. package/dist/model/properties/catalogue.mjs +115 -36
  108. package/dist/model/properties/log.d.mts +1 -1
  109. package/dist/model/properties/log.mjs +5 -5
  110. package/dist/model/properties/table.d.mts +16 -2
  111. package/dist/model/properties/table.mjs +17 -5
  112. package/dist/model/text/Extractor.d.mts +289 -0
  113. package/dist/model/text/Extractor.mjs +2162 -0
  114. package/dist/model/text/LinkTargets.d.mts +45 -0
  115. package/dist/model/text/LinkTargets.mjs +170 -0
  116. package/dist/model/text/MarkdownExporter.d.mts +140 -0
  117. package/dist/model/text/MarkdownExporter.mjs +255 -0
  118. package/dist/model/text/PlainText.d.mts +29 -0
  119. package/dist/model/text/PlainText.mjs +539 -0
  120. package/dist/model/text/TextContext.d.mts +161 -0
  121. package/dist/model/text/TextContext.mjs +425 -0
  122. package/dist/model/text/TextOptions.d.mts +150 -0
  123. package/dist/model/text/TextOptions.mjs +278 -0
  124. package/dist/model/text/TextResult.d.mts +504 -0
  125. package/dist/model/text/TextResult.mjs +526 -0
  126. package/dist/model/text/Texts.d.mts +99 -0
  127. package/dist/model/text/Texts.mjs +187 -0
  128. package/dist/model/text/WmlToMarkdown.d.mts +120 -0
  129. package/dist/model/text/WmlToMarkdown.mjs +911 -0
  130. package/dist/model/text/commonmark.d.mts +121 -0
  131. package/dist/model/text/commonmark.mjs +655 -0
  132. package/dist/model/text/index.d.mts +7 -0
  133. package/dist/model/text/index.mjs +8 -0
  134. package/dist/model/text/normalize.d.mts +8 -0
  135. package/dist/model/text/normalize.mjs +13 -0
  136. package/dist/model/text/outline.d.mts +4 -0
  137. package/dist/model/text/outline.mjs +71 -0
  138. package/dist/office-js/fragment.d.mts +7 -5
  139. package/dist/office-js/fragment.mjs +63 -14
  140. package/dist/office-js/proxy.d.mts +8 -0
  141. package/dist/office-js/proxy.mjs +29 -0
  142. package/dist/office-js/run.mjs +2 -0
  143. package/dist/office-js/supported.generated.mjs +16 -0
  144. package/dist/office-js/toApiScript.mjs +21 -5
  145. package/dist/opc/Load.d.mts +9 -2
  146. package/dist/opc/Save.d.mts +17 -0
  147. package/dist/opc/Save.mjs +12 -2
  148. package/dist/opc/index.d.mts +3 -3
  149. package/dist/opc/index.mjs +2 -2
  150. package/dist/opc/mce/preprocessor.d.mts +88 -7
  151. package/dist/opc/mce/preprocessor.mjs +144 -37
  152. package/dist/opc/mce/understood.d.mts +34 -0
  153. package/dist/opc/mce/understood.mjs +55 -3
  154. package/dist/packages/OpcPackage.d.mts +20 -0
  155. package/dist/packages/OpcPackage.mjs +16 -3
  156. package/dist/packages/WordprocessingMLPackage.d.mts +77 -7
  157. package/dist/packages/WordprocessingMLPackage.mjs +242 -11
  158. package/dist/parts/DefaultXmlPart.mjs +2 -2
  159. package/dist/parts/Part.d.mts +10 -2
  160. package/dist/parts/XmlPart.d.mts +35 -32
  161. package/dist/parts/XmlPart.mjs +107 -107
  162. package/dist/parts/wml/comments.mjs +30 -0
  163. package/dist/parts/wml/index.mjs +12 -0
  164. package/dist/xml/dom.d.mts +7 -0
  165. package/dist/xml/dom.mjs +18 -0
  166. package/package.json +2 -2
@@ -4,24 +4,33 @@ import { registerPackageClass } from './registry.mjs';
4
4
  import { ContentTypes } from '../opc/ContentTypes.mjs';
5
5
  import { Docx4JException } from '../opc/exceptions.mjs';
6
6
  import { Namespaces } from '../parts/Namespaces.mjs';
7
- import { MainDocumentPart, StyleDefinitionsPart, DocumentSettingsPart } from '../parts/wml/index.mjs';
7
+ import { MainDocumentPart, StyleDefinitionsPart, DocumentSettingsPart, GlossaryDocumentPart } from '../parts/wml/index.mjs';
8
8
  import { DocPropsCorePart, DocPropsExtendedPart } from '../parts/docProps/index.mjs';
9
9
  import { DEFAULT_STYLES_XML } from '../parts/wml/defaultStyles.mjs';
10
10
  import { ThemePart } from '../parts/dml/index.mjs';
11
11
  import { newFontSettings, themeOfSettings, } from '../model/fonts/defaultTheme.mjs';
12
12
  import { HeaderPart, FooterPart } from '../parts/wml/index.mjs';
13
+ import { Body, resolveStory, trackedChangesOfStory } from '../model/content/Body.mjs';
14
+ import { pieceMatcher } from '../model/content/trackedChangeFilter.mjs';
13
15
  import { CustomXmlPartCollection } from '../model/customxml/CustomXmlPartCollection.mjs';
14
16
  import { DefaultXPathEngine } from '../model/customxml/xpath.mjs';
15
17
  import { ChangeTracker, highestAnnotationId } from '../model/content/tracking.mjs';
16
18
  import { NumberingFacade } from '../model/content/List.mjs';
17
19
  import { StylesFacade } from '../model/content/stylesFacade.mjs';
18
20
  import { flushPendingCommentRemovals } from '../model/content/Comment.mjs';
21
+ import { GlossaryEntries } from '../model/content/placeholders.mjs';
19
22
  import { PropertyResolver } from '../model/properties/PropertyResolver.mjs';
20
23
  import { PropertyResolverNotCreatedException } from '../opc/exceptions.mjs';
24
+ import { flushPendingNoteRemovals, notesGoing } from '../model/content/TrackedChange.mjs';
25
+ import { flushPendingRelationshipRemovals } from '../model/content/relationships.mjs';
21
26
  import { XmlPart } from '../parts/XmlPart.mjs';
22
27
  // Registers the comment parts with the content API (CR-002 phase G); no cycle: the model half
23
28
  // never imports a part, as packages/registry.mts does for the package classes.
24
29
  import '../parts/wml/comments.mjs';
30
+ // Registers the text model's outline reader with Body.outline (CR-012 D3), the same way: the content API
31
+ // can't import the text model, which imports it.
32
+ import '../model/text/outline.mjs';
33
+ import { Texts } from '../model/text/Texts.mjs';
25
34
  const MAIN_CONTENT_TYPES = [
26
35
  ContentTypes.WORDPROCESSINGML_DOCUMENT,
27
36
  ContentTypes.WORDPROCESSINGML_DOCUMENT_MACROENABLED,
@@ -88,6 +97,26 @@ export class WordprocessingMLPackage extends OpcPackage {
88
97
  * the package is saved (check 32; `TrackedChange`). An extension.
89
98
  */
90
99
  this.pendingCommentRemovals = new Set();
100
+ /**
101
+ * Footnotes and endnotes whose references went with content a tracked change took away (a rejected
102
+ * insertion, an accepted deletion, a row or cell, a move's other half): their entries go from the
103
+ * notes parts when the package is saved, each unless a reference to it still stands, as Word took
104
+ * them (CR-002 section 44; `TrackedChange`). An extension.
105
+ */
106
+ this.pendingNoteRemovals = { footnote: new Set(), endnote: new Set() };
107
+ /**
108
+ * Relationships, by the name of their part, that a picture removed outright referenced (tracking off,
109
+ * or a picture taken back from its author's own insertion): each goes when the package is saved if
110
+ * nothing then references it, and its part with it when nothing else targets that (CR-002 section
111
+ * 47). Settled on save, not at once, so a picture cut and pasted back keeps its image. An extension.
112
+ */
113
+ this.pendingRelationshipRemovals = new Map();
114
+ /**
115
+ * The glossary's placeholder entries: read privately when a body is first read (`getBody`, `Word.run`), and those a
116
+ * placeholder written since names, which go into the glossary when the package is saved, the glossary made where the
117
+ * file has none (CR-002 section 63, D8 and D9). An extension.
118
+ */
119
+ this.glossaryEntries = new GlossaryEntries();
91
120
  /**
92
121
  * The parts `seedAnnotationIds()` has read, with what it read: `true` for a part still holding its
93
122
  * source, whose bytes cannot change, and otherwise the bytes that were set, which stay the same
@@ -220,6 +249,10 @@ export class WordprocessingMLPackage extends OpcPackage {
220
249
  this.resolver = resolver;
221
250
  this.resolverPending = undefined;
222
251
  return resolver;
252
+ }, (e) => {
253
+ // not kept: a styles part put right (setXml) is read again on the next call
254
+ this.resolverPending = undefined;
255
+ throw e;
223
256
  }));
224
257
  return this.resolverPending;
225
258
  }
@@ -393,12 +426,99 @@ export class WordprocessingMLPackage extends OpcPackage {
393
426
  }
394
427
  /**
395
428
  * Office JS `document.getTrackedChanges()`: every tracked change in the main document part,
396
- * in document order (headers and footers have their own `Body`). The part must be
397
- * unmarshalled, as `body` needs it to be.
429
+ * in document order (headers and footers have their own `Body`; `getAllTrackedChanges` lists
430
+ * every story). The part must be unmarshalled, as `body` needs it to be.
398
431
  */
399
432
  getTrackedChanges() {
400
433
  return this.body.getTrackedChanges();
401
434
  }
435
+ /**
436
+ * Every tracked change in every story, in docx4j's order (its CR-043 `TrackedChanges.in`, CR-011):
437
+ * the main document, each footnote, each endnote, each comment, then the headers and footers in the
438
+ * order the sections reference them; a text box's after the story it is in. Each story's are its
439
+ * `Body.getTrackedChanges()`, grouped as Office JS groups them, so changes never group across two
440
+ * stories. `TrackedChange.part` says which part each is in.
441
+ *
442
+ * With a filter, the changes with a piece it matches, each whole (docx4j's `in(pkg, filter)`).
443
+ *
444
+ * A story part whose source holds no revision markup is not read, so it keeps its bytes; one that
445
+ * does is unmarshalled, since the views returned must be resolvable, and so is re-marshalled on save.
446
+ * An extension: Office JS lists one body at a time.
447
+ */
448
+ async getAllTrackedChanges(filter) {
449
+ const matches = filter === undefined ? undefined : pieceMatcher(filter);
450
+ const out = [];
451
+ for (const body of await this.revisedStories())
452
+ out.push(...trackedChangesOfStory(body));
453
+ return matches ? out.filter((change) => change.pieces.some(matches)) : out;
454
+ }
455
+ /**
456
+ * Accepts every tracked change in every story: the main document, the footnotes, the endnotes and
457
+ * the comments, the headers and footers, and the text boxes in each. Word's
458
+ * `Document.AcceptAllRevisions` did that on docx4j's CR-043 probe (Word 16.0.20326), and left no
459
+ * revision in any story; `Body.acceptAll` resolves its own story only, as Office JS's `body` does
460
+ * (CR-002 section 49). A story part with no revision markup is not read, so it keeps its bytes.
461
+ * Returns how many changes were listed, story by story. An extension, named after Word's verb.
462
+ *
463
+ * With a filter (CR-011), only the single changes it matches are accepted, as `Body.acceptAll`
464
+ * takes one, and the count is of the listed changes with a matching piece.
465
+ */
466
+ async acceptAllRevisions(filter) {
467
+ return this.resolveEveryStory('accept', filter);
468
+ }
469
+ /** Rejects every tracked change in every story, as `acceptAllRevisions` accepts them (Word's `Document.RejectAllRevisions`). */
470
+ async rejectAllRevisions(filter) {
471
+ return this.resolveEveryStory('reject', filter);
472
+ }
473
+ async resolveEveryStory(what, filter) {
474
+ if (filter !== undefined)
475
+ pieceMatcher(filter); // a filter misread throws before anything is read or resolved
476
+ let count = 0;
477
+ for (const body of await this.revisedStories())
478
+ count += resolveStory(body, what, filter);
479
+ return count;
480
+ }
481
+ /**
482
+ * The body of every story whose part holds revision markup, in docx4j's order (its CR-043
483
+ * `TrackedChanges.in`): the main document, each footnote, each endnote, each comment, then the
484
+ * headers and footers in section order (`inSectionOrder`). A footnote, an endnote or a comment is a
485
+ * body of its own over its content.
486
+ */
487
+ async revisedStories() {
488
+ var _a, _b;
489
+ const main = this.getMainDocumentPart();
490
+ const out = [];
491
+ if (await holdsRevisions(main))
492
+ out.push(await main.getBody());
493
+ // a note or comment a resolution has taken the reference or anchor of goes on save; it is no story now
494
+ const pending = this.pendingNoteRemovals;
495
+ const going = pending.footnote.size + pending.endnote.size > 0 ? notesGoing(await main.readContents(), pending) : pending;
496
+ for (const [part, kind] of [[main.footnotesPart, 'footnote'], [main.endnotesPart, 'endnote']]) {
497
+ if (!part || !(await holdsRevisions(part)))
498
+ continue;
499
+ const contents = await part.getContents();
500
+ for (const note of (_a = (kind === 'footnote' ? contents.footnote : contents.endnote)) !== null && _a !== void 0 ? _a : []) {
501
+ if ((note.type === undefined || note.type === 'normal') && going[kind].has(Number(note.id)))
502
+ continue;
503
+ out.push(new Body(part, note, `${kind}:${note.id}`, this));
504
+ }
505
+ }
506
+ const comments = main.commentsPart;
507
+ if (comments && (await holdsRevisions(comments))) {
508
+ for (const comment of (_b = (await comments.getContents()).comment) !== null && _b !== void 0 ? _b : []) {
509
+ if (this.pendingCommentRemovals.has(comment.id))
510
+ continue;
511
+ out.push(new Body(comments, comment, `comment:${comment.id}`, this));
512
+ }
513
+ }
514
+ const revised = [];
515
+ for (const part of [...main.headerParts, ...main.footerParts])
516
+ if (await holdsRevisions(part))
517
+ revised.push(part);
518
+ for (const part of revised.length > 1 ? await inSectionOrder(main, revised) : revised)
519
+ out.push(await part.getBody());
520
+ return out;
521
+ }
402
522
  /** The tracker every content-API mutation asks for; undefined while the mode is `Off`. */
403
523
  get changeTracker() {
404
524
  const mode = this.changeTrackingMode;
@@ -506,6 +626,13 @@ export class WordprocessingMLPackage extends OpcPackage {
506
626
  target.author = { ...this.author };
507
627
  for (const id of this.pendingCommentRemovals)
508
628
  target.pendingCommentRemovals.add(id);
629
+ for (const id of this.pendingNoteRemovals.footnote)
630
+ target.pendingNoteRemovals.footnote.add(id);
631
+ for (const id of this.pendingNoteRemovals.endnote)
632
+ target.pendingNoteRemovals.endnote.add(id);
633
+ for (const [name, ids] of this.pendingRelationshipRemovals)
634
+ target.pendingRelationshipRemovals.set(name, new Set(ids));
635
+ this.glossaryEntries.copyTo(target.glossaryEntries);
509
636
  target.trackedChangeDate = this.trackedChangeDate;
510
637
  target.fonts.defaultTheme = this.fonts.defaultTheme;
511
638
  target.xpathEngine = this.xpathEngine;
@@ -514,15 +641,35 @@ export class WordprocessingMLPackage extends OpcPackage {
514
641
  var _a;
515
642
  if (this.trackingPending)
516
643
  await this.setChangeTrackingMode((_a = this.trackingMode) !== null && _a !== void 0 ? _a : 'Off');
517
- if (this.pendingCommentRemovals.size > 0)
518
- await flushPendingCommentRemovals(await this.getBody());
644
+ // what removed content took with it, until nothing more goes: a removed note takes the comments
645
+ // anchored in it, and a removed note or comment the relationships its content held, which the last
646
+ // step settles (CR-002 section 50; docx4j's Removals.flush repeats the same way)
647
+ const notes = this.pendingNoteRemovals;
648
+ for (let round = 0; round < 10 && this.pendingCommentRemovals.size + notes.footnote.size + notes.endnote.size > 0; round++) {
649
+ if (this.pendingCommentRemovals.size > 0)
650
+ await flushPendingCommentRemovals(await this.getBody());
651
+ if (notes.footnote.size + notes.endnote.size > 0)
652
+ await flushPendingNoteRemovals(this.getMainDocumentPart(), notes, this);
653
+ }
654
+ if (this.pendingRelationshipRemovals.size > 0)
655
+ flushPendingRelationshipRemovals(this, this.pendingRelationshipRemovals);
656
+ if (this.glossaryEntries.waiting) {
657
+ await this.glossaryEntries.flush(this, {
658
+ glossary: () => new GlossaryDocumentPart(),
659
+ styles: () => new StyleDefinitionsPart('/word/glossary/styles.xml'),
660
+ });
661
+ }
519
662
  if (this.requiredStyles.size > 0) {
520
663
  await this.styles.ensure([...this.requiredStyles]);
521
664
  this.requiredStyles.clear();
522
665
  }
523
666
  return super.saveTo(sink);
524
667
  }
525
- /** The paragraph at an address anywhere in the document: 'body/3', 'header:rId5/0', a paraId, or a text match. */
668
+ /**
669
+ * The paragraph at an address anywhere in the document: 'body/3', 'header:rId5/0', 'footnote:2/0',
670
+ * 'endnote:1/0', 'comment:5/0', 'body/3/textbox:0/1' (a text box's paragraph, read-only: CR-012 D4 and
671
+ * D5), a paraId, or a text match. Undefined where the address names no paragraph.
672
+ */
526
673
  async paragraphAt(address) {
527
674
  for (const body of await this.bodies(address)) {
528
675
  const p = body.paragraphAt(address);
@@ -531,28 +678,44 @@ export class WordprocessingMLPackage extends OpcPackage {
531
678
  }
532
679
  return undefined;
533
680
  }
534
- /** Every paragraph and table with its address, style and text (CR-002 section 3.3). Unmarshals the main part, headers and footers. */
535
- async outline() {
681
+ /**
682
+ * Every paragraph and table with its address, style and text (CR-002 section 3.3), read by the text model
683
+ * under one context (CR-012 D3: `Body.outline`, whose options it takes): the main document, then each
684
+ * header and footer. Unmarshals the main part, headers and footers.
685
+ */
686
+ async outline(options, context) {
536
687
  var _a, _b, _c, _d;
537
688
  const main = await this.getBody();
538
- const out = { ...main.outline(), headers: [], footers: [] };
689
+ const ctx = context !== null && context !== void 0 ? context : await Texts.contextOf(this);
690
+ const out = { ...await main.outline(options, ctx), headers: [], footers: [] };
539
691
  for (const part of this.getMainDocumentPart().headerParts) {
540
692
  const body = await part.getBody();
541
- out.headers.push({ relId: (_b = (_a = part.sourceRelationship) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '', partName: part.partName.name, ...body.outline() });
693
+ out.headers.push({ relId: (_b = (_a = part.sourceRelationship) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '', partName: part.partName.name, ...await body.outline(options, ctx) });
542
694
  }
543
695
  for (const part of this.getMainDocumentPart().footerParts) {
544
696
  const body = await part.getBody();
545
- out.footers.push({ relId: (_d = (_c = part.sourceRelationship) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : '', partName: part.partName.name, ...body.outline() });
697
+ out.footers.push({ relId: (_d = (_c = part.sourceRelationship) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : '', partName: part.partName.name, ...await body.outline(options, ctx) });
546
698
  }
547
699
  return out;
548
700
  }
549
701
  async bodies(address) {
702
+ var _a;
550
703
  const main = this.getMainDocumentPart();
551
704
  if (typeof address === 'string' && !address.startsWith('w14:')) {
552
705
  const prefix = address.split('/')[0];
553
706
  if (prefix === 'body')
554
707
  return [await main.getBody()];
555
708
  const [kind, relId] = prefix.split(':');
709
+ if (kind === 'footnote' || kind === 'endnote' || kind === 'comment') {
710
+ // a note's or a comment's body, by its w:id, as the text model and Comment address them (CR-012 D4)
711
+ const part = kind === 'footnote' ? main.footnotesPart : kind === 'endnote' ? main.endnotesPart : main.commentsPart;
712
+ if (!part)
713
+ return [];
714
+ const contents = await part.getContents();
715
+ const entry = ((_a = contents[kind]) !== null && _a !== void 0 ? _a : []).find((e) => String(e.id) === relId);
716
+ await this.getPropertyResolver().catch(() => undefined);
717
+ return entry ? [new Body(part, entry, `${kind}:${relId}`, this)] : [];
718
+ }
556
719
  const parts = kind === 'header' ? main.headerParts : kind === 'footer' ? main.footerParts : [];
557
720
  const part = parts.find((p) => { var _a; return ((_a = p.sourceRelationship) === null || _a === void 0 ? void 0 : _a.id) === relId || p.partName.fileName === relId; });
558
721
  return part instanceof HeaderPart || part instanceof FooterPart ? [await part.getBody()] : [];
@@ -628,6 +791,74 @@ export class WordprocessingMLPackage extends OpcPackage {
628
791
  return 'Word.Document';
629
792
  }
630
793
  }
794
+ /**
795
+ * Headers and footers in docx4j's order (its CR-043 `Lister.headersAndFooters`): as the sections
796
+ * reference them, in document order, a part once, then any other in the order of the main part's
797
+ * relationships. The main part's tree is read privately, so a main part nobody has otherwise read keeps
798
+ * its bytes.
799
+ */
800
+ async function inSectionOrder(main, parts) {
801
+ var _a;
802
+ const rels = main.relationshipsPart;
803
+ if (!rels)
804
+ return parts;
805
+ const order = [];
806
+ const add = (part) => {
807
+ const known = parts.find((p) => p === part);
808
+ if (known && !order.includes(known))
809
+ order.push(known);
810
+ };
811
+ const body = (await main.readContents()).body;
812
+ for (const sectPr of sectionsOf(body))
813
+ for (const ref of (_a = sectPr.egHdrFtrReferences) !== null && _a !== void 0 ? _a : [])
814
+ add(rels.getPart(ref.id));
815
+ for (const rel of rels.list)
816
+ add(rels.getPart(rel));
817
+ for (const part of parts)
818
+ if (!order.includes(part))
819
+ order.push(part);
820
+ return order;
821
+ }
822
+ /**
823
+ * The body's `w:sectPr`s in document order, the paragraphs' first and the body's own last, as docx4j's
824
+ * walk finds them: not inside a run (where a text box's paragraphs are), DOM the model does not type
825
+ * left alone.
826
+ */
827
+ function sectionsOf(body) {
828
+ const out = [];
829
+ const visit = (o) => {
830
+ if (typeof o !== 'object' || o === null || typeof o.nodeType === 'number')
831
+ return;
832
+ if (Array.isArray(o)) {
833
+ for (const item of o)
834
+ visit(item);
835
+ return;
836
+ }
837
+ const typeName = o.TYPE_NAME;
838
+ if (typeName === 'org_docx4j_wml.SectPr') {
839
+ out.push(o);
840
+ return;
841
+ }
842
+ if (typeName === 'org_docx4j_wml.R')
843
+ return;
844
+ for (const key in o)
845
+ if (key !== 'PARENT')
846
+ visit(o[key]);
847
+ };
848
+ visit(body === null || body === void 0 ? void 0 : body.content);
849
+ if (body === null || body === void 0 ? void 0 : body.sectPr)
850
+ out.push(body.sectPr);
851
+ return out;
852
+ }
853
+ /** Revision markup, by local name in any prefix: what a story part holds when it has a tracked change to resolve. */
854
+ const REVISION_MARKUP = /<(?:[\w.-]+:)?(?:ins|del|moveFrom|moveTo|rPrChange|pPrChange|sectPrChange|tblPrChange|trPrChange|tcPrChange|tblGridChange|tblPrExChange|cellIns|cellDel|cellMerge|numberingChange|customXmlInsRangeStart|customXmlDelRangeStart)[\s/>]/;
855
+ /**
856
+ * True when a story part may hold a tracked change: always once it is unmarshalled, else when its
857
+ * source has revision markup, read without unmarshalling it, so a part with none keeps its bytes.
858
+ */
859
+ async function holdsRevisions(part) {
860
+ return part.isUnmarshalled || REVISION_MARKUP.test(await part.getXml());
861
+ }
631
862
  /**
632
863
  * `w:trackRevisions` is a flag, so a file cannot distinguish `TrackAll` from `TrackMineOnly`;
633
864
  * a document that has it on reads as `TrackAll`, and either value writes it.
@@ -2,7 +2,7 @@ import { Part } from './Part.mjs';
2
2
  import { ContentTypes } from '../opc/ContentTypes.mjs';
3
3
  import { Namespaces } from './Namespaces.mjs';
4
4
  import { Docx4JException } from '../opc/exceptions.mjs';
5
- import { parseXml, serializeXmlPart, encodeText, decodeXmlText } from '../xml/dom.mjs';
5
+ import { parseXml, serializeXmlPart, encodeText, decodeXmlText, assertXmlCharacters } from '../xml/dom.mjs';
6
6
  /**
7
7
  * An XML part the object model does not type, held as a DOM (docx4j DefaultXmlPart / XmlPart).
8
8
  * The document is parsed on first `getDocument()`; a part whose document was never requested is
@@ -71,7 +71,7 @@ export class DefaultXmlPart extends Part {
71
71
  }
72
72
  async getBytes() {
73
73
  if (this.document !== undefined)
74
- return encodeText(serializeXmlPart(this.document));
74
+ return encodeText(assertXmlCharacters(serializeXmlPart(this.document), `Part ${this.partName}`));
75
75
  if (this.bytes !== undefined)
76
76
  return this.bytes;
77
77
  const loaded = await this.loadSourceBytes();
@@ -1,7 +1,12 @@
1
1
  import type { Relationship } from '@docx4j/generated-objects-ts/modules/org_docx4j_relationships';
2
+ import type { Normalisation } from '@docx4j/generated-objects-ts';
2
3
  import { PartName } from '../opc/PartName.mjs';
3
4
  import type { RelationshipsPart, AddPartBehaviour } from './RelationshipsPart.mjs';
4
5
  import type { OpcPackage } from '../packages/OpcPackage.mjs';
6
+ /** What a save asks of a part's bytes (CR-015 D8): to be told of each value the objects facade's normaliser rewrote. */
7
+ export interface PartBytesOptions {
8
+ onNormalised?(normalisation: Normalisation): void;
9
+ }
5
10
  /** Something that owns a relationships part: a part or the package (docx4j Base). */
6
11
  export interface RelationshipSource {
7
12
  readonly partName: PartName;
@@ -49,8 +54,11 @@ export declare abstract class Part implements RelationshipSource {
49
54
  setPartShortcut(_part: Part, _relationshipType: string): boolean;
50
55
  /** The bytes this part was loaded from, if it came from a container. Undefined for new parts. */
51
56
  protected loadSourceBytes(): Promise<Uint8Array | undefined>;
52
- /** The bytes to write on save. Untouched parts come back from the source container unchanged. */
53
- abstract getBytes(): Promise<Uint8Array>;
57
+ /**
58
+ * The bytes to write on save. Untouched parts come back from the source container unchanged. A part
59
+ * marshalled through the objects facade tells `options.onNormalised` of each value written in its normal form.
60
+ */
61
+ abstract getBytes(options?: PartBytesOptions): Promise<Uint8Array>;
54
62
  /** Whether the part carries content of its own (set or unmarshalled) rather than relying on the source container. */
55
63
  abstract get isLoaded(): boolean;
56
64
  }
@@ -1,7 +1,8 @@
1
1
  import { type Jsonix } from '@docx4j/generated-objects-ts';
2
- import { Part } from './Part.mjs';
2
+ import { Part, type PartBytesOptions } from './Part.mjs';
3
3
  import { PartName } from '../opc/PartName.mjs';
4
4
  import { type Bound } from '../model/content/binder.mjs';
5
+ import type { PartWarning } from '../opc/mce/preprocessor.mjs';
5
6
  /** What `selectObjects` needs: the engine to evaluate with, and any prefixes beyond the defaults. */
6
7
  export interface SelectOptions {
7
8
  /** The engine; `pkg.selectObjects` and `Body.select` pass the package's. It is readied on the snapshot. */
@@ -17,8 +18,11 @@ export interface RootName {
17
18
  namespaceURI: string;
18
19
  localPart: string;
19
20
  }
20
- /** A hook run over the DOM before unmarshalling (the MCE preprocessor); set by the package on load. */
21
- export type DomPreprocessor = (doc: Document, part?: XmlPart<unknown>) => void;
21
+ /**
22
+ * A hook run over the DOM before unmarshalling (the MCE preprocessor); set by the package on load.
23
+ * What it returns becomes the part's `warnings`.
24
+ */
25
+ export type DomPreprocessor = (doc: Document, part?: XmlPart<unknown>) => void | readonly PartWarning[];
22
26
  /**
23
27
  * A part whose content is a typed element of the object model (docx4j JaxbXmlPart<E>). `T` is the
24
28
  * root element's value type (`Document`, `Styles`, ...). Contents are unmarshalled on first
@@ -32,12 +36,7 @@ export declare class XmlPart<T = unknown> extends Part {
32
36
  rootName: RootName | undefined;
33
37
  /** Runs over the DOM before unmarshalling; the package sets its MCE preprocessor here. */
34
38
  preprocessor: DomPreprocessor | undefined;
35
- /**
36
- * The namespace declarations the source root element carried, prefix to URI, recorded when the
37
- * part was parsed. They are what `mc:Ignorable` prefixes the object model does not bind are
38
- * re-declared from on marshal; see `declareIgnorablePrefixes`.
39
- */
40
- private sourceDeclarations;
39
+ private parseWarnings;
41
40
  constructor(partName: PartName | string, contentType: string, relationshipType: string, rootName?: RootName);
42
41
  /** True once contents were unmarshalled or set; the part is then re-marshalled on save. */
43
42
  get isUnmarshalled(): boolean;
@@ -48,6 +47,13 @@ export declare class XmlPart<T = unknown> extends Part {
48
47
  * which is what lets a reader remember it (CR-002 section 31).
49
48
  */
50
49
  get isUntouched(): boolean;
50
+ /**
51
+ * What the last parse of this part reported: an mc:AlternateContent whose branches' text differs
52
+ * (`mce.branches_differ`, CR-010 phase 1), and each attribute value its type refuses that was read as
53
+ * Word 365 reads it (`value.kept.exact`, `value.kept.invalid`: CR-015 D8). Empty until the part is
54
+ * parsed, by `getContents()` or a private read, and again once its contents are set.
55
+ */
56
+ get warnings(): readonly PartWarning[];
51
57
  /** The unmarshalled contents (docx4j getJaxbElement()); throws when not yet unmarshalled. */
52
58
  get contents(): T;
53
59
  /** The root element as [name, value]; undefined until unmarshalled. */
@@ -64,6 +70,7 @@ export declare class XmlPart<T = unknown> extends Part {
64
70
  * reads the styles and numbering parts of a document nobody has otherwise touched.
65
71
  */
66
72
  readContents(): Promise<T>;
73
+ private preprocess;
67
74
  /** Replaces the contents (docx4j setJaxbElement / setContents). The root name defaults to the part's. */
68
75
  setContents(value: T, rootName?: RootName): void;
69
76
  /** Replaces the contents with raw XML bytes; they are unmarshalled on the next getContents(). */
@@ -73,7 +80,7 @@ export declare class XmlPart<T = unknown> extends Part {
73
80
  /** The XML as it would be saved (docx4j XmlUtils.marshaltoString on the part). */
74
81
  getXml(): Promise<string>;
75
82
  /** Marshalled bytes when unmarshalled or set, else the source bytes untouched. */
76
- getBytes(): Promise<Uint8Array>;
83
+ getBytes(options?: PartBytesOptions): Promise<Uint8Array>;
77
84
  /**
78
85
  * The objects an XPath selects in this part, through a DOM marshalled for the purpose
79
86
  * (CR-006; docx4j `JaxbXmlPart.getJAXBNodesViaXPath`, which does it with a JAXB `Binder`).
@@ -92,32 +99,28 @@ export declare class XmlPart<T = unknown> extends Part {
92
99
  * (CR-002 section 31).
93
100
  */
94
101
  selectObjects(xpath: string, options?: SelectOptions): Promise<Bound[]>;
95
- /** The root element as a DOM element (docx4j XmlUtils.marshaltoW3CDomDocument). */
96
- marshalToNode(): Promise<Element>;
102
+ /**
103
+ * The root element as a DOM element (docx4j XmlUtils.marshaltoW3CDomDocument). Run properties are written in
104
+ * their normal form unless the package's `normaliseOnSave` (else the objects package's `setNormaliseOnSave`) is
105
+ * false (CR-015 D2, the objects facade's CR-013 phase C): the tree keeps what it read.
106
+ */
107
+ marshalToNode(options?: PartBytesOptions): Promise<Element>;
108
+ /**
109
+ * Parses the part's bytes, and names the part when the XML is not well-formed. xmldom's error
110
+ * (Node) carries the line and column of the fault; a browser's says them in its text, which
111
+ * `parseXml` passes on.
112
+ */
113
+ private parse;
97
114
  /**
98
115
  * Unmarshals, and names the part when the runtime refuses. The runtime's own message says what is
99
116
  * wrong and, since `@docx4j/jsonix` 3.4.0, where - `w:sz/@w:val (line 12): Argument [NaN] must be
100
117
  * an integer` - but not which part it was reading, which is the first thing a person editing a
101
- * part's XML needs (CR-002 section 27). The location it found is lifted onto the exception so a
102
- * caller does not parse the message; `line` is undefined where the parser supplies no position,
103
- * which is every browser (only xmldom carries a locator).
118
+ * part's XML needs (CR-002 section 27), nor the value it refused. The message names the part, and
119
+ * for an attribute gives the value as written and the column too:
120
+ * `/word/styles.xml: w:sz/@w:val="big" (line 12, column 9): Argument [NaN] must be an integer, ...`.
121
+ * The location it found is lifted onto the exception so a caller does not parse the message; `line`
122
+ * is undefined where the parser supplies no position, which is every browser (only xmldom carries a
123
+ * locator).
104
124
  */
105
125
  private unmarshal;
106
- /** The source root element's namespace declarations, prefix to URI (`xmlns=` is the empty prefix). */
107
- private recordDeclarations;
108
- /**
109
- * Every prefix the marshalled root's `mc:Ignorable` names must be declared on that root
110
- * (ECMA-376 Part 3 10.1.2; Word and Excel treat an undeclared one as a corrupt part and offer
111
- * to repair the file). The object model binds only the namespaces it has modules for, so an
112
- * `mc:Ignorable` prefix whose content was dropped on unmarshal - `xr6`, `xr10` and `xr2` on
113
- * Excel's `xl/workbook.xml`, for instance - loses its declaration: the runtime declares one
114
- * prefix per entry of the objects package's table and the facade can only *remove* unused
115
- * declarations, never add one it never made (CR-001 section 17).
116
- *
117
- * So the part re-declares them itself: from what the source root declared, else from the
118
- * objects package's `NAMESPACE_PREFIXES`. A prefix neither knows is dropped from
119
- * `mc:Ignorable` - an undeclared prefix is the one thing that must not be written - and warned
120
- * about once.
121
- */
122
- private declareIgnorablePrefixes;
123
126
  }