@docx4j/core-ts 0.1.0 → 0.1.2
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 +410 -10
- package/dist/model/content/Body.d.mts +109 -4
- package/dist/model/content/Body.mjs +300 -29
- package/dist/model/content/Comment.d.mts +78 -0
- package/dist/model/content/Comment.mjs +429 -0
- package/dist/model/content/ContentControl.d.mts +173 -0
- package/dist/model/content/ContentControl.mjs +638 -0
- package/dist/model/content/Font.d.mts +48 -8
- package/dist/model/content/Font.mjs +55 -13
- package/dist/model/content/InlinePicture.d.mts +103 -0
- package/dist/model/content/InlinePicture.mjs +361 -0
- package/dist/model/content/List.d.mts +253 -0
- package/dist/model/content/List.mjs +778 -0
- package/dist/model/content/Paragraph.d.mts +213 -16
- package/dist/model/content/Paragraph.mjs +611 -57
- package/dist/model/content/Range.d.mts +48 -4
- package/dist/model/content/Range.mjs +161 -4
- package/dist/model/content/Table.d.mts +130 -0
- package/dist/model/content/Table.mjs +429 -0
- package/dist/model/content/TrackedChange.d.mts +96 -0
- package/dist/model/content/TrackedChange.mjs +352 -0
- package/dist/model/content/comments.d.mts +122 -0
- package/dist/model/content/comments.mjs +248 -0
- package/dist/model/content/index.d.mts +13 -3
- package/dist/model/content/index.mjs +15 -3
- package/dist/model/content/ooxml.d.mts +17 -0
- package/dist/model/content/ooxml.mjs +214 -0
- package/dist/model/content/styles.d.mts +21 -0
- package/dist/model/content/styles.mjs +86 -0
- package/dist/model/content/tracking.d.mts +104 -0
- package/dist/model/content/tracking.mjs +313 -0
- package/dist/model/content/tree.d.mts +56 -9
- package/dist/model/content/tree.mjs +104 -31
- package/dist/model/customxml/CustomXmlPart.d.mts +158 -0
- package/dist/model/customxml/CustomXmlPart.mjs +415 -0
- package/dist/model/customxml/CustomXmlPartCollection.d.mts +68 -0
- package/dist/model/customxml/CustomXmlPartCollection.mjs +183 -0
- package/dist/model/customxml/XmlMapping.d.mts +46 -0
- package/dist/model/customxml/XmlMapping.mjs +98 -0
- package/dist/model/customxml/bindings.d.mts +41 -0
- package/dist/model/customxml/bindings.mjs +230 -0
- package/dist/model/customxml/index.d.mts +7 -0
- package/dist/model/customxml/index.mjs +9 -0
- package/dist/model/customxml/insert.d.mts +20 -0
- package/dist/model/customxml/insert.mjs +22 -0
- package/dist/model/customxml/kinds.d.mts +103 -0
- package/dist/model/customxml/kinds.mjs +267 -0
- package/dist/model/customxml/xpath.d.mts +52 -0
- package/dist/model/customxml/xpath.mjs +205 -0
- package/dist/model/fonts/CJKToEnglish.d.mts +2 -0
- package/dist/model/fonts/CJKToEnglish.mjs +16 -0
- package/dist/model/fonts/FontFallback.d.mts +58 -0
- package/dist/model/fonts/FontFallback.mjs +219 -0
- package/dist/model/fonts/IdentityPlusMapper.d.mts +7 -0
- package/dist/model/fonts/IdentityPlusMapper.mjs +19 -0
- package/dist/model/fonts/LanguageTagToScriptMapping.d.mts +7 -0
- package/dist/model/fonts/LanguageTagToScriptMapping.mjs +67 -0
- package/dist/model/fonts/Mapper.d.mts +190 -0
- package/dist/model/fonts/Mapper.mjs +481 -0
- package/dist/model/fonts/PhysicalFont.d.mts +34 -0
- package/dist/model/fonts/PhysicalFont.mjs +62 -0
- package/dist/model/fonts/RunFontSelector.d.mts +194 -0
- package/dist/model/fonts/RunFontSelector.mjs +441 -0
- package/dist/model/fonts/ThemeFonts.d.mts +17 -0
- package/dist/model/fonts/ThemeFonts.mjs +76 -0
- package/dist/model/fonts/defaultTheme.d.mts +37 -0
- package/dist/model/fonts/defaultTheme.mjs +57 -0
- package/dist/model/fonts/families.generated.d.mts +11 -0
- package/dist/model/fonts/families.generated.mjs +17 -0
- package/dist/model/fonts/fontsInUse.d.mts +39 -0
- package/dist/model/fonts/fontsInUse.mjs +183 -0
- package/dist/model/fonts/index.d.mts +14 -0
- package/dist/model/fonts/index.mjs +29 -0
- package/dist/model/fonts/lookup.d.mts +5 -0
- package/dist/model/fonts/lookup.mjs +12 -0
- package/dist/model/fonts/registry.d.mts +31 -0
- package/dist/model/fonts/registry.mjs +103 -0
- package/dist/model/fonts/scripts.d.mts +2 -0
- package/dist/model/fonts/scripts.mjs +78 -0
- package/dist/model/fonts/substitutions.generated.d.mts +30 -0
- package/dist/model/fonts/substitutions.generated.mjs +12 -0
- package/dist/model/fonts/themes.generated.d.mts +6 -0
- package/dist/model/fonts/themes.generated.mjs +11 -0
- package/dist/model/index.d.mts +5 -0
- package/dist/model/index.mjs +13 -1
- package/dist/model/listnumbering/Emulator.d.mts +187 -0
- package/dist/model/listnumbering/Emulator.mjs +332 -0
- package/dist/model/listnumbering/definitions.d.mts +251 -0
- package/dist/model/listnumbering/definitions.mjs +571 -0
- package/dist/model/listnumbering/formats.d.mts +25 -0
- package/dist/model/listnumbering/formats.mjs +320 -0
- package/dist/model/listnumbering/index.d.mts +4 -0
- package/dist/model/listnumbering/index.mjs +15 -0
- package/dist/model/listnumbering/state.d.mts +97 -0
- package/dist/model/listnumbering/state.mjs +179 -0
- package/dist/model/properties/PropertyResolver.d.mts +216 -0
- package/dist/model/properties/PropertyResolver.mjs +524 -0
- package/dist/model/properties/catalogue.d.mts +179 -0
- package/dist/model/properties/catalogue.mjs +860 -0
- package/dist/model/properties/index.d.mts +4 -0
- package/dist/model/properties/index.mjs +9 -0
- package/dist/model/properties/log.d.mts +7 -0
- package/dist/model/properties/log.mjs +25 -0
- package/dist/model/properties/styleUtil.d.mts +112 -0
- package/dist/model/properties/styleUtil.mjs +359 -0
- package/dist/node/index.d.mts +1 -0
- package/dist/node/index.mjs +12 -0
- package/dist/office-js/document.d.mts +111 -0
- package/dist/office-js/document.mjs +267 -0
- package/dist/office-js/enums.d.mts +187 -0
- package/dist/office-js/enums.mjs +176 -0
- package/dist/office-js/errors.d.mts +29 -0
- package/dist/office-js/errors.mjs +39 -0
- package/dist/office-js/extras.d.mts +5 -0
- package/dist/office-js/extras.mjs +29 -0
- package/dist/office-js/fragment.d.mts +10 -0
- package/dist/office-js/fragment.mjs +23 -0
- package/dist/office-js/index.d.mts +183 -0
- package/dist/office-js/index.mjs +54 -0
- package/dist/office-js/proxy.d.mts +72 -0
- package/dist/office-js/proxy.mjs +279 -0
- package/dist/office-js/run.d.mts +32 -0
- package/dist/office-js/run.mjs +66 -0
- package/dist/office-js/supported.d.mts +7 -0
- package/dist/office-js/supported.generated.d.mts +1 -0
- package/dist/office-js/supported.generated.mjs +235 -0
- package/dist/office-js/supported.mjs +53 -0
- package/dist/office-js/toApiScript.d.mts +25 -0
- package/dist/office-js/toApiScript.mjs +394 -0
- package/dist/opc/ContentTypes.d.mts +11 -0
- package/dist/opc/ContentTypes.mjs +14 -0
- package/dist/opc/DirectoryPartStore.d.mts +47 -0
- package/dist/opc/DirectoryPartStore.mjs +123 -0
- package/dist/opc/exceptions.d.mts +30 -0
- package/dist/opc/exceptions.mjs +38 -0
- package/dist/opc/index.d.mts +1 -1
- package/dist/opc/index.mjs +1 -1
- package/dist/packages/OpcPackage.d.mts +19 -1
- package/dist/packages/OpcPackage.mjs +29 -0
- package/dist/packages/PresentationMLPackage.d.mts +71 -1
- package/dist/packages/PresentationMLPackage.mjs +179 -0
- package/dist/packages/SpreadsheetMLPackage.d.mts +30 -1
- package/dist/packages/SpreadsheetMLPackage.mjs +78 -0
- package/dist/packages/WordprocessingMLPackage.d.mts +124 -2
- package/dist/packages/WordprocessingMLPackage.mjs +309 -2
- package/dist/packages/index.d.mts +1 -1
- package/dist/packages/index.mjs +1 -1
- package/dist/parts/DefaultXmlPart.d.mts +18 -0
- package/dist/parts/DefaultXmlPart.mjs +34 -5
- package/dist/parts/Namespaces.d.mts +18 -0
- package/dist/parts/Namespaces.mjs +20 -0
- package/dist/parts/Part.d.mts +6 -0
- package/dist/parts/Part.mjs +13 -0
- package/dist/parts/PartRegistry.mjs +13 -1
- package/dist/parts/XmlPart.d.mts +33 -0
- package/dist/parts/XmlPart.mjs +105 -2
- package/dist/parts/pml/defaults.d.mts +6 -0
- package/dist/parts/pml/defaults.mjs +53 -0
- package/dist/parts/pml/index.d.mts +66 -3
- package/dist/parts/pml/index.mjs +206 -7
- package/dist/parts/pml/placeholders.d.mts +71 -0
- package/dist/parts/pml/placeholders.mjs +141 -0
- package/dist/parts/sml/index.d.mts +80 -1
- package/dist/parts/sml/index.mjs +154 -1
- package/dist/parts/wml/comments.d.mts +1 -0
- package/dist/parts/wml/comments.mjs +179 -0
- package/dist/parts/wml/defaultNumbering.d.mts +1 -0
- package/dist/parts/wml/defaultNumbering.mjs +216 -0
- package/dist/parts/wml/index.d.mts +121 -0
- package/dist/parts/wml/index.mjs +291 -0
- package/dist/xml/dom.mjs +1 -2
- package/package.json +25 -4
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
import { deepCopy, unmarshalNode } from '@docx4j/generated-objects-ts';
|
|
2
|
+
import * as f from '@docx4j/generated-objects-ts/factory/org_docx4j_wml';
|
|
3
|
+
import { parseXml } from '../../xml/dom.mjs';
|
|
4
|
+
import { Docx4JException } from '../../opc/exceptions.mjs';
|
|
5
|
+
import { DEFAULT_NUMBERING_XML } from '../../parts/wml/defaultNumbering.mjs';
|
|
6
|
+
import { NumberingState, storyKindOf } from '../listnumbering/state.mjs';
|
|
7
|
+
import { LEVELS } from '../listnumbering/definitions.mjs';
|
|
8
|
+
import { isElement, linkParents } from './tree.mjs';
|
|
9
|
+
import { Font } from './Font.mjs';
|
|
10
|
+
function packageOf(body) {
|
|
11
|
+
const pkg = body.package_;
|
|
12
|
+
return typeof (pkg === null || pkg === void 0 ? void 0 : pkg.getMainDocumentPart) === 'function' ? pkg : undefined;
|
|
13
|
+
}
|
|
14
|
+
function mainOf(body) {
|
|
15
|
+
var _a;
|
|
16
|
+
try {
|
|
17
|
+
return (_a = packageOf(body)) === null || _a === void 0 ? void 0 : _a.getMainDocumentPart();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return undefined; // no main document part
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** The numbering part, or undefined where the document has none. */
|
|
24
|
+
function numberingPartOf(body) {
|
|
25
|
+
var _a;
|
|
26
|
+
return (_a = mainOf(body)) === null || _a === void 0 ? void 0 : _a.numberingDefinitionsPart;
|
|
27
|
+
}
|
|
28
|
+
/** The definitions, or undefined where there is no numbering part or nothing has read it. */
|
|
29
|
+
function definitionsOf(body) {
|
|
30
|
+
var _a;
|
|
31
|
+
try {
|
|
32
|
+
return (_a = numberingPartOf(body)) === null || _a === void 0 ? void 0 : _a.definitions;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return undefined; // the part has not been read (await pkg.getPropertyResolver())
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The package's numbering emulator, or undefined where the document has no lists (or nothing
|
|
40
|
+
* has read the numbering part: `await pkg.getBody()` builds the resolver, which reads it).
|
|
41
|
+
*/
|
|
42
|
+
export function numberingEmulatorOf(body) {
|
|
43
|
+
var _a;
|
|
44
|
+
try {
|
|
45
|
+
return (_a = numberingPartOf(body)) === null || _a === void 0 ? void 0 : _a.getEmulator();
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Where a paragraph's numbering resolves to (docx4j `Emulator.numRefFor`), or undefined. */
|
|
52
|
+
function numRefOf(paragraph) {
|
|
53
|
+
var _a;
|
|
54
|
+
const ref = (_a = numberingEmulatorOf(paragraph.parentBody)) === null || _a === void 0 ? void 0 : _a.numRefFor(paragraph.p.pPr);
|
|
55
|
+
return ref === undefined || ref.notNumbered ? undefined : ref;
|
|
56
|
+
}
|
|
57
|
+
// --------------------------------------------------------------------------- the story walk
|
|
58
|
+
/**
|
|
59
|
+
* Every paragraph under `root` in document order, each with the {@link NumberingState} it
|
|
60
|
+
* counts in. `test/parity.test.mjs`'s `walkNumbering`, which is the harness's own walk: a text
|
|
61
|
+
* box's paragraphs stay in this story's list (they are in the part, in document order) but
|
|
62
|
+
* count in a story of their own, which is docx4j's rule (CR-014 probe P7).
|
|
63
|
+
*/
|
|
64
|
+
function walkStory(root, state, emit) {
|
|
65
|
+
const walk = (value, current) => {
|
|
66
|
+
if (Array.isArray(value)) {
|
|
67
|
+
for (const item of value)
|
|
68
|
+
walk(item, current);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (typeof value !== 'object' || value === null)
|
|
72
|
+
return;
|
|
73
|
+
if (isElement(value)) {
|
|
74
|
+
walk(value.value, current);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const node = value;
|
|
78
|
+
let inner = current;
|
|
79
|
+
if (node.TYPE_NAME === 'org_docx4j_wml.P')
|
|
80
|
+
emit(node, current);
|
|
81
|
+
else if (node.TYPE_NAME === 'org_docx4j_wml.CTTxbxContent')
|
|
82
|
+
inner = new NumberingState();
|
|
83
|
+
for (const key of Object.keys(node)) {
|
|
84
|
+
if (key === 'PARENT' || key === 'TYPE_NAME')
|
|
85
|
+
continue;
|
|
86
|
+
walk(node[key], inner);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
walk(root, state);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The parts whose paragraphs share one story with this one, in docx4j's order: every header
|
|
93
|
+
* then every footer for a header or a footer, and the part alone otherwise (CR-001 section
|
|
94
|
+
* 15.2, `NumberingStates.forPart`).
|
|
95
|
+
*/
|
|
96
|
+
function storyPartsOf(body, part) {
|
|
97
|
+
if (storyKindOf(part) !== 'headersFooters')
|
|
98
|
+
return [part];
|
|
99
|
+
const main = mainOf(body);
|
|
100
|
+
return main === undefined ? [part] : [...main.headerParts, ...main.footerParts];
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Every numbered paragraph of this body's **story**, with the label and the counters, in one
|
|
104
|
+
* walk. A header or footer that has not been unmarshalled contributes nothing to the count,
|
|
105
|
+
* since it cannot be read synchronously: `await part.getBody()` on each first where a footer's
|
|
106
|
+
* numbers must continue the headers' (the story rule).
|
|
107
|
+
*/
|
|
108
|
+
export function listLabelsOf(body) {
|
|
109
|
+
const out = new Map();
|
|
110
|
+
const emulator = numberingEmulatorOf(body);
|
|
111
|
+
const part = body.part;
|
|
112
|
+
if (emulator === undefined || part === undefined)
|
|
113
|
+
return out;
|
|
114
|
+
const state = new NumberingState();
|
|
115
|
+
for (const story of storyPartsOf(body, part)) {
|
|
116
|
+
if (!story.isUnmarshalled)
|
|
117
|
+
continue;
|
|
118
|
+
walkStory(story.contents, state, (p, counters) => {
|
|
119
|
+
var _a, _b, _c, _d, _e;
|
|
120
|
+
const result = emulator.getNumber(p.pPr, counters);
|
|
121
|
+
if (result === undefined || result.numString === undefined)
|
|
122
|
+
return;
|
|
123
|
+
out.set(p, {
|
|
124
|
+
listString: result.numString,
|
|
125
|
+
level: Number((_a = result.ilvl) !== null && _a !== void 0 ? _a : '0'),
|
|
126
|
+
numId: (_b = result.numId) !== null && _b !== void 0 ? _b : '',
|
|
127
|
+
siblingIndex: ((_c = result.count) !== null && _c !== void 0 ? _c : 1) - ((_e = (_d = result.level) === null || _d === void 0 ? void 0 : _d.start) !== null && _e !== void 0 ? _e : 1),
|
|
128
|
+
isBullet: result.isBullet,
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
/** The label of one paragraph, from a walk of its story. */
|
|
135
|
+
function labelOf(paragraph) {
|
|
136
|
+
return listLabelsOf(paragraph.parentBody).get(paragraph.p);
|
|
137
|
+
}
|
|
138
|
+
// ------------------------------------------------------------------------- the null objects
|
|
139
|
+
/**
|
|
140
|
+
* Office JS's `*OrNullObject` result: `isNullObject` is true and every other member throws.
|
|
141
|
+
* `src/office-js/proxy.mts` has the same shape for collections; it is repeated here rather
|
|
142
|
+
* than imported so that the content API does not pull the shim's proxies into a bundle.
|
|
143
|
+
*/
|
|
144
|
+
function nullObject(className) {
|
|
145
|
+
const fail = (member) => {
|
|
146
|
+
throw new ItemNotFound(`Word.${className} is a null object (this paragraph is not a list item); check isNullObject before using ${member}`);
|
|
147
|
+
};
|
|
148
|
+
const proxy = new Proxy({ isNullObject: true }, {
|
|
149
|
+
get(target, prop) {
|
|
150
|
+
if (typeof prop === 'symbol')
|
|
151
|
+
return Reflect.get(target, prop);
|
|
152
|
+
if (prop === 'isNullObject')
|
|
153
|
+
return true;
|
|
154
|
+
if (prop === 'toJSON')
|
|
155
|
+
return () => ({ isNullObject: true });
|
|
156
|
+
if (prop === 'then')
|
|
157
|
+
return undefined;
|
|
158
|
+
return fail(prop);
|
|
159
|
+
},
|
|
160
|
+
set(_target, prop) {
|
|
161
|
+
return fail(String(prop));
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
return proxy;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Nothing matched, as Office JS's `ItemNotFound` says it: the same `code` as the shim's
|
|
168
|
+
* `ItemNotFoundError`, so add-in code branching on `error.code` behaves the same.
|
|
169
|
+
*/
|
|
170
|
+
class ItemNotFound extends Error {
|
|
171
|
+
constructor(message) {
|
|
172
|
+
super(message);
|
|
173
|
+
this.code = 'ItemNotFound';
|
|
174
|
+
this.name = 'ItemNotFoundError';
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
export { ItemNotFound as ListItemNotFoundError };
|
|
178
|
+
// -------------------------------------------------------------------------------- the views
|
|
179
|
+
/**
|
|
180
|
+
* A subset of Office JS `Word.List` over one `w:num` and the `w:abstractNum` it names. A light
|
|
181
|
+
* view: it holds the `w:numId` and the body it was found through, and resolves both elements
|
|
182
|
+
* through the numbering part's definitions on every call.
|
|
183
|
+
*
|
|
184
|
+
* Every `setLevel*` method writes on the **abstract** definition, copying it first where
|
|
185
|
+
* another `w:num` shares it ({@link separate}), so that the change stays local to this list;
|
|
186
|
+
* each then refreshes the package's resolver, which rebuilds the numbering definitions.
|
|
187
|
+
*/
|
|
188
|
+
export class List {
|
|
189
|
+
constructor(
|
|
190
|
+
/** `w:numId`, as a string (docx4j's key; {@link id} is Office JS's number). */
|
|
191
|
+
numId,
|
|
192
|
+
/** The body this list was found through: where {@link paragraphs} looks. */
|
|
193
|
+
body) {
|
|
194
|
+
this.numId = numId;
|
|
195
|
+
this.body = body;
|
|
196
|
+
}
|
|
197
|
+
/** Office JS `list.id`: the `w:numId`. */
|
|
198
|
+
get id() {
|
|
199
|
+
return Number(this.numId);
|
|
200
|
+
}
|
|
201
|
+
/** The definitions this list is read from; throws where the numbering part has not been read. */
|
|
202
|
+
get definitions() {
|
|
203
|
+
const definitions = definitionsOf(this.body);
|
|
204
|
+
if (definitions === undefined) {
|
|
205
|
+
throw new Docx4JException('This document has no numbering definitions; await pkg.getBody() (or getPropertyResolver()) first');
|
|
206
|
+
}
|
|
207
|
+
return definitions;
|
|
208
|
+
}
|
|
209
|
+
/** The `w:num`'s definition (docx4j `ListNumberingDefinition`). */
|
|
210
|
+
get definition() {
|
|
211
|
+
const definition = this.definitions.list(this.numId);
|
|
212
|
+
if (definition === undefined)
|
|
213
|
+
throw new ItemNotFound(`No w:num for numId ${this.numId}`);
|
|
214
|
+
return definition;
|
|
215
|
+
}
|
|
216
|
+
/** The `w:num` (extension: the tree). */
|
|
217
|
+
get element() {
|
|
218
|
+
return this.definition.num;
|
|
219
|
+
}
|
|
220
|
+
/** The `w:abstractNum` this list resolves to (extension: the tree). */
|
|
221
|
+
get abstractElement() {
|
|
222
|
+
var _a;
|
|
223
|
+
return (_a = this.definition.abstractDefinition) === null || _a === void 0 ? void 0 : _a.abstractNum;
|
|
224
|
+
}
|
|
225
|
+
/** The level definition (the abstract level with this `w:num`'s override over it), or undefined. */
|
|
226
|
+
level(level) {
|
|
227
|
+
return this.definition.level(level);
|
|
228
|
+
}
|
|
229
|
+
/** Office JS `list.levelExistences`: whether each of the nine levels is defined. */
|
|
230
|
+
get levelExistences() {
|
|
231
|
+
return Array.from({ length: LEVELS }, (_, i) => this.definition.levelExists(i));
|
|
232
|
+
}
|
|
233
|
+
/** Whether the level is defined (extension; Office JS has {@link levelExistences}). */
|
|
234
|
+
levelExists(level) {
|
|
235
|
+
return this.definition.levelExists(level);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Office JS `list.levelTypes`: `Bullet`, `Number` or `Picture` for each of the nine levels,
|
|
239
|
+
* from the level's `w:numFmt` and its `w:lvlPicBulletId`. A level that is not defined reads
|
|
240
|
+
* `Number`, as Office JS reports for a level with no format of its own.
|
|
241
|
+
*/
|
|
242
|
+
get levelTypes() {
|
|
243
|
+
return Array.from({ length: LEVELS }, (_, i) => {
|
|
244
|
+
const level = this.level(i);
|
|
245
|
+
if (level === undefined)
|
|
246
|
+
return 'Number';
|
|
247
|
+
if (level.lvlPicBulletId !== undefined)
|
|
248
|
+
return 'Picture';
|
|
249
|
+
return level.isBullet ? 'Bullet' : 'Number';
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
/** Office JS `list.getLevelString(level)`: the level's `w:lvlText` ('%1.', '(%2)', a bullet). */
|
|
253
|
+
getLevelString(level) {
|
|
254
|
+
var _a, _b;
|
|
255
|
+
return (_b = (_a = this.level(level)) === null || _a === void 0 ? void 0 : _a.lvlText) !== null && _b !== void 0 ? _b : '';
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Office JS `list.getLevelFont(level)`: the `Font` the level's label is drawn with, over the
|
|
259
|
+
* level's `w:rPr`.
|
|
260
|
+
*
|
|
261
|
+
* **It is a writable view**, and Office JS pairs it with `resetLevelFont`, so it
|
|
262
|
+
* {@link separate}s first: the numbering part becomes live and a shared abstract definition
|
|
263
|
+
* is copied, exactly as the `setLevel*` methods do. A read that must not cost the part its
|
|
264
|
+
* byte-for-byte round trip is `list.level(n).labelRPr` (CR-002 section 17).
|
|
265
|
+
*/
|
|
266
|
+
getLevelFont(level) {
|
|
267
|
+
this.separate();
|
|
268
|
+
const holder = this.writableLvl(level);
|
|
269
|
+
return new Font(() => [holder]);
|
|
270
|
+
}
|
|
271
|
+
/** Office JS `list.getLevelParagraphs(level)`: this body's paragraphs at that level of this list. */
|
|
272
|
+
getLevelParagraphs(level) {
|
|
273
|
+
return this.paragraphs.filter((p) => levelOf(p) === level);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Office JS `list.paragraphs`: every paragraph of this body whose effective `w:numPr` names
|
|
277
|
+
* this list, in document order (a style-contributed `w:numPr` counts).
|
|
278
|
+
*/
|
|
279
|
+
get paragraphs() {
|
|
280
|
+
return this.body.paragraphs.filter((p) => { var _a; return ((_a = numRefOf(p)) === null || _a === void 0 ? void 0 : _a.numId) === this.numId; });
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Office JS `list.insertParagraph(text, location)`: a new paragraph of this list before its
|
|
284
|
+
* first item or after its last, at that item's level.
|
|
285
|
+
*/
|
|
286
|
+
insertParagraph(text, location) {
|
|
287
|
+
var _a;
|
|
288
|
+
const items = this.paragraphs;
|
|
289
|
+
if (items.length === 0) {
|
|
290
|
+
throw new ItemNotFound(`List ${this.id} has no paragraphs in this body to insert relative to`);
|
|
291
|
+
}
|
|
292
|
+
const anchor = location === 'Start' ? items[0] : items[items.length - 1];
|
|
293
|
+
const paragraph = anchor.insertParagraph(text, location === 'Start' ? 'Before' : 'After');
|
|
294
|
+
paragraph.attachToList(this.id, (_a = levelOf(anchor)) !== null && _a !== void 0 ? _a : 0);
|
|
295
|
+
return paragraph;
|
|
296
|
+
}
|
|
297
|
+
// --- the level writers ------------------------------------------------------------------
|
|
298
|
+
/**
|
|
299
|
+
* Office JS `list.setLevelNumbering(level, listNumbering, formatString)`: the level's
|
|
300
|
+
* `w:numFmt` and, from `formatString`, its `w:lvlText` - each number in the array is a
|
|
301
|
+
* 0-based level whose counter goes there (`[0, '.', 1]` is `'%1.%2'`), each string a literal.
|
|
302
|
+
* Without one the level numbers itself and takes a trailing full stop (`'%<n>.'`).
|
|
303
|
+
*/
|
|
304
|
+
setLevelNumbering(level, listNumbering, formatString) {
|
|
305
|
+
const lvl = this.writableLvl(level);
|
|
306
|
+
lvl.numFmt = f.createNumFmt({ val: NUMBERING_FORMATS[listNumbering] });
|
|
307
|
+
linkParents(lvl.numFmt, lvl);
|
|
308
|
+
lvl.lvlText = f.createLvlLvlText({ val: lvlTextOf(level, formatString) });
|
|
309
|
+
linkParents(lvl.lvlText, lvl);
|
|
310
|
+
this.written();
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Office JS `list.setLevelBullet(level, listBullet, charCode, fontName)`: the level's
|
|
314
|
+
* `w:numFmt` becomes `bullet` and its `w:lvlText` the bullet character, with the font in the
|
|
315
|
+
* level's `w:rPr/w:rFonts`. `Custom` takes `charCode` and `fontName`; the other six are the
|
|
316
|
+
* characters and faces Word's bullet library uses.
|
|
317
|
+
*/
|
|
318
|
+
setLevelBullet(level, listBullet, charCode, fontName) {
|
|
319
|
+
var _a, _b;
|
|
320
|
+
const preset = BULLETS[listBullet];
|
|
321
|
+
const code = listBullet === 'Custom' ? charCode : preset === null || preset === void 0 ? void 0 : preset.charCode;
|
|
322
|
+
if (code === undefined)
|
|
323
|
+
throw new Docx4JException("setLevelBullet('Custom') needs a charCode");
|
|
324
|
+
const font = fontName !== null && fontName !== void 0 ? fontName : preset === null || preset === void 0 ? void 0 : preset.fontName;
|
|
325
|
+
const lvl = this.writableLvl(level);
|
|
326
|
+
lvl.numFmt = f.createNumFmt({ val: 'bullet' });
|
|
327
|
+
linkParents(lvl.numFmt, lvl);
|
|
328
|
+
lvl.lvlText = f.createLvlLvlText({ val: String.fromCharCode(code) });
|
|
329
|
+
linkParents(lvl.lvlText, lvl);
|
|
330
|
+
if (font !== undefined && font !== '') {
|
|
331
|
+
const rPr = ((_a = lvl.rPr) !== null && _a !== void 0 ? _a : (lvl.rPr = { TYPE_NAME: 'org_docx4j_wml.RPr' }));
|
|
332
|
+
const rFonts = ((_b = rPr.rFonts) !== null && _b !== void 0 ? _b : (rPr.rFonts = { TYPE_NAME: 'org_docx4j_wml.RFonts' }));
|
|
333
|
+
rFonts.ascii = font;
|
|
334
|
+
rFonts.hAnsi = font;
|
|
335
|
+
rFonts.hint = 'default';
|
|
336
|
+
linkParents(rPr, lvl);
|
|
337
|
+
}
|
|
338
|
+
this.written();
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Office JS `list.setLevelIndents(level, textIndent, bulletNumberPictureIndent)`, both in
|
|
342
|
+
* points: the level's `w:ind/@w:left` is where the text sits and the label's position is the
|
|
343
|
+
* hanging indent from it (or `w:firstLine` where the label sits to the right of the text).
|
|
344
|
+
*/
|
|
345
|
+
setLevelIndents(level, textIndent, bulletNumberPictureIndent) {
|
|
346
|
+
var _a, _b;
|
|
347
|
+
const lvl = this.writableLvl(level);
|
|
348
|
+
const pPr = ((_a = lvl.pPr) !== null && _a !== void 0 ? _a : (lvl.pPr = { TYPE_NAME: 'org_docx4j_wml.PPr' }));
|
|
349
|
+
const ind = ((_b = pPr.ind) !== null && _b !== void 0 ? _b : (pPr.ind = f.createPPrBaseInd({})));
|
|
350
|
+
const left = Math.round(textIndent * TWIPS_PER_POINT);
|
|
351
|
+
const label = Math.round(bulletNumberPictureIndent * TWIPS_PER_POINT);
|
|
352
|
+
ind.left = left;
|
|
353
|
+
if (label <= left) {
|
|
354
|
+
ind.hanging = left - label;
|
|
355
|
+
delete ind.firstLine;
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
ind.firstLine = label - left;
|
|
359
|
+
delete ind.hanging;
|
|
360
|
+
}
|
|
361
|
+
linkParents(pPr, lvl);
|
|
362
|
+
this.written();
|
|
363
|
+
}
|
|
364
|
+
/** Office JS `list.setLevelAlignment(level, alignment)`: the level's `w:lvlJc`. */
|
|
365
|
+
setLevelAlignment(level, alignment) {
|
|
366
|
+
const lvl = this.writableLvl(level);
|
|
367
|
+
const val = LVL_JC[alignment];
|
|
368
|
+
if (val === undefined)
|
|
369
|
+
delete lvl.lvlJc;
|
|
370
|
+
else {
|
|
371
|
+
lvl.lvlJc = f.createJc({ val });
|
|
372
|
+
linkParents(lvl.lvlJc, lvl);
|
|
373
|
+
}
|
|
374
|
+
this.written();
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Office JS `list.setLevelStartingNumber(level, startingNumber)`: the level's `w:start`. A
|
|
378
|
+
* `w:startOverride` this `w:num` carries for the level would still win the first use, so it
|
|
379
|
+
* is removed with it.
|
|
380
|
+
*/
|
|
381
|
+
setLevelStartingNumber(level, startingNumber) {
|
|
382
|
+
var _a;
|
|
383
|
+
const lvl = this.writableLvl(level);
|
|
384
|
+
lvl.start = f.createLvlStart({ val: startingNumber });
|
|
385
|
+
linkParents(lvl.start, lvl);
|
|
386
|
+
for (const override of (_a = this.element.lvlOverride) !== null && _a !== void 0 ? _a : []) {
|
|
387
|
+
if (Number(override.ilvl) === level)
|
|
388
|
+
delete override.startOverride;
|
|
389
|
+
}
|
|
390
|
+
this.written();
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* A new `w:num` over this list's `w:abstractNum` with a `w:startOverride` on level 0, which
|
|
394
|
+
* is how Word restarts a list: the counters are shared by abstract definition, and the
|
|
395
|
+
* override is spent on the new `w:num`'s first use in a story (CR-001 section 15.2). Attach
|
|
396
|
+
* the paragraphs that are to start again to the list it returns.
|
|
397
|
+
*
|
|
398
|
+
* Extension name (Office JS has no restart verb); `Paragraph.restartList()` is the sugar.
|
|
399
|
+
*/
|
|
400
|
+
restart() {
|
|
401
|
+
var _a, _b, _c;
|
|
402
|
+
const numbering = this.live();
|
|
403
|
+
const num = this.element;
|
|
404
|
+
const start = (_b = (_a = this.level(0)) === null || _a === void 0 ? void 0 : _a.start) !== null && _b !== void 0 ? _b : 1;
|
|
405
|
+
const created = f.createNumberingNum({
|
|
406
|
+
numId: nextNumId(numbering),
|
|
407
|
+
abstractNumId: f.createNumberingNumAbstractNumId({ val: num.abstractNumId.val }),
|
|
408
|
+
lvlOverride: [f.createNumberingNumLvlOverride({
|
|
409
|
+
ilvl: 0,
|
|
410
|
+
startOverride: f.createNumberingNumLvlOverrideStartOverride({ val: start }),
|
|
411
|
+
})],
|
|
412
|
+
});
|
|
413
|
+
((_c = numbering.num) !== null && _c !== void 0 ? _c : (numbering.num = [])).push(created);
|
|
414
|
+
linkParents(created, numbering);
|
|
415
|
+
this.written();
|
|
416
|
+
return new List(String(created.numId), this.body);
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Gives this list a `w:abstractNum` of its own where another `w:num` shares the one it names
|
|
420
|
+
* (CR-002 section 3.9), so that a change to its levels stays local to it. The numbering part
|
|
421
|
+
* becomes live either way; the `setLevel*` methods call it for you.
|
|
422
|
+
*/
|
|
423
|
+
separate() {
|
|
424
|
+
var _a, _b, _c;
|
|
425
|
+
const numbering = this.live();
|
|
426
|
+
const num = this.element;
|
|
427
|
+
const abstractId = num.abstractNumId.val;
|
|
428
|
+
const shared = ((_a = numbering.num) !== null && _a !== void 0 ? _a : []).some((other) => { var _a; return other !== num && ((_a = other.abstractNumId) === null || _a === void 0 ? void 0 : _a.val) === abstractId; });
|
|
429
|
+
if (!shared)
|
|
430
|
+
return;
|
|
431
|
+
const source = ((_b = numbering.abstractNum) !== null && _b !== void 0 ? _b : []).find((a) => a.abstractNumId === abstractId);
|
|
432
|
+
if (source === undefined)
|
|
433
|
+
return;
|
|
434
|
+
const copy = deepCopy(source);
|
|
435
|
+
copy.abstractNumId = nextAbstractNumId(numbering);
|
|
436
|
+
copy.nsid = f.createCTLongHexNumber({ val: hexId() });
|
|
437
|
+
// w:styleLink maps a numbering style to one definition; the copy must not claim it too
|
|
438
|
+
delete copy.styleLink;
|
|
439
|
+
((_c = numbering.abstractNum) !== null && _c !== void 0 ? _c : (numbering.abstractNum = [])).push(copy);
|
|
440
|
+
linkParents(copy, numbering);
|
|
441
|
+
num.abstractNumId = f.createNumberingNumAbstractNumId({ val: copy.abstractNumId });
|
|
442
|
+
linkParents(num.abstractNumId, num);
|
|
443
|
+
this.written();
|
|
444
|
+
}
|
|
445
|
+
// --- internals --------------------------------------------------------------------------
|
|
446
|
+
/** The numbering part's live tree; the first write promotes the tree the definitions hold. */
|
|
447
|
+
live() {
|
|
448
|
+
const part = numberingPartOf(this.body);
|
|
449
|
+
if (part === undefined)
|
|
450
|
+
throw new Docx4JException('This document has no numbering part');
|
|
451
|
+
return part.makeLive();
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* The `w:lvl` a write goes to: this list's own abstract definition's, created where the
|
|
455
|
+
* definition has no level for it. A `w:lvlOverride/w:lvl` this `w:num` carries for the level
|
|
456
|
+
* would mask the abstract one, so it goes (the `w:startOverride` beside it stays).
|
|
457
|
+
*/
|
|
458
|
+
writableLvl(level) {
|
|
459
|
+
var _a, _b;
|
|
460
|
+
this.separate();
|
|
461
|
+
const abstract = this.abstractElement;
|
|
462
|
+
if (abstract === undefined) {
|
|
463
|
+
throw new ItemNotFound(`w:num ${this.numId} names no w:abstractNum that this document defines`);
|
|
464
|
+
}
|
|
465
|
+
for (const override of (_a = this.element.lvlOverride) !== null && _a !== void 0 ? _a : []) {
|
|
466
|
+
if (Number(override.ilvl) === level)
|
|
467
|
+
delete override.lvl;
|
|
468
|
+
}
|
|
469
|
+
const levels = ((_b = abstract.lvl) !== null && _b !== void 0 ? _b : (abstract.lvl = []));
|
|
470
|
+
let lvl = levels.find((l) => Number(l.ilvl) === level);
|
|
471
|
+
if (lvl === undefined) {
|
|
472
|
+
lvl = f.createLvl({ ilvl: level });
|
|
473
|
+
levels.push(lvl);
|
|
474
|
+
levels.sort((a, b) => Number(a.ilvl) - Number(b.ilvl));
|
|
475
|
+
linkParents(lvl, abstract);
|
|
476
|
+
}
|
|
477
|
+
return lvl;
|
|
478
|
+
}
|
|
479
|
+
/** The numbering tree changed: the resolver (and with it the definitions) is rebuilt. */
|
|
480
|
+
written() {
|
|
481
|
+
var _a;
|
|
482
|
+
(_a = packageOf(this.body)) === null || _a === void 0 ? void 0 : _a.refreshPropertyResolver();
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* A subset of Office JS `Word.ListItem` over one paragraph's place in its list. A view: the
|
|
487
|
+
* paragraph is the state.
|
|
488
|
+
*/
|
|
489
|
+
export class ListItem {
|
|
490
|
+
constructor(
|
|
491
|
+
/** The paragraph this is the list item of (Office JS: `paragraph.listItem`). */
|
|
492
|
+
paragraph) {
|
|
493
|
+
this.paragraph = paragraph;
|
|
494
|
+
}
|
|
495
|
+
/** Where this paragraph's numbering resolves to; throws when it is not a list item any more. */
|
|
496
|
+
get ref() {
|
|
497
|
+
const ref = numRefOf(this.paragraph);
|
|
498
|
+
if (ref === undefined)
|
|
499
|
+
throw new ItemNotFound('This paragraph is not a list item');
|
|
500
|
+
return ref;
|
|
501
|
+
}
|
|
502
|
+
/** The list this item is in. */
|
|
503
|
+
get list() {
|
|
504
|
+
return new List(this.ref.numId, this.paragraph.parentBody);
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Office JS `listItem.level`: the `w:ilvl` the paragraph counts at, a style-contributed level
|
|
508
|
+
* included. Setting it writes the paragraph's own `w:numPr/w:ilvl` (with the `w:numId` beside
|
|
509
|
+
* it where the list came from the style), through the accessor that records `w:pPrChange`
|
|
510
|
+
* while the package tracks changes.
|
|
511
|
+
*/
|
|
512
|
+
get level() {
|
|
513
|
+
var _a;
|
|
514
|
+
return Number((_a = this.ref.ilvl) !== null && _a !== void 0 ? _a : '0');
|
|
515
|
+
}
|
|
516
|
+
set level(value) {
|
|
517
|
+
var _a;
|
|
518
|
+
const ref = this.ref;
|
|
519
|
+
const numPr = this.paragraph.numPr();
|
|
520
|
+
if (((_a = numPr.numId) === null || _a === void 0 ? void 0 : _a.val) === undefined && ref.numId !== undefined) {
|
|
521
|
+
numPr.numId = f.createPPrBaseNumPrNumId({ val: Number(ref.numId) });
|
|
522
|
+
linkParents(numPr.numId, numPr);
|
|
523
|
+
}
|
|
524
|
+
numPr.ilvl = f.createPPrBaseNumPrIlvl({ val: value });
|
|
525
|
+
linkParents(numPr.ilvl, numPr);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Office JS `listItem.listString`: the label Word paints in front of the paragraph.
|
|
529
|
+
*
|
|
530
|
+
* It is counted by walking the paragraph's **story** from its start in document order
|
|
531
|
+
* (CR-001 section 15.2), so it costs one walk of the story per read; `Body.listLabels()`
|
|
532
|
+
* answers for every paragraph of the story in one walk.
|
|
533
|
+
*/
|
|
534
|
+
get listString() {
|
|
535
|
+
var _a, _b;
|
|
536
|
+
return (_b = (_a = labelOf(this.paragraph)) === null || _a === void 0 ? void 0 : _a.listString) !== null && _b !== void 0 ? _b : '';
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Office JS `listItem.siblingIndex`: the 0-based index of this item among the items of the
|
|
540
|
+
* same list and level, counted from where the level last restarted. The same walk as
|
|
541
|
+
* {@link listString}.
|
|
542
|
+
*/
|
|
543
|
+
get siblingIndex() {
|
|
544
|
+
var _a, _b;
|
|
545
|
+
return (_b = (_a = labelOf(this.paragraph)) === null || _a === void 0 ? void 0 : _a.siblingIndex) !== null && _b !== void 0 ? _b : 0;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Office JS `listItem.getAncestor(parentOnly)`: the nearest preceding paragraph of this list
|
|
549
|
+
* at a shallower level - the parent when `parentOnly` is true, which throws when the nearest
|
|
550
|
+
* one is not exactly one level up. Throws `ItemNotFound` when there is no ancestor.
|
|
551
|
+
*/
|
|
552
|
+
getAncestor(parentOnly = false) {
|
|
553
|
+
const level = this.level;
|
|
554
|
+
const items = this.list.paragraphs;
|
|
555
|
+
const at = items.findIndex((p) => p.p === this.paragraph.p);
|
|
556
|
+
for (let i = at - 1; i >= 0; i--) {
|
|
557
|
+
const other = levelOf(items[i]);
|
|
558
|
+
if (other === undefined || other >= level)
|
|
559
|
+
continue;
|
|
560
|
+
if (parentOnly && other !== level - 1)
|
|
561
|
+
break;
|
|
562
|
+
return items[i];
|
|
563
|
+
}
|
|
564
|
+
throw new ItemNotFound(`This list item (level ${level}) has no ${parentOnly ? 'parent' : 'ancestor'} in list ${this.list.id}`);
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Office JS `listItem.getDescendants(directChildrenOnly)`: the paragraphs of this list which
|
|
568
|
+
* follow this one at a deeper level, up to the next one at this level or shallower; only the
|
|
569
|
+
* ones one level down when `directChildrenOnly` is true.
|
|
570
|
+
*/
|
|
571
|
+
getDescendants(directChildrenOnly = false) {
|
|
572
|
+
const level = this.level;
|
|
573
|
+
const items = this.list.paragraphs;
|
|
574
|
+
const at = items.findIndex((p) => p.p === this.paragraph.p);
|
|
575
|
+
const out = [];
|
|
576
|
+
for (let i = at + 1; i < items.length; i++) {
|
|
577
|
+
const other = levelOf(items[i]);
|
|
578
|
+
if (other === undefined || other <= level)
|
|
579
|
+
break;
|
|
580
|
+
if (!directChildrenOnly || other === level + 1)
|
|
581
|
+
out.push(items[i]);
|
|
582
|
+
}
|
|
583
|
+
return out;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
// ------------------------------------------------------------- what Paragraph and Body call
|
|
587
|
+
/** The effective `w:ilvl` of a paragraph, or undefined when it is not a list item. */
|
|
588
|
+
export function levelOf(paragraph) {
|
|
589
|
+
var _a;
|
|
590
|
+
const ref = numRefOf(paragraph);
|
|
591
|
+
return ref === undefined ? undefined : Number((_a = ref.ilvl) !== null && _a !== void 0 ? _a : '0');
|
|
592
|
+
}
|
|
593
|
+
/** Office JS `paragraph.isListItem`: docx4j's `Emulator.numRefFor` answers it. */
|
|
594
|
+
export function isListItem(paragraph) {
|
|
595
|
+
return numRefOf(paragraph) !== undefined;
|
|
596
|
+
}
|
|
597
|
+
/** Office JS `paragraph.listItem`; throws `ItemNotFound` when the paragraph is not one. */
|
|
598
|
+
export function listItemOf(paragraph) {
|
|
599
|
+
if (!isListItem(paragraph)) {
|
|
600
|
+
throw new ItemNotFound('This paragraph is not a list item (listItemOrNullObject does not throw)');
|
|
601
|
+
}
|
|
602
|
+
return new ListItem(paragraph);
|
|
603
|
+
}
|
|
604
|
+
/** Office JS `paragraph.listItemOrNullObject`. */
|
|
605
|
+
export function listItemOrNullObjectOf(paragraph) {
|
|
606
|
+
return isListItem(paragraph) ? new ListItem(paragraph) : nullObject('ListItem');
|
|
607
|
+
}
|
|
608
|
+
/** Office JS `paragraph.list`; throws `ItemNotFound` when the paragraph is not a list item. */
|
|
609
|
+
export function listOf(paragraph) {
|
|
610
|
+
const ref = numRefOf(paragraph);
|
|
611
|
+
if (ref === undefined) {
|
|
612
|
+
throw new ItemNotFound('This paragraph is not a list item (listOrNullObject does not throw)');
|
|
613
|
+
}
|
|
614
|
+
return new List(ref.numId, paragraph.parentBody);
|
|
615
|
+
}
|
|
616
|
+
/** Office JS `paragraph.listOrNullObject`. */
|
|
617
|
+
export function listOrNullObjectOf(paragraph) {
|
|
618
|
+
const ref = numRefOf(paragraph);
|
|
619
|
+
return ref === undefined ? nullObject('List') : new List(ref.numId, paragraph.parentBody);
|
|
620
|
+
}
|
|
621
|
+
/** Office JS `body.lists`: every `w:num` a paragraph of this body names, in order of first use. */
|
|
622
|
+
export function listsOf(body) {
|
|
623
|
+
const emulator = numberingEmulatorOf(body);
|
|
624
|
+
if (emulator === undefined)
|
|
625
|
+
return [];
|
|
626
|
+
const seen = new Set();
|
|
627
|
+
const out = [];
|
|
628
|
+
for (const paragraph of body.paragraphs) {
|
|
629
|
+
const ref = emulator.numRefFor(paragraph.p.pPr);
|
|
630
|
+
if (ref.notNumbered || ref.numId === undefined || seen.has(ref.numId))
|
|
631
|
+
continue;
|
|
632
|
+
seen.add(ref.numId);
|
|
633
|
+
out.push(new List(ref.numId, body));
|
|
634
|
+
}
|
|
635
|
+
return out;
|
|
636
|
+
}
|
|
637
|
+
/** Office JS `paragraph.attachToList(listId, level)`: the paragraph's own `w:numPr`. */
|
|
638
|
+
export function attachToList(paragraph, listId, level = 0) {
|
|
639
|
+
const numPr = paragraph.numPr();
|
|
640
|
+
numPr.numId = f.createPPrBaseNumPrNumId({ val: Number(listId) });
|
|
641
|
+
numPr.ilvl = f.createPPrBaseNumPrIlvl({ val: level });
|
|
642
|
+
linkParents(numPr, paragraph.p.pPr);
|
|
643
|
+
return new List(String(listId), paragraph.parentBody);
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Office JS `paragraph.detachFromList()`: the paragraph's own `w:numPr` goes. Where its style
|
|
647
|
+
* still numbers it, a `w:numPr` with `w:numId` 0 takes its place, which is how Word switches an
|
|
648
|
+
* inherited list off (ECMA-376 17.9.18, and what `Emulator.resolve` reads).
|
|
649
|
+
*/
|
|
650
|
+
export function detachFromList(paragraph) {
|
|
651
|
+
var _a;
|
|
652
|
+
if (((_a = paragraph.p.pPr) === null || _a === void 0 ? void 0 : _a.numPr) !== undefined) {
|
|
653
|
+
delete paragraph.pPr().numPr;
|
|
654
|
+
}
|
|
655
|
+
if (!isListItem(paragraph))
|
|
656
|
+
return;
|
|
657
|
+
const numPr = paragraph.numPr();
|
|
658
|
+
numPr.numId = f.createPPrBaseNumPrNumId({ val: 0 });
|
|
659
|
+
linkParents(numPr.numId, numPr);
|
|
660
|
+
delete numPr.ilvl;
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Office JS `paragraph.startNewList()`: a `w:abstractNum` copied from docx4j's default
|
|
664
|
+
* definitions (its `numbering.xml` resource: `w:abstractNum` 1 is Word's decimal set and 0 its
|
|
665
|
+
* bullet set), a `w:num` for it, and the paragraph attached to that list.
|
|
666
|
+
*
|
|
667
|
+
* Asynchronous where Office JS's is synchronous: the default definitions are XML which has to
|
|
668
|
+
* be unmarshalled, and a document with no numbering part gets one here (CR-002 section 17).
|
|
669
|
+
*/
|
|
670
|
+
export async function startNewList(paragraph, options) {
|
|
671
|
+
var _a, _b, _c, _d, _e;
|
|
672
|
+
const body = paragraph.parentBody;
|
|
673
|
+
const part = await ensureNumberingPart(body);
|
|
674
|
+
const numbering = await part.getContents();
|
|
675
|
+
const defaults = await defaultNumbering();
|
|
676
|
+
const template = ((_a = defaults.abstractNum) !== null && _a !== void 0 ? _a : [])[(options === null || options === void 0 ? void 0 : options.bullet) === true ? 0 : 1];
|
|
677
|
+
if (template === undefined)
|
|
678
|
+
throw new Docx4JException('The default numbering definitions are missing an abstractNum');
|
|
679
|
+
const abstract = deepCopy(template);
|
|
680
|
+
abstract.abstractNumId = nextAbstractNumId(numbering);
|
|
681
|
+
abstract.nsid = f.createCTLongHexNumber({ val: hexId() });
|
|
682
|
+
delete abstract.styleLink;
|
|
683
|
+
delete abstract.numStyleLink;
|
|
684
|
+
((_b = numbering.abstractNum) !== null && _b !== void 0 ? _b : (numbering.abstractNum = [])).push(abstract);
|
|
685
|
+
linkParents(abstract, numbering);
|
|
686
|
+
const num = f.createNumberingNum({
|
|
687
|
+
numId: nextNumId(numbering),
|
|
688
|
+
abstractNumId: f.createNumberingNumAbstractNumId({ val: abstract.abstractNumId }),
|
|
689
|
+
});
|
|
690
|
+
((_c = numbering.num) !== null && _c !== void 0 ? _c : (numbering.num = [])).push(num);
|
|
691
|
+
linkParents(num, numbering);
|
|
692
|
+
(_d = packageOf(body)) === null || _d === void 0 ? void 0 : _d.refreshPropertyResolver();
|
|
693
|
+
return attachToList(paragraph, num.numId, (_e = options === null || options === void 0 ? void 0 : options.level) !== null && _e !== void 0 ? _e : 0);
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* The numbering part, created with docx4j's default numbering where the document has none
|
|
697
|
+
* (docx4j's own `createNumbering` idiom), and unmarshalled either way so that what is written
|
|
698
|
+
* to it is saved.
|
|
699
|
+
*/
|
|
700
|
+
async function ensureNumberingPart(body) {
|
|
701
|
+
const pkg = packageOf(body);
|
|
702
|
+
const main = mainOf(body);
|
|
703
|
+
if (pkg === undefined || main === undefined) {
|
|
704
|
+
throw new Docx4JException('This body has no package, so a list cannot be added to it');
|
|
705
|
+
}
|
|
706
|
+
const existing = main.numberingDefinitionsPart;
|
|
707
|
+
if (existing !== undefined) {
|
|
708
|
+
// the resolver read the part privately; unmarshalling it gives a second tree, so the
|
|
709
|
+
// definitions (and the `getInd` the resolver folds into every layer) are rebuilt over the
|
|
710
|
+
// live one
|
|
711
|
+
const before = existing.isUnmarshalled;
|
|
712
|
+
await existing.getContents();
|
|
713
|
+
if (!before)
|
|
714
|
+
pkg.refreshPropertyResolver();
|
|
715
|
+
return existing;
|
|
716
|
+
}
|
|
717
|
+
// Imported here so that the content API does not import the parts statically: parts/wml
|
|
718
|
+
// builds the Body views, so a static import would be a runtime cycle (as `ooxml.mts` does).
|
|
719
|
+
const { NumberingDefinitionsPart } = await import('../../parts/wml/index.mjs');
|
|
720
|
+
const created = new NumberingDefinitionsPart();
|
|
721
|
+
main.addTargetPart(created);
|
|
722
|
+
await created.unmarshalDefaultNumbering();
|
|
723
|
+
// the resolver was built before the part existed, and refreshPropertyResolver() cannot find
|
|
724
|
+
// a part added since (CR-001 section 15.2 departure 6)
|
|
725
|
+
await pkg.refresh();
|
|
726
|
+
return created;
|
|
727
|
+
}
|
|
728
|
+
// ----------------------------------------------------------------------------- the mechanics
|
|
729
|
+
const TWIPS_PER_POINT = 20;
|
|
730
|
+
/** Office JS `Word.ListNumbering` as `w:numFmt`. */
|
|
731
|
+
const NUMBERING_FORMATS = {
|
|
732
|
+
None: 'none',
|
|
733
|
+
Arabic: 'decimal',
|
|
734
|
+
UpperRoman: 'upperRoman',
|
|
735
|
+
LowerRoman: 'lowerRoman',
|
|
736
|
+
UpperLetter: 'upperLetter',
|
|
737
|
+
LowerLetter: 'lowerLetter',
|
|
738
|
+
};
|
|
739
|
+
/** Office JS `Word.ListBullet` as the character and face Word's bullet library uses. */
|
|
740
|
+
const BULLETS = {
|
|
741
|
+
Custom: undefined,
|
|
742
|
+
Solid: { charCode: 0xf0b7, fontName: 'Symbol' }, // a filled round bullet
|
|
743
|
+
Hollow: { charCode: 0x006f, fontName: 'Courier New' }, // a lower-case o
|
|
744
|
+
Square: { charCode: 0xf0a7, fontName: 'Wingdings' }, // a filled square
|
|
745
|
+
Diamonds: { charCode: 0xf076, fontName: 'Wingdings' }, // a four-diamond bullet
|
|
746
|
+
Arrow: { charCode: 0xf0d8, fontName: 'Wingdings' }, // a solid right-pointing arrowhead
|
|
747
|
+
Checkmark: { charCode: 0xf0fc, fontName: 'Wingdings' }, // a tick
|
|
748
|
+
};
|
|
749
|
+
/** `w:lvlJc`, which takes the same values as a paragraph's `w:jc`. */
|
|
750
|
+
const LVL_JC = {
|
|
751
|
+
Left: 'left', Centered: 'center', Right: 'right', Justified: 'both', Unknown: undefined,
|
|
752
|
+
};
|
|
753
|
+
/** Office JS's `formatString` as a `w:lvlText`: a number is a 0-based level, a string a literal. */
|
|
754
|
+
function lvlTextOf(level, formatString) {
|
|
755
|
+
if (formatString === undefined || formatString.length === 0)
|
|
756
|
+
return `%${level + 1}.`;
|
|
757
|
+
return formatString.map((part) => (typeof part === 'number' ? `%${part + 1}` : part)).join('');
|
|
758
|
+
}
|
|
759
|
+
/** A free `w:numId`: one past the highest the document uses (Word numbers from 1). */
|
|
760
|
+
function nextNumId(numbering) {
|
|
761
|
+
var _a;
|
|
762
|
+
return ((_a = numbering.num) !== null && _a !== void 0 ? _a : []).reduce((max, num) => Math.max(max, Number(num.numId)), 0) + 1;
|
|
763
|
+
}
|
|
764
|
+
/** A free `w:abstractNumId`. */
|
|
765
|
+
function nextAbstractNumId(numbering) {
|
|
766
|
+
var _a;
|
|
767
|
+
return ((_a = numbering.abstractNum) !== null && _a !== void 0 ? _a : []).reduce((max, a) => Math.max(max, Number(a.abstractNumId)), -1) + 1;
|
|
768
|
+
}
|
|
769
|
+
/** An eight-digit `w:nsid`, which is how Word tells two list families apart. */
|
|
770
|
+
function hexId() {
|
|
771
|
+
return Math.floor(Math.random() * 0x7fffffff).toString(16).toUpperCase().padStart(8, '0');
|
|
772
|
+
}
|
|
773
|
+
/** docx4j's default numbering definitions, unmarshalled once. */
|
|
774
|
+
let defaults;
|
|
775
|
+
function defaultNumbering() {
|
|
776
|
+
defaults !== null && defaults !== void 0 ? defaults : (defaults = (async () => (await unmarshalNode(parseXml(DEFAULT_NUMBERING_XML))).value)());
|
|
777
|
+
return defaults;
|
|
778
|
+
}
|