@docx4j/core-ts 0.6.0 → 0.7.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 (119) hide show
  1. package/README.md +60 -0
  2. package/dist/model/content/Body.mjs +6 -17
  3. package/dist/model/content/Comment.d.mts +2 -2
  4. package/dist/model/content/Comment.mjs +12 -7
  5. package/dist/model/content/ContentControl.d.mts +1 -1
  6. package/dist/model/content/ContentControl.mjs +8 -4
  7. package/dist/model/content/Font.d.mts +4 -1
  8. package/dist/model/content/Font.mjs +6 -2
  9. package/dist/model/content/List.d.mts +6 -3
  10. package/dist/model/content/List.mjs +15 -8
  11. package/dist/model/content/Paragraph.d.mts +12 -2
  12. package/dist/model/content/Paragraph.mjs +31 -11
  13. package/dist/model/content/Table.mjs +26 -16
  14. package/dist/model/content/TrackedChange.d.mts +2 -2
  15. package/dist/model/content/TrackedChange.mjs +6 -3
  16. package/dist/model/content/index.d.mts +3 -1
  17. package/dist/model/content/index.mjs +4 -1
  18. package/dist/model/content/ooxml.mjs +12 -1
  19. package/dist/model/content/paraIds.d.mts +5 -0
  20. package/dist/model/content/paraIds.mjs +54 -0
  21. package/dist/model/content/search.d.mts +2 -0
  22. package/dist/model/content/search.mjs +51 -7
  23. package/dist/model/content/story.d.mts +5 -2
  24. package/dist/model/content/story.mjs +6 -3
  25. package/dist/model/content/tableRules.mjs +2 -0
  26. package/dist/model/content/tracking.d.mts +35 -6
  27. package/dist/model/content/tracking.mjs +93 -12
  28. package/dist/model/content/tree.d.mts +7 -8
  29. package/dist/model/content/tree.mjs +7 -9
  30. package/dist/model/content/wordRange.d.mts +48 -0
  31. package/dist/model/content/wordRange.mjs +97 -0
  32. package/dist/model/customxml/CustomXmlPartCollection.d.mts +22 -1
  33. package/dist/model/customxml/CustomXmlPartCollection.mjs +48 -2
  34. package/dist/model/customxml/XmlMapping.d.mts +31 -7
  35. package/dist/model/customxml/XmlMapping.mjs +53 -10
  36. package/dist/model/customxml/bindings.d.mts +12 -2
  37. package/dist/model/customxml/bindings.mjs +98 -16
  38. package/dist/model/customxml/index.d.mts +1 -0
  39. package/dist/model/customxml/index.mjs +1 -0
  40. package/dist/model/customxml/opendope.d.mts +4 -1
  41. package/dist/model/customxml/opendope.mjs +9 -6
  42. package/dist/model/customxml/pictures.mjs +5 -3
  43. package/dist/model/customxml/storeItems.d.mts +57 -0
  44. package/dist/model/customxml/storeItems.mjs +122 -0
  45. package/dist/model/customxml/xhtml.mjs +15 -20
  46. package/dist/model/dateZone.d.mts +25 -0
  47. package/dist/model/dateZone.mjs +57 -0
  48. package/dist/model/index.d.mts +1 -0
  49. package/dist/model/index.mjs +1 -0
  50. package/dist/model/properties/PropertyResolver.d.mts +14 -1
  51. package/dist/model/properties/PropertyResolver.mjs +18 -3
  52. package/dist/model/properties/index.d.mts +1 -1
  53. package/dist/model/properties/log.mjs +2 -2
  54. package/dist/model/properties/styleUtil.d.mts +3 -1
  55. package/dist/model/properties/styleUtil.mjs +20 -24
  56. package/dist/model/text/CaseMapping.d.mts +38 -0
  57. package/dist/model/text/CaseMapping.mjs +86 -0
  58. package/dist/model/text/Extractor.d.mts +66 -6
  59. package/dist/model/text/Extractor.mjs +300 -56
  60. package/dist/model/text/PlainText.mjs +7 -4
  61. package/dist/model/text/TextContext.d.mts +103 -8
  62. package/dist/model/text/TextContext.mjs +228 -53
  63. package/dist/model/text/TextOptions.d.mts +88 -5
  64. package/dist/model/text/TextOptions.mjs +158 -17
  65. package/dist/model/text/TextResult.d.mts +126 -9
  66. package/dist/model/text/TextResult.mjs +115 -12
  67. package/dist/model/text/WmlToMarkdown.mjs +32 -7
  68. package/dist/model/text/caseMapping.generated.d.mts +7 -0
  69. package/dist/model/text/caseMapping.generated.mjs +214 -0
  70. package/dist/model/text/index.d.mts +3 -2
  71. package/dist/model/text/index.mjs +1 -0
  72. package/dist/model/wordIntegers.d.mts +29 -0
  73. package/dist/model/wordIntegers.mjs +78 -0
  74. package/dist/office-js/document.mjs +15 -1
  75. package/dist/office-js/toApiScript.mjs +8 -7
  76. package/dist/opc/DirectoryPartStore.d.mts +6 -2
  77. package/dist/opc/DirectoryPartStore.mjs +11 -4
  78. package/dist/opc/FlatOpcPartStore.d.mts +4 -2
  79. package/dist/opc/FlatOpcPartStore.mjs +11 -8
  80. package/dist/opc/Load.d.mts +32 -0
  81. package/dist/opc/Load.mjs +115 -5
  82. package/dist/opc/PartStore.d.mts +34 -1
  83. package/dist/opc/PartStore.mjs +71 -8
  84. package/dist/opc/Save.d.mts +17 -2
  85. package/dist/opc/Save.mjs +100 -4
  86. package/dist/opc/ZipPartStore.d.mts +4 -2
  87. package/dist/opc/ZipPartStore.mjs +12 -7
  88. package/dist/opc/exceptions.d.mts +140 -0
  89. package/dist/opc/exceptions.mjs +108 -0
  90. package/dist/opc/index.d.mts +7 -4
  91. package/dist/opc/index.mjs +5 -3
  92. package/dist/opc/mce/preprocessor.d.mts +121 -10
  93. package/dist/opc/mce/preprocessor.mjs +122 -21
  94. package/dist/opc/mce/understood.d.mts +3 -35
  95. package/dist/opc/mce/understood.mjs +27 -53
  96. package/dist/opc/repair/breaks.d.mts +23 -0
  97. package/dist/opc/repair/breaks.mjs +288 -0
  98. package/dist/opc/repair/hosts.d.mts +12 -0
  99. package/dist/opc/repair/hosts.mjs +33 -0
  100. package/dist/opc/repair/mainRoot.d.mts +19 -0
  101. package/dist/opc/repair/mainRoot.mjs +168 -0
  102. package/dist/opc/repair/unknownElements.d.mts +8 -0
  103. package/dist/opc/repair/unknownElements.mjs +180 -0
  104. package/dist/opc/repair/wmlNames.generated.d.mts +2 -0
  105. package/dist/opc/repair/wmlNames.generated.mjs +80 -0
  106. package/dist/opc/skipped.d.mts +50 -0
  107. package/dist/opc/skipped.mjs +140 -0
  108. package/dist/packages/OpcPackage.d.mts +98 -5
  109. package/dist/packages/OpcPackage.mjs +122 -7
  110. package/dist/packages/WordprocessingMLPackage.mjs +3 -1
  111. package/dist/packages/index.d.mts +1 -1
  112. package/dist/parts/Part.d.mts +5 -1
  113. package/dist/parts/RelationshipsPart.d.mts +15 -0
  114. package/dist/parts/RelationshipsPart.mjs +66 -1
  115. package/dist/parts/XmlPart.d.mts +54 -5
  116. package/dist/parts/XmlPart.mjs +203 -10
  117. package/dist/parts/wml/index.d.mts +12 -1
  118. package/dist/parts/wml/index.mjs +25 -1
  119. package/package.json +2 -2
