@docx4j/core-ts 0.1.5 → 0.2.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 +76 -0
- package/dist/model/content/Body.d.mts +36 -2
- package/dist/model/content/Body.mjs +79 -4
- package/dist/model/content/Comment.d.mts +15 -5
- package/dist/model/content/Comment.mjs +70 -22
- package/dist/model/content/List.d.mts +13 -2
- package/dist/model/content/List.mjs +48 -9
- package/dist/model/content/Paragraph.d.mts +11 -1
- package/dist/model/content/Paragraph.mjs +18 -7
- package/dist/model/content/Range.d.mts +7 -2
- package/dist/model/content/Range.mjs +21 -8
- package/dist/model/content/binder.d.mts +44 -0
- package/dist/model/content/binder.mjs +95 -0
- package/dist/model/content/comments.d.mts +19 -1
- package/dist/model/content/index.d.mts +5 -3
- package/dist/model/content/index.mjs +3 -1
- package/dist/model/content/search.d.mts +27 -2
- package/dist/model/content/search.mjs +59 -3
- package/dist/model/content/stylesFacade.d.mts +37 -0
- package/dist/model/content/stylesFacade.mjs +114 -0
- package/dist/model/content/tracking.d.mts +57 -2
- package/dist/model/content/tracking.mjs +47 -15
- package/dist/model/customxml/CustomXmlPartCollection.d.mts +7 -0
- package/dist/model/customxml/CustomXmlPartCollection.mjs +40 -6
- package/dist/model/customxml/xpath.d.mts +18 -3
- package/dist/model/customxml/xpath.mjs +18 -3
- package/dist/office-js/index.d.mts +3 -1
- package/dist/office-js/run.d.mts +12 -2
- package/dist/office-js/run.mjs +8 -1
- package/dist/office-js/shimmed.d.mts +46 -0
- package/dist/office-js/shimmed.mjs +1 -0
- package/dist/opc/exceptions.d.mts +25 -0
- package/dist/opc/exceptions.mjs +20 -0
- package/dist/opc/index.d.mts +1 -1
- package/dist/opc/index.mjs +1 -1
- package/dist/opc/mce/understood.mjs +37 -8
- package/dist/packages/WordprocessingMLPackage.d.mts +92 -2
- package/dist/packages/WordprocessingMLPackage.mjs +144 -1
- package/dist/parts/XmlPart.d.mts +44 -0
- package/dist/parts/XmlPart.mjs +86 -3
- package/dist/parts/sml/index.d.mts +9 -8
- package/dist/parts/sml/index.mjs +23 -19
- package/dist/parts/wml/index.d.mts +10 -2
- package/dist/parts/wml/index.mjs +11 -3
- package/dist/parts/wml/spliceableStyles.d.mts +1 -0
- package/dist/parts/wml/spliceableStyles.mjs +122 -0
- package/package.json +2 -2
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { OpcPackage, type PackageSource } from './OpcPackage.mjs';
|
|
2
2
|
import type { LoadOptions } from '../opc/Load.mjs';
|
|
3
3
|
import type { Part } from '../parts/Part.mjs';
|
|
4
|
-
import { MainDocumentPart, type NumberingDefinitionsPart } from '../parts/wml/index.mjs';
|
|
4
|
+
import { MainDocumentPart, StyleDefinitionsPart, type NumberingDefinitionsPart } from '../parts/wml/index.mjs';
|
|
5
5
|
import { type FontSettings, type DefaultThemeValue } from '../model/fonts/defaultTheme.mjs';
|
|
6
6
|
import type { Body, Address, Outline, OutlineParagraph, OutlineTable } from '../model/content/Body.mjs';
|
|
7
7
|
import type { Paragraph } from '../model/content/Paragraph.mjs';
|
|
8
8
|
import type { Author } from '../model/content/comments.mjs';
|
|
9
9
|
import { CustomXmlPartCollection } from '../model/customxml/CustomXmlPartCollection.mjs';
|
|
10
10
|
import { type XPathEngine } from '../model/customxml/xpath.mjs';
|
|
11
|
-
import { ChangeTracker, type ChangeTrackingMode, type TrackingHost } from '../model/content/tracking.mjs';
|
|
11
|
+
import { ChangeTracker, type ChangeTrackingMode, type TrackingHost, type TrackingScope } from '../model/content/tracking.mjs';
|
|
12
12
|
import { NumberingFacade } from '../model/content/List.mjs';
|
|
13
|
+
import { StylesFacade } from '../model/content/stylesFacade.mjs';
|
|
14
|
+
import type { Bound } from '../model/content/binder.mjs';
|
|
13
15
|
import { PropertyResolver } from '../model/properties/PropertyResolver.mjs';
|
|
14
16
|
import type { Emulator } from '../model/listnumbering/Emulator.mjs';
|
|
15
17
|
import type { TrackedChange } from '../model/content/TrackedChange.mjs';
|
|
18
|
+
import { XmlPart } from '../parts/XmlPart.mjs';
|
|
16
19
|
import type { PartSink } from '../opc/PartStore.mjs';
|
|
17
20
|
import '../parts/wml/comments.mjs';
|
|
18
21
|
/** The outline of a document: the body's paragraphs and tables, then each header's and footer's (loaded ones only). */
|
|
@@ -59,6 +62,25 @@ export declare class WordprocessingMLPackage extends OpcPackage implements Track
|
|
|
59
62
|
private tracker;
|
|
60
63
|
/** Set when `changeTrackingMode` was written but the settings part was not unmarshalled yet. */
|
|
61
64
|
private trackingPending;
|
|
65
|
+
/**
|
|
66
|
+
* `withTracking`'s mode for the duration of one call: read by the getter before everything else
|
|
67
|
+
* and **never** written by the setter, so it touches neither the settings part, `trackingPending`
|
|
68
|
+
* nor the cached `trackingMode` (CR-002 section 28.1).
|
|
69
|
+
*/
|
|
70
|
+
private trackingOverride;
|
|
71
|
+
/**
|
|
72
|
+
* The `withTracking` calls still running, oldest first, and what the package said before the
|
|
73
|
+
* first of them. The latest call's markup applies; when it ends, the latest one still running
|
|
74
|
+
* applies again, and when none is left the package is as it was (CR-002 section 31).
|
|
75
|
+
*/
|
|
76
|
+
private trackingScopes;
|
|
77
|
+
private beforeScopes;
|
|
78
|
+
/**
|
|
79
|
+
* The parts `seedAnnotationIds()` has read, with what it read: `true` for a part still holding its
|
|
80
|
+
* source, whose bytes cannot change, and otherwise the bytes that were set, which stay the same
|
|
81
|
+
* array until they are set again.
|
|
82
|
+
*/
|
|
83
|
+
private seededParts;
|
|
62
84
|
/**
|
|
63
85
|
* The package's font settings (CR-001 Phase B step 4), docx4j's `docx4j.fonts.*` properties
|
|
64
86
|
* scoped to one package: `defaultTheme` says which Office theme a font reference resolves
|
|
@@ -131,6 +153,30 @@ export declare class WordprocessingMLPackage extends OpcPackage implements Track
|
|
|
131
153
|
*/
|
|
132
154
|
get changeTrackingMode(): ChangeTrackingMode;
|
|
133
155
|
set changeTrackingMode(mode: ChangeTrackingMode);
|
|
156
|
+
/**
|
|
157
|
+
* Runs `fn` with revisions recorded as `markup` says, and the document's own setting untouched:
|
|
158
|
+
* nothing is written to `w:trackRevisions`, no settings part is created, and the mode a later read
|
|
159
|
+
* reports is the one that was there before (CR-002 section 28.1, for an editor whose agent must
|
|
160
|
+
* record its edits as a named author in a room whose tracking setting belongs to the document).
|
|
161
|
+
*
|
|
162
|
+
* The author, the date and the mode are restored in a `finally`, so a throw from `fn` cannot leave
|
|
163
|
+
* the package tracking. It is asynchronous for one reason: it awaits `seedAnnotationIds()` before
|
|
164
|
+
* running `fn`, so a revision written inside it cannot reuse an id from a part nobody has read
|
|
165
|
+
* (section 28.2). `fn` itself may be synchronous.
|
|
166
|
+
*
|
|
167
|
+
* Calls nest, and calls that overlap in time end in whatever order they end without leaving the
|
|
168
|
+
* package tracking. But the markup is the package's for as long as a call runs, not the call's:
|
|
169
|
+
* while two are running, **the later one's author and mode apply to every edit**, the earlier
|
|
170
|
+
* one's included, because nothing here can tell which asynchronous call an edit came from. Await
|
|
171
|
+
* one before starting another when they must be recorded as different authors (section 31).
|
|
172
|
+
*/
|
|
173
|
+
withTracking<T>(markup: TrackingScope, fn: () => T | Promise<T>): Promise<T>;
|
|
174
|
+
/**
|
|
175
|
+
* One `withTracking` call's markup made the package's. The tracker is left alone: the getter makes
|
|
176
|
+
* another when the mode differs, and the id counter it reads is the package's, so a tracker kept
|
|
177
|
+
* across the call cannot reissue an id given out inside it (section 31).
|
|
178
|
+
*/
|
|
179
|
+
private applyTrackingScope;
|
|
134
180
|
/** The mode as the settings part has it, unmarshalling the part (docx4j's async counterpart of the getter). */
|
|
135
181
|
getChangeTrackingMode(): Promise<ChangeTrackingMode>;
|
|
136
182
|
/** Writes `w:trackRevisions`, unmarshalling the settings part, or creating it when there is none. */
|
|
@@ -145,6 +191,41 @@ export declare class WordprocessingMLPackage extends OpcPackage implements Track
|
|
|
145
191
|
getTrackedChanges(): TrackedChange[];
|
|
146
192
|
/** The tracker every content-API mutation asks for; undefined while the mode is `Off`. */
|
|
147
193
|
get changeTracker(): ChangeTracker | undefined;
|
|
194
|
+
/**
|
|
195
|
+
* The objects an XPath selects in a part of this document, through the package's engine
|
|
196
|
+
* (CR-006; docx4j `MainDocumentPart.getJAXBNodesViaXPath`). The part defaults to the main
|
|
197
|
+
* document part, which is what a `//w:p[...]` expression means.
|
|
198
|
+
*/
|
|
199
|
+
selectObjects(xpath: string, options?: {
|
|
200
|
+
part?: XmlPart<unknown>;
|
|
201
|
+
namespaces?: Record<string, string>;
|
|
202
|
+
}): Promise<Bound[]>;
|
|
203
|
+
/**
|
|
204
|
+
* TrackingHost: a floor for the annotation id, from the WordprocessingML parts that were **not**
|
|
205
|
+
* unmarshalled; `seedAnnotationIds()` fills it. 0 until then (CR-002 section 28.2).
|
|
206
|
+
*/
|
|
207
|
+
annotationIdFloor: number;
|
|
208
|
+
/**
|
|
209
|
+
* TrackingHost: the last annotation id issued, shared by every tracker this package makes (CR-002
|
|
210
|
+
* section 31). Forgotten when the mode is set, as the tracker used to be, so that the next id is
|
|
211
|
+
* found by a fresh scan of whatever has arrived since by other routes (`insertOoxml`, `setContents`).
|
|
212
|
+
*/
|
|
213
|
+
annotationIdCounter: number | undefined;
|
|
214
|
+
/**
|
|
215
|
+
* Raises `annotationIdFloor` above every annotation id in the WordprocessingML parts that are not
|
|
216
|
+
* unmarshalled, reading each **privately** (`readContents`, CR-001 Phase D's device), so that a
|
|
217
|
+
* part nobody has touched keeps its byte-for-byte round trip and still cannot have its ids reused.
|
|
218
|
+
*
|
|
219
|
+
* Awaited by `setChangeTrackingMode()` and by `withTracking()`, which is where tracking is turned
|
|
220
|
+
* on; a caller that uses the synchronous `changeTrackingMode` setter on a document with unread
|
|
221
|
+
* parts should await this itself, since `nextId()` cannot (the content API's setters are
|
|
222
|
+
* synchronous, which is why the floor exists at all rather than the scan being made async).
|
|
223
|
+
*
|
|
224
|
+
* A part is read once for the bytes it holds: an agent that wraps every edit in `withTracking`
|
|
225
|
+
* would otherwise re-read every header, footer and note part each time (section 31). Its source
|
|
226
|
+
* bytes cannot change; bytes that were set are read again when they are set again.
|
|
227
|
+
*/
|
|
228
|
+
seedAnnotationIds(): Promise<number>;
|
|
148
229
|
/** TrackingHost: the trees a new revision id must be above, which is every part already unmarshalled. */
|
|
149
230
|
markupRoots(): object[];
|
|
150
231
|
/** A clone carries the identity, the tracking date, the theme setting and the XPath engine. */
|
|
@@ -162,6 +243,15 @@ export declare class WordprocessingMLPackage extends OpcPackage implements Track
|
|
|
162
243
|
* `customXmlParts.load()`; every node call is synchronous afterwards.
|
|
163
244
|
*/
|
|
164
245
|
xpathEngine: XPathEngine;
|
|
246
|
+
/**
|
|
247
|
+
* The document's style definitions as a verb that needs no `Body`: `ensure(ids)` splices in a
|
|
248
|
+
* style the document lacks, out of the same defaults `createPackage()` writes, with what it is
|
|
249
|
+
* based on and linked to (CR-002 section 22.2).
|
|
250
|
+
*/
|
|
251
|
+
get styles(): StylesFacade;
|
|
252
|
+
private stylesFacade;
|
|
253
|
+
/** The styles part, created with the defaults when the document has none (CR-002 section 22.2). */
|
|
254
|
+
ensureStyleDefinitionsPart(): StyleDefinitionsPart;
|
|
165
255
|
/**
|
|
166
256
|
* The document's list definitions as verbs that need no paragraph: `newList()` and
|
|
167
257
|
* `restart(numId)` (CR-002 section 19). `Paragraph.startNewList()` and `List.restart()` are
|
|
@@ -12,8 +12,9 @@ import { newFontSettings, themeOfSettings, } from '../model/fonts/defaultTheme.m
|
|
|
12
12
|
import { HeaderPart, FooterPart } from '../parts/wml/index.mjs';
|
|
13
13
|
import { CustomXmlPartCollection } from '../model/customxml/CustomXmlPartCollection.mjs';
|
|
14
14
|
import { DefaultXPathEngine } from '../model/customxml/xpath.mjs';
|
|
15
|
-
import { ChangeTracker } from '../model/content/tracking.mjs';
|
|
15
|
+
import { ChangeTracker, highestAnnotationId } from '../model/content/tracking.mjs';
|
|
16
16
|
import { NumberingFacade } from '../model/content/List.mjs';
|
|
17
|
+
import { StylesFacade } from '../model/content/stylesFacade.mjs';
|
|
17
18
|
import { PropertyResolver } from '../model/properties/PropertyResolver.mjs';
|
|
18
19
|
import { PropertyResolverNotCreatedException } from '../opc/exceptions.mjs';
|
|
19
20
|
import { XmlPart } from '../parts/XmlPart.mjs';
|
|
@@ -68,6 +69,18 @@ export class WordprocessingMLPackage extends OpcPackage {
|
|
|
68
69
|
this.author = { name: 'docx4j' };
|
|
69
70
|
/** Set when `changeTrackingMode` was written but the settings part was not unmarshalled yet. */
|
|
70
71
|
this.trackingPending = false;
|
|
72
|
+
/**
|
|
73
|
+
* The `withTracking` calls still running, oldest first, and what the package said before the
|
|
74
|
+
* first of them. The latest call's markup applies; when it ends, the latest one still running
|
|
75
|
+
* applies again, and when none is left the package is as it was (CR-002 section 31).
|
|
76
|
+
*/
|
|
77
|
+
this.trackingScopes = [];
|
|
78
|
+
/**
|
|
79
|
+
* The parts `seedAnnotationIds()` has read, with what it read: `true` for a part still holding its
|
|
80
|
+
* source, whose bytes cannot change, and otherwise the bytes that were set, which stay the same
|
|
81
|
+
* array until they are set again.
|
|
82
|
+
*/
|
|
83
|
+
this.seededParts = new WeakMap();
|
|
71
84
|
/**
|
|
72
85
|
* The package's font settings (CR-001 Phase B step 4), docx4j's `docx4j.fonts.*` properties
|
|
73
86
|
* scoped to one package: `defaultTheme` says which Office theme a font reference resolves
|
|
@@ -79,6 +92,11 @@ export class WordprocessingMLPackage extends OpcPackage {
|
|
|
79
92
|
* `mainDocumentPart.refreshFonts()`.
|
|
80
93
|
*/
|
|
81
94
|
this.fonts = newFontSettings();
|
|
95
|
+
/**
|
|
96
|
+
* TrackingHost: a floor for the annotation id, from the WordprocessingML parts that were **not**
|
|
97
|
+
* unmarshalled; `seedAnnotationIds()` fills it. 0 until then (CR-002 section 28.2).
|
|
98
|
+
*/
|
|
99
|
+
this.annotationIdFloor = 0;
|
|
82
100
|
// --- custom XML (CR-002 phase E) ---
|
|
83
101
|
/**
|
|
84
102
|
* The XPath engine the custom XML views use (CR-002 section 3.6): `document.evaluate` in a
|
|
@@ -258,6 +276,8 @@ export class WordprocessingMLPackage extends OpcPackage {
|
|
|
258
276
|
*/
|
|
259
277
|
get changeTrackingMode() {
|
|
260
278
|
var _a;
|
|
279
|
+
if (this.trackingOverride !== undefined)
|
|
280
|
+
return this.trackingOverride;
|
|
261
281
|
if (this.trackingMode !== undefined)
|
|
262
282
|
return this.trackingMode;
|
|
263
283
|
const settings = (_a = this.mainDocumentPart) === null || _a === void 0 ? void 0 : _a.documentSettingsPart;
|
|
@@ -268,6 +288,7 @@ export class WordprocessingMLPackage extends OpcPackage {
|
|
|
268
288
|
set changeTrackingMode(mode) {
|
|
269
289
|
this.trackingMode = mode;
|
|
270
290
|
this.tracker = undefined;
|
|
291
|
+
this.annotationIdCounter = undefined;
|
|
271
292
|
const settings = this.mainDocumentPart ? this.settingsPart() : undefined;
|
|
272
293
|
if (settings === null || settings === void 0 ? void 0 : settings.isUnmarshalled) {
|
|
273
294
|
writeTrackRevisions(settings.contents, mode);
|
|
@@ -276,6 +297,58 @@ export class WordprocessingMLPackage extends OpcPackage {
|
|
|
276
297
|
else
|
|
277
298
|
this.trackingPending = true;
|
|
278
299
|
}
|
|
300
|
+
/**
|
|
301
|
+
* Runs `fn` with revisions recorded as `markup` says, and the document's own setting untouched:
|
|
302
|
+
* nothing is written to `w:trackRevisions`, no settings part is created, and the mode a later read
|
|
303
|
+
* reports is the one that was there before (CR-002 section 28.1, for an editor whose agent must
|
|
304
|
+
* record its edits as a named author in a room whose tracking setting belongs to the document).
|
|
305
|
+
*
|
|
306
|
+
* The author, the date and the mode are restored in a `finally`, so a throw from `fn` cannot leave
|
|
307
|
+
* the package tracking. It is asynchronous for one reason: it awaits `seedAnnotationIds()` before
|
|
308
|
+
* running `fn`, so a revision written inside it cannot reuse an id from a part nobody has read
|
|
309
|
+
* (section 28.2). `fn` itself may be synchronous.
|
|
310
|
+
*
|
|
311
|
+
* Calls nest, and calls that overlap in time end in whatever order they end without leaving the
|
|
312
|
+
* package tracking. But the markup is the package's for as long as a call runs, not the call's:
|
|
313
|
+
* while two are running, **the later one's author and mode apply to every edit**, the earlier
|
|
314
|
+
* one's included, because nothing here can tell which asynchronous call an edit came from. Await
|
|
315
|
+
* one before starting another when they must be recorded as different authors (section 31).
|
|
316
|
+
*/
|
|
317
|
+
async withTracking(markup, fn) {
|
|
318
|
+
await this.seedAnnotationIds();
|
|
319
|
+
const scope = { ...markup };
|
|
320
|
+
if (this.trackingScopes.length === 0) {
|
|
321
|
+
this.beforeScopes = { author: this.author, date: this.trackedChangeDate, override: this.trackingOverride };
|
|
322
|
+
}
|
|
323
|
+
this.trackingScopes.push(scope);
|
|
324
|
+
this.applyTrackingScope(scope);
|
|
325
|
+
try {
|
|
326
|
+
return await fn();
|
|
327
|
+
}
|
|
328
|
+
finally {
|
|
329
|
+
this.trackingScopes.splice(this.trackingScopes.indexOf(scope), 1);
|
|
330
|
+
const latest = this.trackingScopes[this.trackingScopes.length - 1];
|
|
331
|
+
if (latest !== undefined)
|
|
332
|
+
this.applyTrackingScope(latest);
|
|
333
|
+
else if (this.beforeScopes !== undefined) {
|
|
334
|
+
this.author = this.beforeScopes.author;
|
|
335
|
+
this.trackedChangeDate = this.beforeScopes.date;
|
|
336
|
+
this.trackingOverride = this.beforeScopes.override;
|
|
337
|
+
this.beforeScopes = undefined;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* One `withTracking` call's markup made the package's. The tracker is left alone: the getter makes
|
|
343
|
+
* another when the mode differs, and the id counter it reads is the package's, so a tracker kept
|
|
344
|
+
* across the call cannot reissue an id given out inside it (section 31).
|
|
345
|
+
*/
|
|
346
|
+
applyTrackingScope(scope) {
|
|
347
|
+
var _a;
|
|
348
|
+
this.author = scope.author;
|
|
349
|
+
this.trackedChangeDate = scope.date;
|
|
350
|
+
this.trackingOverride = (_a = scope.mode) !== null && _a !== void 0 ? _a : 'TrackAll';
|
|
351
|
+
}
|
|
279
352
|
/** The mode as the settings part has it, unmarshalling the part (docx4j's async counterpart of the getter). */
|
|
280
353
|
async getChangeTrackingMode() {
|
|
281
354
|
var _a, _b;
|
|
@@ -288,8 +361,11 @@ export class WordprocessingMLPackage extends OpcPackage {
|
|
|
288
361
|
async setChangeTrackingMode(mode) {
|
|
289
362
|
this.trackingMode = mode;
|
|
290
363
|
this.tracker = undefined;
|
|
364
|
+
this.annotationIdCounter = undefined;
|
|
291
365
|
this.trackingPending = false;
|
|
292
366
|
writeTrackRevisions(await this.settingsPart().getContents(), mode);
|
|
367
|
+
if (mode !== 'Off')
|
|
368
|
+
await this.seedAnnotationIds();
|
|
293
369
|
}
|
|
294
370
|
/** The settings part, created (with its relationship and content type) when the document has none. */
|
|
295
371
|
settingsPart() {
|
|
@@ -319,6 +395,53 @@ export class WordprocessingMLPackage extends OpcPackage {
|
|
|
319
395
|
this.tracker = new ChangeTracker(this, mode);
|
|
320
396
|
return this.tracker;
|
|
321
397
|
}
|
|
398
|
+
/**
|
|
399
|
+
* The objects an XPath selects in a part of this document, through the package's engine
|
|
400
|
+
* (CR-006; docx4j `MainDocumentPart.getJAXBNodesViaXPath`). The part defaults to the main
|
|
401
|
+
* document part, which is what a `//w:p[...]` expression means.
|
|
402
|
+
*/
|
|
403
|
+
async selectObjects(xpath, options = {}) {
|
|
404
|
+
var _a;
|
|
405
|
+
const part = (_a = options.part) !== null && _a !== void 0 ? _a : this.getMainDocumentPart();
|
|
406
|
+
return part.selectObjects(xpath, { engine: this.xpathEngine, namespaces: options.namespaces });
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Raises `annotationIdFloor` above every annotation id in the WordprocessingML parts that are not
|
|
410
|
+
* unmarshalled, reading each **privately** (`readContents`, CR-001 Phase D's device), so that a
|
|
411
|
+
* part nobody has touched keeps its byte-for-byte round trip and still cannot have its ids reused.
|
|
412
|
+
*
|
|
413
|
+
* Awaited by `setChangeTrackingMode()` and by `withTracking()`, which is where tracking is turned
|
|
414
|
+
* on; a caller that uses the synchronous `changeTrackingMode` setter on a document with unread
|
|
415
|
+
* parts should await this itself, since `nextId()` cannot (the content API's setters are
|
|
416
|
+
* synchronous, which is why the floor exists at all rather than the scan being made async).
|
|
417
|
+
*
|
|
418
|
+
* A part is read once for the bytes it holds: an agent that wraps every edit in `withTracking`
|
|
419
|
+
* would otherwise re-read every header, footer and note part each time (section 31). Its source
|
|
420
|
+
* bytes cannot change; bytes that were set are read again when they are set again.
|
|
421
|
+
*/
|
|
422
|
+
async seedAnnotationIds() {
|
|
423
|
+
for (const part of this.parts) {
|
|
424
|
+
if (!(part instanceof XmlPart) || part.isUnmarshalled)
|
|
425
|
+
continue;
|
|
426
|
+
if (!part.partName.name.startsWith('/word/'))
|
|
427
|
+
continue;
|
|
428
|
+
const read = part.isUntouched ? true : await part.getBytes();
|
|
429
|
+
if (this.seededParts.get(part) === read)
|
|
430
|
+
continue;
|
|
431
|
+
let tree;
|
|
432
|
+
try {
|
|
433
|
+
tree = await part.readContents();
|
|
434
|
+
}
|
|
435
|
+
catch {
|
|
436
|
+
tree = undefined;
|
|
437
|
+
} // an unreadable part cannot hold ids we must avoid
|
|
438
|
+
if (typeof tree === 'object' && tree !== null) {
|
|
439
|
+
this.annotationIdFloor = highestAnnotationId(tree, this.annotationIdFloor);
|
|
440
|
+
}
|
|
441
|
+
this.seededParts.set(part, read);
|
|
442
|
+
}
|
|
443
|
+
return this.annotationIdFloor;
|
|
444
|
+
}
|
|
322
445
|
/** TrackingHost: the trees a new revision id must be above, which is every part already unmarshalled. */
|
|
323
446
|
markupRoots() {
|
|
324
447
|
const out = [];
|
|
@@ -386,6 +509,26 @@ export class WordprocessingMLPackage extends OpcPackage {
|
|
|
386
509
|
all.push(await part.getBody());
|
|
387
510
|
return all;
|
|
388
511
|
}
|
|
512
|
+
/**
|
|
513
|
+
* The document's style definitions as a verb that needs no `Body`: `ensure(ids)` splices in a
|
|
514
|
+
* style the document lacks, out of the same defaults `createPackage()` writes, with what it is
|
|
515
|
+
* based on and linked to (CR-002 section 22.2).
|
|
516
|
+
*/
|
|
517
|
+
get styles() {
|
|
518
|
+
var _a;
|
|
519
|
+
return ((_a = this.stylesFacade) !== null && _a !== void 0 ? _a : (this.stylesFacade = new StylesFacade(this)));
|
|
520
|
+
}
|
|
521
|
+
/** The styles part, created with the defaults when the document has none (CR-002 section 22.2). */
|
|
522
|
+
ensureStyleDefinitionsPart() {
|
|
523
|
+
const main = this.getMainDocumentPart();
|
|
524
|
+
let part = main.styleDefinitionsPart;
|
|
525
|
+
if (!part) {
|
|
526
|
+
part = new StyleDefinitionsPart();
|
|
527
|
+
part.setXml(DEFAULT_STYLES_XML);
|
|
528
|
+
main.addTargetPart(part);
|
|
529
|
+
}
|
|
530
|
+
return part;
|
|
531
|
+
}
|
|
389
532
|
/**
|
|
390
533
|
* The document's list definitions as verbs that need no paragraph: `newList()` and
|
|
391
534
|
* `restart(numId)` (CR-002 section 19). `Paragraph.startNewList()` and `List.restart()` are
|
package/dist/parts/XmlPart.d.mts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { type Jsonix } from '@docx4j/generated-objects-ts';
|
|
2
2
|
import { Part } from './Part.mjs';
|
|
3
3
|
import { PartName } from '../opc/PartName.mjs';
|
|
4
|
+
import { type Bound } from '../model/content/binder.mjs';
|
|
5
|
+
/** What `selectObjects` needs: the engine to evaluate with, and any prefixes beyond the defaults. */
|
|
6
|
+
export interface SelectOptions {
|
|
7
|
+
/** The engine; `pkg.selectObjects` and `Body.select` pass the package's. It is readied on the snapshot. */
|
|
8
|
+
engine?: {
|
|
9
|
+
ready(context?: Node): Promise<void>;
|
|
10
|
+
select(expression: string, context: Node, namespaces?: Record<string, string>): Node[];
|
|
11
|
+
} | undefined;
|
|
12
|
+
/** Prefixes added to (or overriding) the objects package's own table. */
|
|
13
|
+
namespaces?: Record<string, string> | undefined;
|
|
14
|
+
}
|
|
4
15
|
/** A qualified name for a part's root element. */
|
|
5
16
|
export interface RootName {
|
|
6
17
|
namespaceURI: string;
|
|
@@ -31,6 +42,12 @@ export declare class XmlPart<T = unknown> extends Part {
|
|
|
31
42
|
/** True once contents were unmarshalled or set; the part is then re-marshalled on save. */
|
|
32
43
|
get isUnmarshalled(): boolean;
|
|
33
44
|
get isLoaded(): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* True while the part holds nothing but its source: never unmarshalled, no contents or bytes set.
|
|
47
|
+
* Such a part is written back byte for byte, and what a private read finds in it cannot change,
|
|
48
|
+
* which is what lets a reader remember it (CR-002 section 31).
|
|
49
|
+
*/
|
|
50
|
+
get isUntouched(): boolean;
|
|
34
51
|
/** The unmarshalled contents (docx4j getJaxbElement()); throws when not yet unmarshalled. */
|
|
35
52
|
get contents(): T;
|
|
36
53
|
/** The root element as [name, value]; undefined until unmarshalled. */
|
|
@@ -57,8 +74,35 @@ export declare class XmlPart<T = unknown> extends Part {
|
|
|
57
74
|
getXml(): Promise<string>;
|
|
58
75
|
/** Marshalled bytes when unmarshalled or set, else the source bytes untouched. */
|
|
59
76
|
getBytes(): Promise<Uint8Array>;
|
|
77
|
+
/**
|
|
78
|
+
* The objects an XPath selects in this part, through a DOM marshalled for the purpose
|
|
79
|
+
* (CR-006; docx4j `JaxbXmlPart.getJAXBNodesViaXPath`, which does it with a JAXB `Binder`).
|
|
80
|
+
*
|
|
81
|
+
* Each result carries the node the expression selected, the tree object behind it, the attribute
|
|
82
|
+
* name when an attribute was selected, and the node's path from the root. The DOM is a **snapshot**:
|
|
83
|
+
* changing it changes nothing, and a select leaves the part's own bytes alone - the marshal is
|
|
84
|
+
* thrown away. Every call marshals afresh, since the tree has no change log a cached snapshot could
|
|
85
|
+
* be checked against (CR-006 section 2).
|
|
86
|
+
*
|
|
87
|
+
* The prefixes default to the ones the objects package writes, so `//w:p[w:pPr/w:pStyle/@w:val='Heading1']`
|
|
88
|
+
* needs no mapping; `options.namespaces` adds to or overrides them. The engine is the caller's
|
|
89
|
+
* (`pkg.xpathEngine`: XPath 1.0 by default, 3.1 with `FontoXPathEngine`), and is readied here on
|
|
90
|
+
* the snapshot itself: whether an engine can evaluate a tree is a property of the document that
|
|
91
|
+
* tree is in (CR-002 section 21), and readying it on a marshal of its own would cost a second one
|
|
92
|
+
* (CR-002 section 31).
|
|
93
|
+
*/
|
|
94
|
+
selectObjects(xpath: string, options?: SelectOptions): Promise<Bound[]>;
|
|
60
95
|
/** The root element as a DOM element (docx4j XmlUtils.marshaltoW3CDomDocument). */
|
|
61
96
|
marshalToNode(): Promise<Element>;
|
|
97
|
+
/**
|
|
98
|
+
* Unmarshals, and names the part when the runtime refuses. The runtime's own message says what is
|
|
99
|
+
* wrong and, since `@docx4j/jsonix` 3.4.0, where - `w:sz/@w:val (line 12): Argument [NaN] must be
|
|
100
|
+
* 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).
|
|
104
|
+
*/
|
|
105
|
+
private unmarshal;
|
|
62
106
|
/** The source root element's namespace declarations, prefix to URI (`xmlns=` is the empty prefix). */
|
|
63
107
|
private recordDeclarations;
|
|
64
108
|
/**
|
package/dist/parts/XmlPart.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { unmarshalNode, marshalNode, NAMESPACE_PREFIXES } from '@docx4j/generated-objects-ts';
|
|
2
2
|
import { Part } from './Part.mjs';
|
|
3
3
|
import { Namespaces } from './Namespaces.mjs';
|
|
4
|
-
import { Docx4JException } from '../opc/exceptions.mjs';
|
|
4
|
+
import { Docx4JException, PartUnmarshalException } from '../opc/exceptions.mjs';
|
|
5
5
|
import { parseXml, serializeXml, encodeText, decodeXmlText, XML_DECLARATION } from '../xml/dom.mjs';
|
|
6
6
|
import { log } from '../model/properties/log.mjs';
|
|
7
|
+
import { bindSnapshot, boundsOf } from '../model/content/binder.mjs';
|
|
7
8
|
const XMLNS_NS = 'http://www.w3.org/2000/xmlns/';
|
|
8
9
|
const MC_NS = Namespaces.MARKUP_COMPATIBILITY;
|
|
9
10
|
/** The objects package's prefix table the other way round, prefix to URI, built once. */
|
|
@@ -18,6 +19,26 @@ function knownUriOf(prefix) {
|
|
|
18
19
|
return prefixToUri.get(prefix);
|
|
19
20
|
}
|
|
20
21
|
const warnedPrefixes = new Set();
|
|
22
|
+
/**
|
|
23
|
+
* The element or attribute a located runtime error names, out of its message: jsonix writes
|
|
24
|
+
* `w:sz/@w:val (line 7): ...` when it could place the fault and `w:bogus (line 3): ...` for an
|
|
25
|
+
* element, so the location is everything before the position or the colon - and `w:sz` must not be
|
|
26
|
+
* cut at its own prefix separator, which a naive split on `:` does.
|
|
27
|
+
*/
|
|
28
|
+
function locationOf(message, located) {
|
|
29
|
+
var _a;
|
|
30
|
+
if (!located)
|
|
31
|
+
return undefined;
|
|
32
|
+
return (_a = /^(.+?)(?: \(line \d+(?:, column \d+)?\))?: /.exec(message)) === null || _a === void 0 ? void 0 : _a[1];
|
|
33
|
+
}
|
|
34
|
+
/** The objects package's prefix table as XPath wants it, prefix to URI; built once. */
|
|
35
|
+
const NAMESPACE_PREFIXES_BY_PREFIX = (() => {
|
|
36
|
+
const out = {};
|
|
37
|
+
for (const [uri, prefix] of Object.entries(NAMESPACE_PREFIXES))
|
|
38
|
+
if (prefix !== '' && !(prefix in out))
|
|
39
|
+
out[prefix] = uri;
|
|
40
|
+
return out;
|
|
41
|
+
})();
|
|
21
42
|
/**
|
|
22
43
|
* A part whose content is a typed element of the object model (docx4j JaxbXmlPart<E>). `T` is the
|
|
23
44
|
* root element's value type (`Document`, `Styles`, ...). Contents are unmarshalled on first
|
|
@@ -36,6 +57,14 @@ export class XmlPart extends Part {
|
|
|
36
57
|
get isLoaded() {
|
|
37
58
|
return this.element !== undefined || this.bytes !== undefined;
|
|
38
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* True while the part holds nothing but its source: never unmarshalled, no contents or bytes set.
|
|
62
|
+
* Such a part is written back byte for byte, and what a private read finds in it cannot change,
|
|
63
|
+
* which is what lets a reader remember it (CR-002 section 31).
|
|
64
|
+
*/
|
|
65
|
+
get isUntouched() {
|
|
66
|
+
return this.element === undefined && this.bytes === undefined;
|
|
67
|
+
}
|
|
39
68
|
/** The unmarshalled contents (docx4j getJaxbElement()); throws when not yet unmarshalled. */
|
|
40
69
|
get contents() {
|
|
41
70
|
if (this.element === undefined)
|
|
@@ -56,7 +85,7 @@ export class XmlPart extends Part {
|
|
|
56
85
|
const doc = parseXml(decodeXmlText(bytes));
|
|
57
86
|
this.recordDeclarations(doc);
|
|
58
87
|
(_b = this.preprocessor) === null || _b === void 0 ? void 0 : _b.call(this, doc, this);
|
|
59
|
-
this.element = await
|
|
88
|
+
this.element = await this.unmarshal(doc);
|
|
60
89
|
(_c = this.rootName) !== null && _c !== void 0 ? _c : (this.rootName = { namespaceURI: this.element.name.namespaceURI, localPart: this.element.name.localPart });
|
|
61
90
|
this.bytes = undefined;
|
|
62
91
|
}
|
|
@@ -81,7 +110,7 @@ export class XmlPart extends Part {
|
|
|
81
110
|
const doc = parseXml(decodeXmlText(bytes));
|
|
82
111
|
this.recordDeclarations(doc);
|
|
83
112
|
(_b = this.preprocessor) === null || _b === void 0 ? void 0 : _b.call(this, doc, this);
|
|
84
|
-
return (await
|
|
113
|
+
return (await this.unmarshal(doc)).value;
|
|
85
114
|
}
|
|
86
115
|
/** Replaces the contents (docx4j setJaxbElement / setContents). The root name defaults to the part's. */
|
|
87
116
|
setContents(value, rootName) {
|
|
@@ -116,6 +145,36 @@ export class XmlPart extends Part {
|
|
|
116
145
|
throw new Docx4JException(`Part ${this.partName} has no content and no source container`);
|
|
117
146
|
return loaded;
|
|
118
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* The objects an XPath selects in this part, through a DOM marshalled for the purpose
|
|
150
|
+
* (CR-006; docx4j `JaxbXmlPart.getJAXBNodesViaXPath`, which does it with a JAXB `Binder`).
|
|
151
|
+
*
|
|
152
|
+
* Each result carries the node the expression selected, the tree object behind it, the attribute
|
|
153
|
+
* name when an attribute was selected, and the node's path from the root. The DOM is a **snapshot**:
|
|
154
|
+
* changing it changes nothing, and a select leaves the part's own bytes alone - the marshal is
|
|
155
|
+
* thrown away. Every call marshals afresh, since the tree has no change log a cached snapshot could
|
|
156
|
+
* be checked against (CR-006 section 2).
|
|
157
|
+
*
|
|
158
|
+
* The prefixes default to the ones the objects package writes, so `//w:p[w:pPr/w:pStyle/@w:val='Heading1']`
|
|
159
|
+
* needs no mapping; `options.namespaces` adds to or overrides them. The engine is the caller's
|
|
160
|
+
* (`pkg.xpathEngine`: XPath 1.0 by default, 3.1 with `FontoXPathEngine`), and is readied here on
|
|
161
|
+
* the snapshot itself: whether an engine can evaluate a tree is a property of the document that
|
|
162
|
+
* tree is in (CR-002 section 21), and readying it on a marshal of its own would cost a second one
|
|
163
|
+
* (CR-002 section 31).
|
|
164
|
+
*/
|
|
165
|
+
async selectObjects(xpath, options = {}) {
|
|
166
|
+
var _a;
|
|
167
|
+
const element = (_a = this.element) !== null && _a !== void 0 ? _a : (await this.getContents(), this.element);
|
|
168
|
+
if (element === undefined)
|
|
169
|
+
throw new Docx4JException(`Part ${this.partName} has no contents to select over`);
|
|
170
|
+
const engine = options.engine;
|
|
171
|
+
if (engine === undefined)
|
|
172
|
+
throw new Docx4JException(`selectObjects on ${this.partName} needs an XPath engine; pkg.selectObjects passes the package's`);
|
|
173
|
+
const snapshot = await bindSnapshot(element);
|
|
174
|
+
await engine.ready(snapshot.root);
|
|
175
|
+
const namespaces = { ...NAMESPACE_PREFIXES_BY_PREFIX, ...options.namespaces };
|
|
176
|
+
return boundsOf(engine.select(xpath, snapshot.root, namespaces), snapshot);
|
|
177
|
+
}
|
|
119
178
|
/** The root element as a DOM element (docx4j XmlUtils.marshaltoW3CDomDocument). */
|
|
120
179
|
async marshalToNode() {
|
|
121
180
|
if (this.element === undefined)
|
|
@@ -124,6 +183,30 @@ export class XmlPart extends Part {
|
|
|
124
183
|
this.declareIgnorablePrefixes(root);
|
|
125
184
|
return root;
|
|
126
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Unmarshals, and names the part when the runtime refuses. The runtime's own message says what is
|
|
188
|
+
* wrong and, since `@docx4j/jsonix` 3.4.0, where - `w:sz/@w:val (line 12): Argument [NaN] must be
|
|
189
|
+
* an integer` - but not which part it was reading, which is the first thing a person editing a
|
|
190
|
+
* part's XML needs (CR-002 section 27). The location it found is lifted onto the exception so a
|
|
191
|
+
* caller does not parse the message; `line` is undefined where the parser supplies no position,
|
|
192
|
+
* which is every browser (only xmldom carries a locator).
|
|
193
|
+
*/
|
|
194
|
+
async unmarshal(doc) {
|
|
195
|
+
var _a, _b;
|
|
196
|
+
try {
|
|
197
|
+
return await unmarshalNode(doc);
|
|
198
|
+
}
|
|
199
|
+
catch (cause) {
|
|
200
|
+
const located = cause;
|
|
201
|
+
const message = typeof (located === null || located === void 0 ? void 0 : located.message) === 'string' ? located.message : String(cause);
|
|
202
|
+
throw new PartUnmarshalException(`${this.partName}: ${message}`, this.partName.name, {
|
|
203
|
+
cause,
|
|
204
|
+
location: locationOf(message, (located === null || located === void 0 ? void 0 : located.jsonixLocated) === true),
|
|
205
|
+
line: typeof ((_a = located === null || located === void 0 ? void 0 : located.node) === null || _a === void 0 ? void 0 : _a.lineNumber) === 'number' ? located.node.lineNumber : undefined,
|
|
206
|
+
column: typeof ((_b = located === null || located === void 0 ? void 0 : located.node) === null || _b === void 0 ? void 0 : _b.columnNumber) === 'number' ? located.node.columnNumber : undefined,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
127
210
|
/** The source root element's namespace declarations, prefix to URI (`xmlns=` is the empty prefix). */
|
|
128
211
|
recordDeclarations(doc) {
|
|
129
212
|
var _a;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type * as sml from '@docx4j/generated-objects-ts/modules/org_xlsx4j_sml';
|
|
2
|
+
import type * as x14 from '@docx4j/generated-objects-ts/modules/org_xlsx4j_com_microsoft_schemas_office_spreadsheetml_x2009_x9_main';
|
|
3
|
+
import type * as x15 from '@docx4j/generated-objects-ts/modules/org_xlsx4j_com_microsoft_schemas_office_spreadsheetml_x2010_x11_main';
|
|
2
4
|
import { XmlPart } from '../XmlPart.mjs';
|
|
3
|
-
import { DefaultXmlPart } from '../DefaultXmlPart.mjs';
|
|
4
5
|
import { BinaryPart } from '../BinaryPart.mjs';
|
|
5
6
|
import { Part } from '../Part.mjs';
|
|
6
7
|
import { PartName } from '../../opc/PartName.mjs';
|
|
@@ -99,31 +100,31 @@ export declare class PrinterSettingsPart extends BinaryPart {
|
|
|
99
100
|
constructor(partName: PartName | string);
|
|
100
101
|
}
|
|
101
102
|
/** A slicer cache (`x14:slicerCacheDefinition`), from the workbook. docx4j `SlicerCachePart`. */
|
|
102
|
-
export declare class SlicerCachePart extends
|
|
103
|
+
export declare class SlicerCachePart extends XmlPart<x14.CTSlicerCacheDefinition> {
|
|
103
104
|
constructor(partName?: PartName | string);
|
|
104
105
|
}
|
|
105
106
|
/** A sheet's slicers (`x14:slicers`). docx4j `SlicersPart`. */
|
|
106
|
-
export declare class SlicersPart extends
|
|
107
|
+
export declare class SlicersPart extends XmlPart<x14.CTSlicers> {
|
|
107
108
|
constructor(partName?: PartName | string);
|
|
108
109
|
}
|
|
109
110
|
/** A timeline cache (`x15:timelineCacheDefinition`), from the workbook. docx4j `TimelineCachePart`. */
|
|
110
|
-
export declare class TimelineCachePart extends
|
|
111
|
+
export declare class TimelineCachePart extends XmlPart<x15.CTTimelineCacheDefinition> {
|
|
111
112
|
constructor(partName?: PartName | string);
|
|
112
113
|
}
|
|
113
114
|
/** A sheet's timelines (`x15:timelines`). docx4j `TimelinesPart`. */
|
|
114
|
-
export declare class TimelinesPart extends
|
|
115
|
+
export declare class TimelinesPart extends XmlPart<x15.CTTimelines> {
|
|
115
116
|
constructor(partName?: PartName | string);
|
|
116
117
|
}
|
|
117
118
|
/** A form control's properties (`x14:formControlPr`), from a worksheet. docx4j `ControlPropertiesPart`. */
|
|
118
|
-
export declare class ControlPropertiesPart extends
|
|
119
|
+
export declare class ControlPropertiesPart extends XmlPart<x14.CTFormControlPr> {
|
|
119
120
|
constructor(partName?: PartName | string);
|
|
120
121
|
}
|
|
121
122
|
/** The properties of a custom data part (`x14:datastoreItem`). docx4j `CustomDataPropertiesPart`. */
|
|
122
|
-
export declare class CustomDataPropertiesPart extends
|
|
123
|
+
export declare class CustomDataPropertiesPart extends XmlPart<x14.CTDatastoreItem> {
|
|
123
124
|
constructor(partName?: PartName | string);
|
|
124
125
|
}
|
|
125
126
|
/** A survey (`x15:survey`), from the workbook. docx4j `SurveyPart`. */
|
|
126
|
-
export declare class SurveyPart extends
|
|
127
|
+
export declare class SurveyPart extends XmlPart<x15.CTSurvey> {
|
|
127
128
|
constructor(partName?: PartName | string);
|
|
128
129
|
}
|
|
129
130
|
/**
|