@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.
- package/README.md +60 -0
- package/dist/model/content/Body.mjs +6 -17
- package/dist/model/content/Comment.d.mts +2 -2
- package/dist/model/content/Comment.mjs +12 -7
- package/dist/model/content/ContentControl.d.mts +1 -1
- package/dist/model/content/ContentControl.mjs +8 -4
- package/dist/model/content/Font.d.mts +4 -1
- package/dist/model/content/Font.mjs +6 -2
- package/dist/model/content/List.d.mts +6 -3
- package/dist/model/content/List.mjs +15 -8
- package/dist/model/content/Paragraph.d.mts +12 -2
- package/dist/model/content/Paragraph.mjs +31 -11
- package/dist/model/content/Table.mjs +26 -16
- package/dist/model/content/TrackedChange.d.mts +2 -2
- package/dist/model/content/TrackedChange.mjs +6 -3
- package/dist/model/content/index.d.mts +3 -1
- package/dist/model/content/index.mjs +4 -1
- package/dist/model/content/ooxml.mjs +12 -1
- package/dist/model/content/paraIds.d.mts +5 -0
- package/dist/model/content/paraIds.mjs +54 -0
- package/dist/model/content/search.d.mts +2 -0
- package/dist/model/content/search.mjs +51 -7
- package/dist/model/content/story.d.mts +5 -2
- package/dist/model/content/story.mjs +6 -3
- package/dist/model/content/tableRules.mjs +2 -0
- package/dist/model/content/tracking.d.mts +35 -6
- package/dist/model/content/tracking.mjs +93 -12
- package/dist/model/content/tree.d.mts +7 -8
- package/dist/model/content/tree.mjs +7 -9
- package/dist/model/content/wordRange.d.mts +48 -0
- package/dist/model/content/wordRange.mjs +97 -0
- package/dist/model/customxml/CustomXmlPartCollection.d.mts +22 -1
- package/dist/model/customxml/CustomXmlPartCollection.mjs +48 -2
- package/dist/model/customxml/XmlMapping.d.mts +31 -7
- package/dist/model/customxml/XmlMapping.mjs +53 -10
- package/dist/model/customxml/bindings.d.mts +12 -2
- package/dist/model/customxml/bindings.mjs +98 -16
- package/dist/model/customxml/index.d.mts +1 -0
- package/dist/model/customxml/index.mjs +1 -0
- package/dist/model/customxml/opendope.d.mts +4 -1
- package/dist/model/customxml/opendope.mjs +9 -6
- package/dist/model/customxml/pictures.mjs +5 -3
- package/dist/model/customxml/storeItems.d.mts +57 -0
- package/dist/model/customxml/storeItems.mjs +122 -0
- package/dist/model/customxml/xhtml.mjs +15 -20
- package/dist/model/dateZone.d.mts +25 -0
- package/dist/model/dateZone.mjs +57 -0
- package/dist/model/index.d.mts +1 -0
- package/dist/model/index.mjs +1 -0
- package/dist/model/properties/PropertyResolver.d.mts +14 -1
- package/dist/model/properties/PropertyResolver.mjs +18 -3
- package/dist/model/properties/index.d.mts +1 -1
- package/dist/model/properties/log.mjs +2 -2
- package/dist/model/properties/styleUtil.d.mts +3 -1
- package/dist/model/properties/styleUtil.mjs +20 -24
- package/dist/model/text/CaseMapping.d.mts +38 -0
- package/dist/model/text/CaseMapping.mjs +86 -0
- package/dist/model/text/Extractor.d.mts +66 -6
- package/dist/model/text/Extractor.mjs +300 -56
- package/dist/model/text/PlainText.mjs +7 -4
- package/dist/model/text/TextContext.d.mts +103 -8
- package/dist/model/text/TextContext.mjs +228 -53
- package/dist/model/text/TextOptions.d.mts +88 -5
- package/dist/model/text/TextOptions.mjs +158 -17
- package/dist/model/text/TextResult.d.mts +126 -9
- package/dist/model/text/TextResult.mjs +115 -12
- package/dist/model/text/WmlToMarkdown.mjs +32 -7
- package/dist/model/text/caseMapping.generated.d.mts +7 -0
- package/dist/model/text/caseMapping.generated.mjs +214 -0
- package/dist/model/text/index.d.mts +3 -2
- package/dist/model/text/index.mjs +1 -0
- package/dist/model/wordIntegers.d.mts +29 -0
- package/dist/model/wordIntegers.mjs +78 -0
- package/dist/office-js/document.mjs +15 -1
- package/dist/office-js/toApiScript.mjs +8 -7
- package/dist/opc/DirectoryPartStore.d.mts +6 -2
- package/dist/opc/DirectoryPartStore.mjs +11 -4
- package/dist/opc/FlatOpcPartStore.d.mts +4 -2
- package/dist/opc/FlatOpcPartStore.mjs +11 -8
- package/dist/opc/Load.d.mts +32 -0
- package/dist/opc/Load.mjs +115 -5
- package/dist/opc/PartStore.d.mts +34 -1
- package/dist/opc/PartStore.mjs +71 -8
- package/dist/opc/Save.d.mts +17 -2
- package/dist/opc/Save.mjs +100 -4
- package/dist/opc/ZipPartStore.d.mts +4 -2
- package/dist/opc/ZipPartStore.mjs +12 -7
- package/dist/opc/exceptions.d.mts +140 -0
- package/dist/opc/exceptions.mjs +108 -0
- package/dist/opc/index.d.mts +7 -4
- package/dist/opc/index.mjs +5 -3
- package/dist/opc/mce/preprocessor.d.mts +121 -10
- package/dist/opc/mce/preprocessor.mjs +122 -21
- package/dist/opc/mce/understood.d.mts +3 -35
- package/dist/opc/mce/understood.mjs +27 -53
- package/dist/opc/repair/breaks.d.mts +23 -0
- package/dist/opc/repair/breaks.mjs +288 -0
- package/dist/opc/repair/hosts.d.mts +12 -0
- package/dist/opc/repair/hosts.mjs +33 -0
- package/dist/opc/repair/mainRoot.d.mts +19 -0
- package/dist/opc/repair/mainRoot.mjs +168 -0
- package/dist/opc/repair/unknownElements.d.mts +8 -0
- package/dist/opc/repair/unknownElements.mjs +180 -0
- package/dist/opc/repair/wmlNames.generated.d.mts +2 -0
- package/dist/opc/repair/wmlNames.generated.mjs +80 -0
- package/dist/opc/skipped.d.mts +50 -0
- package/dist/opc/skipped.mjs +140 -0
- package/dist/packages/OpcPackage.d.mts +98 -5
- package/dist/packages/OpcPackage.mjs +122 -7
- package/dist/packages/WordprocessingMLPackage.mjs +3 -1
- package/dist/packages/index.d.mts +1 -1
- package/dist/parts/Part.d.mts +5 -1
- package/dist/parts/RelationshipsPart.d.mts +15 -0
- package/dist/parts/RelationshipsPart.mjs +66 -1
- package/dist/parts/XmlPart.d.mts +54 -5
- package/dist/parts/XmlPart.mjs +203 -10
- package/dist/parts/wml/index.d.mts +12 -1
- package/dist/parts/wml/index.mjs +25 -1
- 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 : ''}
|
|
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.
|
|
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
|
|
9
|
-
import
|
|
10
|
-
import type
|
|
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
|
-
|
|
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
|
-
/**
|
|
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)
|
|
98
|
-
*
|
|
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
|
-
/**
|
|
8
|
-
const
|
|
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
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
140
|
-
*
|
|
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 =
|
|
147
|
-
|
|
148
|
-
|
|
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
|
|
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 : ((
|
|
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
|
-
|
|
315
|
-
|
|
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
|
|
493
|
+
const c = await read(pkg.docPropsCorePart, 'core');
|
|
319
494
|
if (c)
|
|
320
495
|
out.core = coreProperties(c);
|
|
321
|
-
const a = await
|
|
496
|
+
const a = await read(pkg.docPropsExtendedPart, 'app');
|
|
322
497
|
if (a)
|
|
323
498
|
out.app = appProperties(a);
|
|
324
|
-
const x = await
|
|
499
|
+
const x = await read(pkg.docPropsCustomPart, 'custom');
|
|
325
500
|
if (x)
|
|
326
501
|
out.custom = customProperties(x);
|
|
327
502
|
return out;
|