@@ -1,3 +1,4 @@
1
+ import { CaseMapping } from './CaseMapping.mjs';
1
2
  /** The spaces for each 720-twip tab stop of indent under `layout: indent` (REQ-709). */
2
3
  export const INDENT_SPACES = 4;
3
4
  /** The note line before a table in the listed format (REQ-605, REQ-707). */
@@ -217,14 +218,16 @@ class Writer {
217
218
  this.out += `${author !== null && author !== void 0 ? author : ''}: `;
218
219
  break;
219
220
  case 'author_date':
220
- this.out += `${author !== null && author !== void 0 ? author : ''}, ${date !== null && date !== void 0 ? date : ''}: `;
221
- break;
221
+ this.out += `${author !== null && author !== void 0 ? author : ''}${date === undefined ? '' : `, ${date}`}: `;
222
+ break; // a mark with no date: its author alone (REQ-702)
222
223
  default:
223
224
  }
224
225
  }
225
226
  run(run) {
226
227
  switch (run.kind) {
227
- case 'TEXT':
228
+ case 'TEXT': // w:caps in capitals, by the run's language (REQ-607)
229
+ this.out += CaseMapping.written(run, this.options);
230
+ break;
228
231
  case 'FIELD_CODE':
229
232
  case 'LIST_LABEL':
230
233
  this.out += run.text;
@@ -263,7 +266,7 @@ class Writer {
263
266
  comment(comment) {
264
267
  this.out += '{>>';
265
268
  this.detail(comment.author, comment.date);
266
- this.out += `${comment.text}<<}`;
269
+ this.out += `${CaseMapping.written(comment, this.options)}<<}`;
267
270
  }
268
271
  /** A block written on its own, for a table cell's value. */
269
272
  text(block) {
@@ -1,13 +1,15 @@
1
1
  import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
2
2
  import type * as w15 from '@docx4j/generated-objects-ts/modules/org_docx4j_w15';
3
+ import type * as w16cid from '@docx4j/generated-objects-ts/modules/org_docx4j_w16cid';
4
+ import type * as w16cex from '@docx4j/generated-objects-ts/modules/org_docx4j_w16cex';
3
5
  import type * as core from '@docx4j/generated-objects-ts/modules/org_docx4j_docProps_core';
4
6
  import type * as extended from '@docx4j/generated-objects-ts/modules/org_docx4j_docProps_extended';
5
7
  import type * as custom from '@docx4j/generated-objects-ts/modules/org_docx4j_docProps_custom';
6
8
  import type { Part } from '../../parts/Part.mjs';
7
9
  import type { XmlPart } from '../../parts/XmlPart.mjs';
8
- import type { PropertyResolver } from '../properties/PropertyResolver.mjs';
9
- import type { NumberingDefinitions } from '../listnumbering/definitions.mjs';
10
- import type { NumberingPartLike } from '../listnumbering/Emulator.mjs';
10
+ import { PropertyResolver } from '../properties/PropertyResolver.mjs';
11
+ import { NumberingDefinitions } from '../listnumbering/definitions.mjs';
12
+ import { Emulator, type NumberingPartLike } from '../listnumbering/Emulator.mjs';
11
13
  import { NumberingStates } from '../listnumbering/state.mjs';
12
14
  import { Body } from '../content/Body.mjs';
13
15
  import { ResolvedView } from '../content/ResolvedView.mjs';
@@ -24,8 +26,33 @@ export interface TextPackage {
24
26
  readonly docPropsCustomPart?: XmlPart<custom.Properties>;
25
27
  readonly customXmlDataStorageParts: ReadonlyMap<string, {
26
28
  parseDocument(): Promise<Document>;
29
+ readonly partName: {
30
+ readonly name: string;
31
+ };
27
32
  }>;
28
33
  xpathEngine: XPathEngine;
34
+ /** The names the container held twice, read from the first under `repair: 'word'` (CR-024). */
35
+ readonly duplicateEntries?: readonly {
36
+ readonly entry: string;
37
+ readonly count: number;
38
+ }[];
39
+ /** More than one officeDocument relationship, the last's target read under `repair: 'word'` (CR-024 section 9). */
40
+ readonly multipleMainParts?: {
41
+ readonly partName: string;
42
+ readonly count: number;
43
+ } | undefined;
44
+ /** An `External` officeDocument relationship beside an internal one, dropped under `repair: 'all'` (CR-031). */
45
+ readonly externalMainPart?: {
46
+ readonly partName: string;
47
+ readonly urls: readonly string[];
48
+ } | undefined;
49
+ /** The relationships parts read by local name under `repair: 'word'` (CR-031 section 2.3). */
50
+ readonly relationshipsNamespaces?: readonly {
51
+ readonly partName: string;
52
+ readonly count: number;
53
+ }[];
54
+ /** What the load repaired (REQ-207's `repaired`, CR-031). */
55
+ readonly repaired?: 'none' | 'word' | 'own';
29
56
  }
30
57
  /** As much of a `MainDocumentPart` as a reading uses. */
31
58
  export interface MainLike extends XmlPart<wml.Document> {
@@ -38,10 +65,17 @@ export interface MainLike extends XmlPart<wml.Document> {
38
65
  readonly endnotesPart?: XmlPart<wml.CTEndnotes>;
39
66
  readonly commentsPart?: XmlPart<wml.Comments>;
40
67
  readonly commentsExtendedPart?: XmlPart<w15.CTCommentsEx>;
68
+ readonly commentsIdsPart?: XmlPart<w16cid.CTCommentsIds>;
69
+ readonly commentsExtensiblePart?: XmlPart<w16cex.CTCommentsExtensible>;
41
70
  readonly glossaryDocumentPart?: XmlPart<wml.GlossaryDocument>;
42
71
  readonly headerParts: XmlPart<wml.Hdr>[];
43
72
  readonly footerParts: XmlPart<wml.Ftr>[];
44
73
  }
74
+ /**
75
+ * A part a reading may need that it could not read (CR-012 section 28, WD REQ-801): its role, by what it supplies. Each is
76
+ * raised as `part.not_read`, naming the part, where the reading needs it.
77
+ */
78
+ export type UnreadPart = 'styles' | 'numbering' | 'settings' | 'core' | 'app' | 'custom';
45
79
  /**
46
80
  * The trees one reading walks, each read **privately** where its part was not unmarshalled
47
81
  * (`XmlPart.readContents`), so that reading a document costs no part its byte-for-byte round trip
@@ -67,37 +101,98 @@ export declare class PackageTrees {
67
101
  tree: wml.Comments;
68
102
  } | undefined;
69
103
  commentsExtended: w15.CTCommentsEx | undefined;
104
+ /**
105
+ * Each comment's `w16cex:dateUtc`, by its last paragraph's `w14:paraId` (upper case): the comments ids part gives the
106
+ * paragraph's durable id, the comments extensible part the durable id's date (REQ-702's offset; docx4j's `commentDatesUtc`).
107
+ */
108
+ readonly commentDatesUtc: Map<string, wml.XmlCalendar>;
70
109
  /** Each header and footer part related from the main document part, with its content. */
71
110
  readonly headersAndFooters: Map<Part, wml.Ftr | wml.Hdr>;
72
111
  glossary: {
73
112
  part: Part;
74
113
  tree: wml.GlossaryDocument;
75
114
  } | undefined;
115
+ /** Each story part that could not be read, with the reason: a reading taking it raises `content.not_read` (REQ-801). */
116
+ readonly unreadable: Map<Part, unknown>;
76
117
  settings: wml.CTSettings | undefined;
77
118
  /** The document properties under `core`, `app` and `custom` (REQ-515). */
78
119
  properties: Record<string, unknown> | undefined;
79
- /** The resolver, where the package has a styles part (docx4j makes none without one). */
120
+ /** The resolver, where the package has a styles part that can be read (docx4j makes none without one). */
80
121
  resolver: PropertyResolver | undefined;
81
- numberingPart: NumberingPartLike | undefined;
122
+ /**
123
+ * What the numbering resolves a style's `w:numPr` through: the package's resolver, else, where a styles or numbering part
124
+ * cannot be read, the reading's own without it (CR-012 section 28); undefined where the package has neither part.
125
+ */
126
+ private numberingResolver;
82
127
  numbering: NumberingDefinitions | undefined;
83
- /** Each binding store's DOM, by its upper-cased item id. */
128
+ /**
129
+ * The numbering this reading counts by: the numbering part's emulator, one of the reading's own where the styles cannot
130
+ * be read, or, where the numbering part is absent or cannot be read, one over Word's default definition, made at the
131
+ * first numbered paragraph (`numberingFor`).
132
+ */
133
+ emulator: Emulator | undefined;
134
+ /** Where the numbering part is absent or cannot be read: the warning the first numbered paragraph raises, and the part named. */
135
+ numberingMissing: {
136
+ kind: 'part.absent' | 'part.not_read';
137
+ partName: string;
138
+ } | undefined;
139
+ /** The parts this reading could not read that it may need, by role, each with its part's name (CR-012 section 28). */
140
+ readonly notRead: Map<UnreadPart, string>;
141
+ /** The custom XML stores and properties parts a binding could not be read against, by normalised store item id: their part names. */
142
+ private readonly unreadStores;
143
+ /** The core and application properties' DOMs, by their normalised store item ids. */
84
144
  private readonly stores;
145
+ /** The custom XML parts' DOMs, in the package's order: what a binding resolves to (CR-020). */
146
+ private readonly customStores;
147
+ private readonly storeItems;
85
148
  private xpath;
86
149
  private anchored;
87
150
  private constructor();
88
151
  /** The trees of a package. */
89
152
  static read(pkg: TextPackage): Promise<PackageTrees>;
153
+ /**
154
+ * The resolver and the numbering (CR-012 section 28, WD REQ-801). The package's own where they can be built; where a
155
+ * styles or numbering part cannot be read, a resolver of the reading's own without it, as for a package that lacks the
156
+ * part, the part kept in `notRead`. The package's resolver is never set from it, so the content API still refuses the
157
+ * part (CR-001 section 26), and a part put right is read again by the next reading. Where the styles cannot be read the
158
+ * numbering's definitions are read directly and counted by an emulator of the reading's own, so that nothing is kept on
159
+ * the numbering part. A numbering part absent or unread is Word's default definition (`numberingFor`, WD 0.76).
160
+ */
161
+ private readStylesAndNumbering;
162
+ /** `commentDatesUtc`, from the comments ids and extensible parts, read privately; neither read, as D3 (a) leaves them, nothing. */
163
+ private readCommentDates;
164
+ /** `readContents()`, undefined for a part that cannot be read, which is then kept in `notRead` under its role. */
165
+ private readOrNote;
166
+ /**
167
+ * The numbering a numbered paragraph is counted by. Where the numbering part is absent or cannot be read, Word's default
168
+ * definition (WD 0.76, REQ-510), made here once, at the first numbered paragraph, and never written to the package: one
169
+ * abstract definition of nine decimal levels, each `w:start` 1 and `w:lvlText` `%{level+1}.`, and one instance per
170
+ * `w:numId` other than 0 the document names, plain where a paragraph style's `w:numPr` names it, else with a
171
+ * `w:startOverride` of 1 at every level, so that the direct paragraphs restart at their first and then share the style's
172
+ * count.
173
+ */
174
+ numberingFor(): Emulator | undefined;
90
175
  /** Trees with no package: content read as it stands (docx4j's planless `Texts.of(content)`). */
91
176
  static none(): PackageTrees;
92
177
  /** The glossary document, read when `include: glossary` first asks for it. */
93
178
  readGlossary(): Promise<void>;
179
+ /** A story part's content; undefined where it has none, and where it could not be read, then kept in `unreadable`. */
180
+ private readStory;
94
181
  private readStores;
95
182
  /**
96
183
  * A data binding's value, docx4j `BoundValues`: the binding's XPath, with its own prefix mappings,
97
- * against the custom XML part its `w:storeItemID` names (or the core or application properties); the
98
- * string value of the first node selected. Undefined where the binding does not resolve.
184
+ * against the custom XML part its `w:storeItemID` names (or the core or application properties), or,
185
+ * where it names none, the part its XPath fits, as Word repairs it (CR-020; nothing is written back);
186
+ * the string value of the first node selected. Undefined where the binding does not resolve.
99
187
  */
100
188
  boundValue(binding: wml.CTDataBinding): string | undefined;
189
+ /**
190
+ * The name of the part a binding names that could not be read: a custom XML store, or the core or application
191
+ * properties (CR-012 section 28, D3); undefined where it names a part that was read, or none.
192
+ */
193
+ unreadStoreOf(binding: wml.CTDataBinding): string | undefined;
194
+ /** The nodes an XPath selects in a store, with a binding's prefix mappings (the default namespace's left out). */
195
+ private selectIn;
101
196
  /**
102
197
  * The ids of the comments anchored in the document: a `w:commentReference` or a `w:commentRangeStart`
103
198
  * in the main document, a header or footer, or a note, in any branch.
@@ -1,12 +1,16 @@
1
+ import * as f from '@docx4j/generated-objects-ts/factory/org_docx4j_wml';
2
+ import { PropertyResolver } from '../properties/PropertyResolver.mjs';
3
+ import { NumberingDefinitions } from '../listnumbering/definitions.mjs';
4
+ import { Emulator } from '../listnumbering/Emulator.mjs';
1
5
  import { NumberingStates } from '../listnumbering/state.mjs';
2
6
  import { Body } from '../content/Body.mjs';
3
7
  import { ResolvedView } from '../content/ResolvedView.mjs';
4
8
  import { parsePrefixMappings } from '../customxml/xpath.mjs';
9
+ import { CORE_PROPERTIES_STOREITEMID, EXTENDED_PROPERTIES_STOREITEMID, StoreItemResolver, isPropertiesStoreItem, normaliseStoreItemId } from '../customxml/storeItems.mjs';
5
10
  import { parseXml, decodeXmlText } from '../../xml/dom.mjs';
6
11
  import { walk } from '@docx4j/generated-objects-ts/builders/wml';
7
- /** The `w:storeItemID`s of the core and application properties, which a control can be bound to (docx4j `BindingHandler`). */
8
- const CORE_PROPERTIES_STOREITEMID = '{6C3C8BC8-F283-45AE-878A-BAB7291924A1}';
9
- const EXTENDED_PROPERTIES_STOREITEMID = '{6668398D-A668-4E3E-A5EB-62B293D839F1}';
12
+ /** Where a package keeps its numbering part: what `part.absent` names where it has none (WD 0.76). */
13
+ const NUMBERING_PART_NAME = '/word/numbering.xml';
10
14
  /** `readContents()`, undefined for a part that cannot be read. */
11
15
  async function readQuietly(part) {
12
16
  if (!part)
@@ -27,10 +31,24 @@ async function readQuietly(part) {
27
31
  export class PackageTrees {
28
32
  constructor(pkg) {
29
33
  this.pkg = pkg;
34
+ /**
35
+ * Each comment's `w16cex:dateUtc`, by its last paragraph's `w14:paraId` (upper case): the comments ids part gives the
36
+ * paragraph's durable id, the comments extensible part the durable id's date (REQ-702's offset; docx4j's `commentDatesUtc`).
37
+ */
38
+ this.commentDatesUtc = new Map();
30
39
  /** Each header and footer part related from the main document part, with its content. */
31
40
  this.headersAndFooters = new Map();
32
- /** Each binding store's DOM, by its upper-cased item id. */
41
+ /** Each story part that could not be read, with the reason: a reading taking it raises `content.not_read` (REQ-801). */
42
+ this.unreadable = new Map();
43
+ /** The parts this reading could not read that it may need, by role, each with its part's name (CR-012 section 28). */
44
+ this.notRead = new Map();
45
+ /** The custom XML stores and properties parts a binding could not be read against, by normalised store item id: their part names. */
46
+ this.unreadStores = new Map();
47
+ /** The core and application properties' DOMs, by their normalised store item ids. */
33
48
  this.stores = new Map();
49
+ /** The custom XML parts' DOMs, in the package's order: what a binding resolves to (CR-020). */
50
+ this.customStores = [];
51
+ this.storeItems = new StoreItemResolver(() => this.customStores);
34
52
  }
35
53
  /** The trees of a package. */
36
54
  static async read(pkg) {
@@ -44,46 +62,174 @@ export class PackageTrees {
44
62
  }
45
63
  if (main) {
46
64
  trees.main = { part: main, document: await main.readContents() };
47
- if (main.styleDefinitionsPart) {
48
- try {
49
- trees.resolver = await pkg.getPropertyResolver();
50
- }
51
- catch {
52
- trees.resolver = undefined; // hidden text and indents not resolved through styles
53
- }
54
- }
55
- const numbering = main.numberingDefinitionsPart;
56
- if (numbering) {
57
- try {
58
- await pkg.getNumberingEmulator();
59
- trees.numbering = await numbering.getDefinitions();
60
- trees.numberingPart = numbering;
61
- }
62
- catch {
63
- trees.numberingPart = undefined;
64
- }
65
- }
66
- trees.settings = await readQuietly(main.documentSettingsPart);
67
- const footnotes = await readQuietly(main.footnotesPart);
65
+ await trees.readStylesAndNumbering(pkg, main);
66
+ trees.settings = await trees.readOrNote(main.documentSettingsPart, 'settings');
67
+ const footnotes = await trees.readStory(main.footnotesPart);
68
68
  if (footnotes && main.footnotesPart)
69
69
  trees.footnotes = { part: main.footnotesPart, tree: footnotes };
70
- const endnotes = await readQuietly(main.endnotesPart);
70
+ const endnotes = await trees.readStory(main.endnotesPart);
71
71
  if (endnotes && main.endnotesPart)
72
72
  trees.endnotes = { part: main.endnotesPart, tree: endnotes };
73
- const comments = await readQuietly(main.commentsPart);
73
+ const comments = await trees.readStory(main.commentsPart);
74
74
  if (comments && main.commentsPart)
75
75
  trees.comments = { part: main.commentsPart, tree: comments };
76
- trees.commentsExtended = await readQuietly(main.commentsExtendedPart);
76
+ trees.commentsExtended = await readQuietly(main.commentsExtendedPart); // passed over silently: CR-012 section 28's D3 (a), not ruled
77
+ await trees.readCommentDates(main);
77
78
  for (const part of [...main.headerParts, ...main.footerParts]) {
78
- const content = await readQuietly(part);
79
+ const content = await trees.readStory(part);
79
80
  if (content)
80
81
  trees.headersAndFooters.set(part, content);
81
82
  }
82
83
  }
83
- trees.properties = await properties(pkg);
84
+ trees.properties = await properties(pkg, (part, role) => trees.readOrNote(part, role));
84
85
  await trees.readStores(pkg);
85
86
  return trees;
86
87
  }
88
+ /**
89
+ * The resolver and the numbering (CR-012 section 28, WD REQ-801). The package's own where they can be built; where a
90
+ * styles or numbering part cannot be read, a resolver of the reading's own without it, as for a package that lacks the
91
+ * part, the part kept in `notRead`. The package's resolver is never set from it, so the content API still refuses the
92
+ * part (CR-001 section 26), and a part put right is read again by the next reading. Where the styles cannot be read the
93
+ * numbering's definitions are read directly and counted by an emulator of the reading's own, so that nothing is kept on
94
+ * the numbering part. A numbering part absent or unread is Word's default definition (`numberingFor`, WD 0.76).
95
+ */
96
+ async readStylesAndNumbering(pkg, main) {
97
+ const stylesPart = main.styleDefinitionsPart;
98
+ const numberingPart = main.numberingDefinitionsPart;
99
+ if (stylesPart || numberingPart) {
100
+ let packages = true;
101
+ try {
102
+ this.numberingResolver = await pkg.getPropertyResolver();
103
+ }
104
+ catch {
105
+ packages = false;
106
+ try {
107
+ this.numberingResolver = await PropertyResolver.create(pkg, {
108
+ onUnreadable: (role) => {
109
+ const part = role === 'styles' ? stylesPart : numberingPart;
110
+ if (part)
111
+ this.notRead.set(role, part.partName.name);
112
+ },
113
+ });
114
+ }
115
+ catch {
116
+ this.numberingResolver = undefined; // hidden text and indents not resolved through styles
117
+ }
118
+ }
119
+ if (stylesPart && !this.notRead.has('styles'))
120
+ this.resolver = this.numberingResolver;
121
+ if (numberingPart && !this.notRead.has('numbering')) {
122
+ try {
123
+ if (packages) {
124
+ await pkg.getNumberingEmulator();
125
+ this.numbering = await numberingPart.getDefinitions();
126
+ this.emulator = numberingPart.getEmulator();
127
+ }
128
+ else {
129
+ const resolver = this.numberingResolver;
130
+ this.numbering = new NumberingDefinitions(await numberingPart.readContents(), (id) => resolver === null || resolver === void 0 ? void 0 : resolver.getStyle(id));
131
+ this.emulator = new Emulator(this.numbering, resolver);
132
+ }
133
+ }
134
+ catch {
135
+ this.numbering = undefined;
136
+ this.emulator = undefined;
137
+ this.notRead.set('numbering', numberingPart.partName.name);
138
+ }
139
+ }
140
+ }
141
+ if (this.emulator === undefined) {
142
+ this.numberingMissing = numberingPart
143
+ ? { kind: 'part.not_read', partName: numberingPart.partName.name }
144
+ : { kind: 'part.absent', partName: NUMBERING_PART_NAME };
145
+ }
146
+ }
147
+ /** `commentDatesUtc`, from the comments ids and extensible parts, read privately; neither read, as D3 (a) leaves them, nothing. */
148
+ async readCommentDates(main) {
149
+ var _a, _b;
150
+ const ids = await readQuietly(main.commentsIdsPart);
151
+ const extensible = await readQuietly(main.commentsExtensiblePart);
152
+ if (!ids || !extensible)
153
+ return;
154
+ const byDurableId = new Map();
155
+ for (const ce of (_a = extensible.commentExtensible) !== null && _a !== void 0 ? _a : []) {
156
+ if (ce.durableId && ce.dateUtc)
157
+ byDurableId.set(ce.durableId.toUpperCase(), ce.dateUtc);
158
+ }
159
+ for (const ci of (_b = ids.commentId) !== null && _b !== void 0 ? _b : []) {
160
+ if (!ci.paraId || !ci.durableId)
161
+ continue;
162
+ const utc = byDurableId.get(ci.durableId.toUpperCase());
163
+ if (utc)
164
+ this.commentDatesUtc.set(ci.paraId.toUpperCase(), utc);
165
+ }
166
+ }
167
+ /** `readContents()`, undefined for a part that cannot be read, which is then kept in `notRead` under its role. */
168
+ async readOrNote(part, role) {
169
+ if (!part)
170
+ return undefined;
171
+ try {
172
+ return await part.readContents();
173
+ }
174
+ catch {
175
+ this.notRead.set(role, part.partName.name);
176
+ return undefined;
177
+ }
178
+ }
179
+ /**
180
+ * The numbering a numbered paragraph is counted by. Where the numbering part is absent or cannot be read, Word's default
181
+ * definition (WD 0.76, REQ-510), made here once, at the first numbered paragraph, and never written to the package: one
182
+ * abstract definition of nine decimal levels, each `w:start` 1 and `w:lvlText` `%{level+1}.`, and one instance per
183
+ * `w:numId` other than 0 the document names, plain where a paragraph style's `w:numPr` names it, else with a
184
+ * `w:startOverride` of 1 at every level, so that the direct paragraphs restart at their first and then share the style's
185
+ * count.
186
+ */
187
+ numberingFor() {
188
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
189
+ if (this.emulator !== undefined || this.numberingMissing === undefined)
190
+ return this.emulator;
191
+ const resolver = this.numberingResolver;
192
+ const plain = new Set();
193
+ for (const style of (_a = resolver === null || resolver === void 0 ? void 0 : resolver.getStylesInOrder()) !== null && _a !== void 0 ? _a : []) {
194
+ const numId = Number(style.type === 'paragraph' ? (_d = (_c = (_b = style.pPr) === null || _b === void 0 ? void 0 : _b.numPr) === null || _c === void 0 ? void 0 : _c.numId) === null || _d === void 0 ? void 0 : _d.val : undefined);
195
+ if (Number.isInteger(numId) && numId !== 0)
196
+ plain.add(numId);
197
+ }
198
+ const direct = new Set();
199
+ const roots = [(_e = this.main) === null || _e === void 0 ? void 0 : _e.document, ...this.headersAndFooters.values(), (_f = this.footnotes) === null || _f === void 0 ? void 0 : _f.tree, (_g = this.endnotes) === null || _g === void 0 ? void 0 : _g.tree, (_h = this.comments) === null || _h === void 0 ? void 0 : _h.tree, (_j = this.glossary) === null || _j === void 0 ? void 0 : _j.tree];
200
+ for (const root of roots) {
201
+ if (root === undefined)
202
+ continue;
203
+ walk(root, (v) => {
204
+ var _a;
205
+ if (v.TYPE_NAME !== 'org_docx4j_wml.PPrBase.NumPr')
206
+ return;
207
+ const numId = Number((_a = v.numId) === null || _a === void 0 ? void 0 : _a.val);
208
+ if (Number.isInteger(numId) && numId !== 0 && !plain.has(numId))
209
+ direct.add(numId);
210
+ });
211
+ }
212
+ const levels = [0, 1, 2, 3, 4, 5, 6, 7, 8];
213
+ const num = (numId, restart) => f.createNumberingNum({
214
+ numId,
215
+ abstractNumId: f.createNumberingNumAbstractNumId({ val: 0 }),
216
+ lvlOverride: restart
217
+ ? levels.map((ilvl) => f.createNumberingNumLvlOverride({ ilvl, startOverride: f.createNumberingNumLvlOverrideStartOverride({ val: 1 }) }))
218
+ : [],
219
+ });
220
+ const tree = f.createNumbering({
221
+ abstractNum: [f.createNumberingAbstractNum({
222
+ abstractNumId: 0,
223
+ lvl: levels.map((ilvl) => f.createLvl({
224
+ ilvl, start: f.createLvlStart({ val: 1 }), numFmt: f.createNumFmt({ val: 'decimal' }), lvlText: f.createLvlLvlText({ val: `%${ilvl + 1}.` }),
225
+ })),
226
+ })],
227
+ num: [...[...plain].map((n) => num(n, false)), ...[...direct].map((n) => num(n, true))],
228
+ });
229
+ this.numbering = new NumberingDefinitions(tree, (id) => resolver === null || resolver === void 0 ? void 0 : resolver.getStyle(id));
230
+ this.emulator = new Emulator(this.numbering, resolver);
231
+ return this.emulator;
232
+ }
87
233
  /** Trees with no package: content read as it stands (docx4j's planless `Texts.of(content)`). */
88
234
  static none() {
89
235
  return new PackageTrees(undefined);
@@ -94,22 +240,38 @@ export class PackageTrees {
94
240
  const part = (_a = this.main) === null || _a === void 0 ? void 0 : _a.part.glossaryDocumentPart;
95
241
  if (this.glossary || !part)
96
242
  return;
97
- const tree = await readQuietly(part);
243
+ const tree = await this.readStory(part);
98
244
  if (tree)
99
245
  this.glossary = { part, tree };
100
246
  }
247
+ /** A story part's content; undefined where it has none, and where it could not be read, then kept in `unreadable`. */
248
+ async readStory(part) {
249
+ if (!part)
250
+ return undefined;
251
+ try {
252
+ return await part.readContents();
253
+ }
254
+ catch (e) {
255
+ this.unreadable.set(part, e);
256
+ return undefined;
257
+ }
258
+ }
101
259
  // --- bound values (REQ-509) ---------------------------------------------------------------------
102
260
  async readStores(pkg) {
103
261
  let context;
104
262
  for (const [id, part] of pkg.customXmlDataStorageParts) {
263
+ let doc;
105
264
  try {
106
- const doc = await part.parseDocument();
107
- this.stores.set(id.toUpperCase(), doc);
265
+ doc = await part.parseDocument();
108
266
  context !== null && context !== void 0 ? context : (context = doc);
109
267
  }
110
268
  catch {
111
- this.stores.set(id.toUpperCase(), undefined);
269
+ // read as absent (CR-012 section 28, D3; WD 0.69): no candidate for a binding, which REQ-509's repair resolves to the
270
+ // store its XPath fits, else its surface; its id kept, for the part.not_read a binding naming it raises
271
+ this.unreadStores.set(normaliseStoreItemId(id), part.partName.name);
272
+ continue;
112
273
  }
274
+ this.customStores.push({ part: doc, id, partName: part.partName.name, root: doc.documentElement, selects: (xpath, mappings) => this.selectIn(doc, xpath, mappings).length > 0 });
113
275
  }
114
276
  for (const [id, part] of [[CORE_PROPERTIES_STOREITEMID, pkg.docPropsCorePart], [EXTENDED_PROPERTIES_STOREITEMID, pkg.docPropsExtendedPart]]) {
115
277
  if (!part)
@@ -117,11 +279,12 @@ export class PackageTrees {
117
279
  try {
118
280
  // the part's XML as it would be saved, parsed apart from it: the part keeps its round trip
119
281
  const doc = parseXml(decodeXmlText(await part.getBytes()));
120
- this.stores.set(id, doc);
282
+ this.stores.set(normaliseStoreItemId(id), doc);
121
283
  context !== null && context !== void 0 ? context : (context = doc);
122
284
  }
123
285
  catch {
124
- this.stores.set(id, undefined);
286
+ this.stores.set(normaliseStoreItemId(id), undefined);
287
+ this.unreadStores.set(normaliseStoreItemId(id), part.partName.name);
125
288
  }
126
289
  }
127
290
  if (context === undefined)
@@ -136,29 +299,42 @@ export class PackageTrees {
136
299
  }
137
300
  /**
138
301
  * A data binding's value, docx4j `BoundValues`: the binding's XPath, with its own prefix mappings,
139
- * against the custom XML part its `w:storeItemID` names (or the core or application properties); the
140
- * string value of the first node selected. Undefined where the binding does not resolve.
302
+ * against the custom XML part its `w:storeItemID` names (or the core or application properties), or,
303
+ * where it names none, the part its XPath fits, as Word repairs it (CR-020; nothing is written back);
304
+ * the string value of the first node selected. Undefined where the binding does not resolve.
141
305
  */
142
306
  boundValue(binding) {
143
- var _a;
307
+ var _a, _b;
144
308
  if (!binding.xpath || !binding.storeItemID || !this.xpath)
145
309
  return undefined;
146
- const doc = this.stores.get(binding.storeItemID.toUpperCase());
147
- if (!doc)
148
- return undefined;
310
+ const doc = isPropertiesStoreItem(binding.storeItemID)
311
+ ? this.stores.get(normaliseStoreItemId(binding.storeItemID))
312
+ : (_a = this.storeItems.resolve(binding.storeItemID, binding.xpath, binding.prefixMappings)) === null || _a === void 0 ? void 0 : _a.part;
149
313
  try {
150
- const mappings = parsePrefixMappings(binding.prefixMappings);
151
- delete mappings[''];
152
- const nodes = this.xpath.select(binding.xpath, doc, mappings);
153
- const node = nodes[0];
314
+ const node = this.selectIn(doc, binding.xpath, binding.prefixMappings)[0];
154
315
  if (!node)
155
316
  return undefined;
156
- return node.nodeType === 2 ? node.value : ((_a = node.textContent) !== null && _a !== void 0 ? _a : '');
317
+ return node.nodeType === 2 ? node.value : ((_b = node.textContent) !== null && _b !== void 0 ? _b : '');
157
318
  }
158
319
  catch {
159
320
  return undefined;
160
321
  }
161
322
  }
323
+ /**
324
+ * The name of the part a binding names that could not be read: a custom XML store, or the core or application
325
+ * properties (CR-012 section 28, D3); undefined where it names a part that was read, or none.
326
+ */
327
+ unreadStoreOf(binding) {
328
+ return binding.storeItemID ? this.unreadStores.get(normaliseStoreItemId(binding.storeItemID)) : undefined;
329
+ }
330
+ /** The nodes an XPath selects in a store, with a binding's prefix mappings (the default namespace's left out). */
331
+ selectIn(doc, xpath, prefixMappings) {
332
+ if (!doc || !this.xpath)
333
+ return [];
334
+ const mappings = parsePrefixMappings(prefixMappings);
335
+ delete mappings[''];
336
+ return this.xpath.select(xpath, doc, mappings);
337
+ }
162
338
  // --- the comments anchored in the document (REQ-504) -----------------------------------------------
163
339
  /**
164
340
  * The ids of the comments anchored in the document: a `w:commentReference` or a `w:commentRangeStart`
@@ -311,17 +487,16 @@ export class TextContext {
311
487
  this.endnoteCount = Math.max(this.endnoteCount, other.endnoteCount);
312
488
  }
313
489
  }
314
- // --- the document properties (REQ-515; docx4j `Metadata`) --------------------------------------------
315
- /** The properties under `core`, `app` and `custom`, each where the package has that part. */
316
- async function properties(pkg) {
490
+ /** The properties under `core`, `app` and `custom`, each where the package has that part and it can be read. */
491
+ async function properties(pkg, read) {
317
492
  const out = {};
318
- const c = await readQuietly(pkg.docPropsCorePart);
493
+ const c = await read(pkg.docPropsCorePart, 'core');
319
494
  if (c)
320
495
  out.core = coreProperties(c);
321
- const a = await readQuietly(pkg.docPropsExtendedPart);
496
+ const a = await read(pkg.docPropsExtendedPart, 'app');
322
497
  if (a)
323
498
  out.app = appProperties(a);
324
- const x = await readQuietly(pkg.docPropsCustomPart);
499
+ const x = await read(pkg.docPropsCustomPart, 'custom');
325
500
  if (x)
326
501
  out.custom = customProperties(x);
327
502
  return out;