@docx4j/core-ts 0.3.2 → 0.5.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 +8 -3
- package/dist/html/convert.d.mts +116 -0
- package/dist/html/convert.mjs +565 -0
- package/dist/html/elements.d.mts +19 -0
- package/dist/html/elements.mjs +182 -0
- package/dist/html/index.d.mts +2 -0
- package/dist/html/index.mjs +4 -0
- package/dist/model/content/Body.d.mts +13 -1
- package/dist/model/content/Body.mjs +97 -19
- package/dist/model/content/Comment.mjs +7 -0
- package/dist/model/content/InlinePicture.d.mts +89 -10
- package/dist/model/content/InlinePicture.mjs +245 -35
- package/dist/model/content/List.mjs +10 -1
- package/dist/model/content/Paragraph.d.mts +28 -5
- package/dist/model/content/Paragraph.mjs +77 -37
- package/dist/model/content/Range.d.mts +18 -2
- package/dist/model/content/Range.mjs +194 -64
- package/dist/model/content/Table.d.mts +38 -0
- package/dist/model/content/Table.mjs +80 -2
- package/dist/model/content/TrackedChange.d.mts +129 -3
- package/dist/model/content/TrackedChange.mjs +577 -12
- package/dist/model/content/binder.d.mts +2 -1
- package/dist/model/content/binder.mjs +48 -1
- package/dist/model/content/index.d.mts +4 -3
- package/dist/model/content/index.mjs +3 -2
- package/dist/model/content/relationships.d.mts +30 -0
- package/dist/model/content/relationships.mjs +133 -0
- package/dist/model/content/stylesFacade.d.mts +4 -2
- package/dist/model/content/stylesFacade.mjs +20 -6
- package/dist/model/content/tableColumns.d.mts +48 -0
- package/dist/model/content/tableColumns.mjs +484 -0
- package/dist/model/content/tree.d.mts +52 -2
- package/dist/model/content/tree.mjs +48 -3
- package/dist/model/customxml/CustomXmlPartCollection.d.mts +6 -1
- package/dist/model/customxml/CustomXmlPartCollection.mjs +19 -3
- package/dist/model/customxml/bindings.d.mts +5 -2
- package/dist/model/customxml/bindings.mjs +21 -6
- package/dist/model/customxml/index.d.mts +3 -0
- package/dist/model/customxml/index.mjs +3 -0
- package/dist/model/customxml/opendope.d.mts +32 -0
- package/dist/model/customxml/opendope.mjs +56 -0
- package/dist/model/customxml/pictures.d.mts +57 -0
- package/dist/model/customxml/pictures.mjs +192 -0
- package/dist/model/customxml/xhtml.d.mts +42 -0
- package/dist/model/customxml/xhtml.mjs +162 -0
- package/dist/model/fonts/families.generated.mjs +2 -2
- package/dist/model/fonts/registry.mjs +7 -1
- package/dist/model/fonts/substitutions.generated.mjs +2 -2
- package/dist/model/fonts/themes.generated.mjs +1 -1
- package/dist/model/listnumbering/definitions.mjs +18 -0
- package/dist/model/properties/PropertyResolver.d.mts +27 -6
- package/dist/model/properties/PropertyResolver.mjs +83 -16
- package/dist/model/properties/catalogue.d.mts +15 -1
- package/dist/model/properties/catalogue.mjs +80 -27
- package/dist/model/properties/table.d.mts +16 -2
- package/dist/model/properties/table.mjs +13 -3
- package/dist/opc/Load.d.mts +9 -2
- package/dist/opc/index.d.mts +2 -2
- package/dist/opc/index.mjs +2 -2
- package/dist/opc/mce/preprocessor.d.mts +62 -6
- package/dist/opc/mce/preprocessor.mjs +142 -35
- package/dist/opc/mce/understood.d.mts +10 -0
- package/dist/opc/mce/understood.mjs +25 -3
- package/dist/packages/OpcPackage.d.mts +7 -0
- package/dist/packages/OpcPackage.mjs +15 -3
- package/dist/packages/WordprocessingMLPackage.d.mts +35 -2
- package/dist/packages/WordprocessingMLPackage.mjs +99 -2
- package/dist/parts/XmlPart.d.mts +26 -5
- package/dist/parts/XmlPart.mjs +67 -18
- package/dist/parts/wml/knownStyles.d.mts +1 -0
- package/dist/parts/wml/knownStyles.mjs +2233 -0
- package/package.json +7 -2
|
@@ -1,15 +1,71 @@
|
|
|
1
1
|
import type { XmlPart } from '../../parts/XmlPart.mjs';
|
|
2
2
|
export declare const MCE_NS = "http://schemas.openxmlformats.org/markup-compatibility/2006";
|
|
3
|
+
/**
|
|
4
|
+
* The hosts whose mc:AlternateContent is kept whole on load, as `{namespace}localName`
|
|
5
|
+
* (CR-010 phase 1): a chart's `c:chartSpace` and SpreadsheetML's `workbook`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const KEPT_HOSTS: ReadonlySet<string>;
|
|
3
8
|
export interface McePreprocessOptions {
|
|
4
9
|
/** Namespaces counted as understood; defaults to the object model's modules. */
|
|
5
10
|
understood?: ReadonlySet<string>;
|
|
11
|
+
/** Hosts, as `{namespace}localName`, whose mc:AlternateContent is left whole; defaults to `KEPT_HOSTS`. */
|
|
12
|
+
keptHosts?: ReadonlySet<string>;
|
|
13
|
+
/**
|
|
14
|
+
* False leaves every element whole and reports what resolving would (CR-010 phase 4): the same
|
|
15
|
+
* warnings, from the same elements, and no change to the DOM. Defaults to true.
|
|
16
|
+
*/
|
|
17
|
+
resolve?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* What load reports about a part (CR-010 phase 1; the text extraction paper's warning channel).
|
|
21
|
+
* `mce.branches_differ`: an mc:AlternateContent whose branches' text differs, so a reader that
|
|
22
|
+
* takes another branch than this package's reads something else. `selected` is the branch this
|
|
23
|
+
* package took (`Choice(wps)`, `Fallback`); `branches` gives every branch's text, in document
|
|
24
|
+
* order, as the w:t, a:t and m:t text it holds, with whitespace collapsed.
|
|
25
|
+
*/
|
|
26
|
+
export interface McBranchesDifferWarning {
|
|
27
|
+
code: 'mce.branches_differ';
|
|
28
|
+
partName: string | undefined;
|
|
29
|
+
/** Where the element starts in the part, where the DOM records it (xmldom does; a browser's DOMParser does not). */
|
|
30
|
+
line?: number;
|
|
31
|
+
column?: number;
|
|
32
|
+
selected: string;
|
|
33
|
+
branches: {
|
|
34
|
+
branch: string;
|
|
35
|
+
text: string;
|
|
36
|
+
}[];
|
|
37
|
+
message: string;
|
|
38
|
+
}
|
|
39
|
+
/** A warning load records on a part. */
|
|
40
|
+
export type PartWarning = McBranchesDifferWarning;
|
|
41
|
+
/**
|
|
42
|
+
* What one pass over a DOM did. `kept` counts the elements it met and left whole; it doesn't enter a
|
|
43
|
+
* branch the rule doesn't select, so an element nested there is not counted.
|
|
44
|
+
*/
|
|
45
|
+
export interface MceResult {
|
|
46
|
+
resolved: number;
|
|
47
|
+
kept: number;
|
|
48
|
+
warnings: PartWarning[];
|
|
6
49
|
}
|
|
7
|
-
/** The default preprocessor, over the object model's namespaces. */
|
|
8
|
-
export declare function mcePreprocess(doc: Document, _part?: XmlPart<unknown>): void;
|
|
9
|
-
/** A preprocessor over a namespace set of your own. */
|
|
10
|
-
export declare function createMcePreprocessor(options: McePreprocessOptions): (doc: Document, part?: XmlPart<unknown>) => void;
|
|
11
50
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
51
|
+
* Load's preprocessor since CR-010 phase 4: every mc:AlternateContent is kept, and the part's
|
|
52
|
+
* warnings are what resolving would report. Returns them.
|
|
53
|
+
*/
|
|
54
|
+
export declare function mceKeep(doc: Document, part?: XmlPart<unknown>): PartWarning[];
|
|
55
|
+
/** The resolving preprocessor (`mcePreprocess: true`), over the object model's namespaces. Returns the part's warnings. */
|
|
56
|
+
export declare function mcePreprocess(doc: Document, part?: XmlPart<unknown>): PartWarning[];
|
|
57
|
+
/** A preprocessor over a namespace set (or kept hosts) of your own. */
|
|
58
|
+
export declare function createMcePreprocessor(options: McePreprocessOptions): (doc: Document, part?: XmlPart<unknown>) => PartWarning[];
|
|
59
|
+
/**
|
|
60
|
+
* Resolves every mc:AlternateContent under a node, outermost first, but those `KEPT_HOSTS` and the
|
|
61
|
+
* nothing-selectable rule keep (see the file comment). Returns the number resolved.
|
|
14
62
|
*/
|
|
15
63
|
export declare function resolveAlternateContent(root: Node, understood?: ReadonlySet<string>): number;
|
|
64
|
+
/**
|
|
65
|
+
* The pass itself: what it resolved, kept and reported. With `resolve: false` it visits the elements
|
|
66
|
+
* resolving would, outermost first and into the branch the rule selects, reports the same warnings,
|
|
67
|
+
* and leaves the DOM as it was.
|
|
68
|
+
*/
|
|
69
|
+
export declare function preprocessAlternateContent(root: Node, options?: McePreprocessOptions & {
|
|
70
|
+
partName?: string;
|
|
71
|
+
}): MceResult;
|
|
@@ -1,42 +1,97 @@
|
|
|
1
1
|
import { UNDERSTOOD_NAMESPACES } from './understood.mjs';
|
|
2
2
|
export const MCE_NS = 'http://schemas.openxmlformats.org/markup-compatibility/2006';
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/**
|
|
4
|
+
* The hosts whose mc:AlternateContent is kept whole on load, as `{namespace}localName`
|
|
5
|
+
* (CR-010 phase 1): a chart's `c:chartSpace` and SpreadsheetML's `workbook`.
|
|
6
|
+
*/
|
|
7
|
+
export const KEPT_HOSTS = new Set([
|
|
8
|
+
'{http://schemas.openxmlformats.org/drawingml/2006/chart}chartSpace',
|
|
9
|
+
'{http://schemas.openxmlformats.org/spreadsheetml/2006/main}workbook',
|
|
10
|
+
]);
|
|
11
|
+
/**
|
|
12
|
+
* Load's preprocessor since CR-010 phase 4: every mc:AlternateContent is kept, and the part's
|
|
13
|
+
* warnings are what resolving would report. Returns them.
|
|
14
|
+
*/
|
|
15
|
+
export function mceKeep(doc, part) {
|
|
16
|
+
return preprocessAlternateContent(doc, { resolve: false, partName: part === null || part === void 0 ? void 0 : part.partName.name }).warnings;
|
|
6
17
|
}
|
|
7
|
-
/**
|
|
18
|
+
/** The resolving preprocessor (`mcePreprocess: true`), over the object model's namespaces. Returns the part's warnings. */
|
|
19
|
+
export function mcePreprocess(doc, part) {
|
|
20
|
+
return preprocessAlternateContent(doc, { partName: part === null || part === void 0 ? void 0 : part.partName.name }).warnings;
|
|
21
|
+
}
|
|
22
|
+
/** A preprocessor over a namespace set (or kept hosts) of your own. */
|
|
8
23
|
export function createMcePreprocessor(options) {
|
|
9
|
-
|
|
10
|
-
const understood = (_a = options.understood) !== null && _a !== void 0 ? _a : UNDERSTOOD_NAMESPACES;
|
|
11
|
-
return (doc) => resolveAlternateContent(doc, understood);
|
|
24
|
+
return (doc, part) => preprocessAlternateContent(doc, { ...options, partName: part === null || part === void 0 ? void 0 : part.partName.name }).warnings;
|
|
12
25
|
}
|
|
13
26
|
/**
|
|
14
|
-
* Resolves every mc:AlternateContent under a node,
|
|
15
|
-
*
|
|
27
|
+
* Resolves every mc:AlternateContent under a node, outermost first, but those `KEPT_HOSTS` and the
|
|
28
|
+
* nothing-selectable rule keep (see the file comment). Returns the number resolved.
|
|
16
29
|
*/
|
|
17
30
|
export function resolveAlternateContent(root, understood = UNDERSTOOD_NAMESPACES) {
|
|
18
|
-
|
|
31
|
+
return preprocessAlternateContent(root, { understood }).resolved;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The pass itself: what it resolved, kept and reported. With `resolve: false` it visits the elements
|
|
35
|
+
* resolving would, outermost first and into the branch the rule selects, reports the same warnings,
|
|
36
|
+
* and leaves the DOM as it was.
|
|
37
|
+
*/
|
|
38
|
+
export function preprocessAlternateContent(root, options = {}) {
|
|
39
|
+
var _a, _b, _c;
|
|
40
|
+
const understood = (_a = options.understood) !== null && _a !== void 0 ? _a : UNDERSTOOD_NAMESPACES;
|
|
41
|
+
const keptHosts = (_b = options.keptHosts) !== null && _b !== void 0 ? _b : KEPT_HOSTS;
|
|
42
|
+
const resolve = (_c = options.resolve) !== null && _c !== void 0 ? _c : true;
|
|
43
|
+
const result = { resolved: 0, kept: 0, warnings: [] };
|
|
19
44
|
const visit = (node) => {
|
|
20
|
-
|
|
21
|
-
const
|
|
45
|
+
var _a;
|
|
46
|
+
const hostKept = node.nodeType === 1 && keptHosts.has(`{${(_a = node.namespaceURI) !== null && _a !== void 0 ? _a : ''}}${node.localName}`);
|
|
47
|
+
// A work list, not the live child list: resolving replaces a child with the branch's content,
|
|
48
|
+
// which is visited in its turn (it may hold, or be, another mc:AlternateContent).
|
|
49
|
+
const queue = [];
|
|
22
50
|
for (let c = node.firstChild; c; c = c.nextSibling)
|
|
23
|
-
|
|
24
|
-
for (
|
|
25
|
-
if (
|
|
51
|
+
queue.push(c);
|
|
52
|
+
for (let i = 0; i < queue.length; i++) {
|
|
53
|
+
if (queue[i].nodeType !== 1)
|
|
54
|
+
continue;
|
|
55
|
+
const el = queue[i];
|
|
56
|
+
if (!isAlternateContent(el)) {
|
|
57
|
+
visit(el);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const { choices, fallback } = branchesOf(el);
|
|
61
|
+
const chosen = choices.find((c) => choiceUnderstood(c, understood));
|
|
62
|
+
const branch = chosen !== null && chosen !== void 0 ? chosen : fallback;
|
|
63
|
+
if (hostKept || (branch === undefined && choices.length > 0)) {
|
|
64
|
+
result.kept++;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const warning = branchesDiffer(el, choices, fallback, branch, understood, options.partName);
|
|
68
|
+
if (warning)
|
|
69
|
+
result.warnings.push(warning);
|
|
70
|
+
if (!resolve) {
|
|
71
|
+
result.kept++;
|
|
72
|
+
if (branch)
|
|
73
|
+
visit(branch);
|
|
26
74
|
continue;
|
|
27
|
-
visit(child);
|
|
28
|
-
const el = child;
|
|
29
|
-
if (el.namespaceURI === MCE_NS && el.localName === 'AlternateContent') {
|
|
30
|
-
replaceAlternateContent(el, understood);
|
|
31
|
-
count++;
|
|
32
75
|
}
|
|
76
|
+
const moved = [];
|
|
77
|
+
if (branch)
|
|
78
|
+
for (let c = branch.firstChild; c; c = c.nextSibling)
|
|
79
|
+
moved.push(c);
|
|
80
|
+
for (const n of moved)
|
|
81
|
+
node.insertBefore(n, el);
|
|
82
|
+
node.removeChild(el);
|
|
83
|
+
queue.splice(i + 1, 0, ...moved);
|
|
84
|
+
result.resolved++;
|
|
33
85
|
}
|
|
34
86
|
};
|
|
35
87
|
visit(root);
|
|
36
|
-
return
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
function isAlternateContent(el) {
|
|
91
|
+
return el.namespaceURI === MCE_NS && el.localName === 'AlternateContent';
|
|
37
92
|
}
|
|
38
|
-
function
|
|
39
|
-
|
|
93
|
+
function branchesOf(ac) {
|
|
94
|
+
const choices = [];
|
|
40
95
|
let fallback;
|
|
41
96
|
for (let c = ac.firstChild; c; c = c.nextSibling) {
|
|
42
97
|
if (c.nodeType !== 1)
|
|
@@ -44,21 +99,12 @@ function replaceAlternateContent(ac, understood) {
|
|
|
44
99
|
const el = c;
|
|
45
100
|
if (el.namespaceURI !== MCE_NS)
|
|
46
101
|
continue;
|
|
47
|
-
if (el.localName === 'Choice'
|
|
48
|
-
|
|
102
|
+
if (el.localName === 'Choice')
|
|
103
|
+
choices.push(el);
|
|
49
104
|
else if (el.localName === 'Fallback' && !fallback)
|
|
50
105
|
fallback = el;
|
|
51
106
|
}
|
|
52
|
-
|
|
53
|
-
const parent = ac.parentNode;
|
|
54
|
-
if (branch) {
|
|
55
|
-
const moved = [];
|
|
56
|
-
for (let c = branch.firstChild; c; c = c.nextSibling)
|
|
57
|
-
moved.push(c);
|
|
58
|
-
for (const n of moved)
|
|
59
|
-
parent.insertBefore(n, ac);
|
|
60
|
-
}
|
|
61
|
-
parent.removeChild(ac);
|
|
107
|
+
return { choices, fallback };
|
|
62
108
|
}
|
|
63
109
|
function choiceUnderstood(choice, understood) {
|
|
64
110
|
var _a;
|
|
@@ -72,3 +118,64 @@ function choiceUnderstood(choice, understood) {
|
|
|
72
118
|
}
|
|
73
119
|
return true;
|
|
74
120
|
}
|
|
121
|
+
/** The text elements a branch's text is read from: WordprocessingML's, DrawingML's and OMML's `t`. */
|
|
122
|
+
const TEXT_NAMESPACES = new Set([
|
|
123
|
+
'http://schemas.openxmlformats.org/wordprocessingml/2006/main',
|
|
124
|
+
'http://schemas.openxmlformats.org/drawingml/2006/main',
|
|
125
|
+
'http://schemas.openxmlformats.org/officeDocument/2006/math',
|
|
126
|
+
]);
|
|
127
|
+
/**
|
|
128
|
+
* The text a branch holds: its w:t, a:t and m:t, whitespace collapsed. A nested
|
|
129
|
+
* mc:AlternateContent contributes the branch the same rule selects, or its first Choice when
|
|
130
|
+
* nothing is selectable (the reading rule).
|
|
131
|
+
*/
|
|
132
|
+
function textOfBranch(node, understood) {
|
|
133
|
+
const out = [];
|
|
134
|
+
const walk = (n) => {
|
|
135
|
+
var _a, _b, _c, _d;
|
|
136
|
+
for (let c = n.firstChild; c; c = c.nextSibling) {
|
|
137
|
+
if (c.nodeType !== 1)
|
|
138
|
+
continue;
|
|
139
|
+
const el = c;
|
|
140
|
+
if (isAlternateContent(el)) {
|
|
141
|
+
const { choices, fallback } = branchesOf(el);
|
|
142
|
+
const branch = (_b = (_a = choices.find((x) => choiceUnderstood(x, understood))) !== null && _a !== void 0 ? _a : fallback) !== null && _b !== void 0 ? _b : choices[0];
|
|
143
|
+
if (branch)
|
|
144
|
+
walk(branch);
|
|
145
|
+
}
|
|
146
|
+
else if (el.localName === 't' && TEXT_NAMESPACES.has((_c = el.namespaceURI) !== null && _c !== void 0 ? _c : ''))
|
|
147
|
+
out.push((_d = el.textContent) !== null && _d !== void 0 ? _d : '');
|
|
148
|
+
else
|
|
149
|
+
walk(el);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
walk(node);
|
|
153
|
+
return out.join('').replace(/\s+/g, ' ').trim();
|
|
154
|
+
}
|
|
155
|
+
function branchName(branch) {
|
|
156
|
+
var _a;
|
|
157
|
+
return branch.localName === 'Choice' ? `Choice(${((_a = branch.getAttribute('Requires')) !== null && _a !== void 0 ? _a : '').trim()})` : 'Fallback';
|
|
158
|
+
}
|
|
159
|
+
function branchesDiffer(ac, choices, fallback, selected, understood, partName) {
|
|
160
|
+
const all = fallback ? [...choices, fallback] : choices;
|
|
161
|
+
if (all.length < 2 || selected === undefined)
|
|
162
|
+
return undefined;
|
|
163
|
+
const branches = all.map((b) => ({ branch: branchName(b), text: textOfBranch(b, understood) }));
|
|
164
|
+
const taken = branches[all.indexOf(selected)];
|
|
165
|
+
if (branches.every((b) => b.text === taken.text))
|
|
166
|
+
return undefined;
|
|
167
|
+
const located = ac;
|
|
168
|
+
const where = typeof located.lineNumber === 'number' ? ` (line ${located.lineNumber}, column ${located.columnNumber})` : '';
|
|
169
|
+
const others = branches.filter((b) => b !== taken).map((b) => `${b.branch} ${JSON.stringify(abbreviate(b.text))}`).join(', ');
|
|
170
|
+
return {
|
|
171
|
+
code: 'mce.branches_differ',
|
|
172
|
+
partName,
|
|
173
|
+
...(typeof located.lineNumber === 'number' ? { line: located.lineNumber, column: located.columnNumber } : {}),
|
|
174
|
+
selected: taken.branch,
|
|
175
|
+
branches,
|
|
176
|
+
message: `${partName !== null && partName !== void 0 ? partName : 'a part'}${where}: mc:AlternateContent's branches say different things; ${taken.branch} was read (${JSON.stringify(abbreviate(taken.text))}), not ${others}`,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function abbreviate(text) {
|
|
180
|
+
return text.length > 60 ? `${text.slice(0, 57)}...` : text;
|
|
181
|
+
}
|
|
@@ -1 +1,11 @@
|
|
|
1
1
|
export declare const UNDERSTOOD_NAMESPACES: ReadonlySet<string>;
|
|
2
|
+
/**
|
|
3
|
+
* The conventional prefixes of `UNDERSTOOD_NAMESPACES` (`NAMESPACE_PREFIXES`), which is what a reader of a
|
|
4
|
+
* kept mc:AlternateContent matches `mc:Choice/@Requires` against (CR-010 section 11.5): the unmarshalled
|
|
5
|
+
* model does not keep the declarations in force at the element, so the objects package's `mcBranchOf`
|
|
6
|
+
* compares prefixes, and its default set, every prefix `NAMESPACE_PREFIXES` knows, is wider than what
|
|
7
|
+
* load understands. Through this set a reader takes the branch the preprocessor would have resolved to,
|
|
8
|
+
* wherever the document writes the conventional prefixes. An understood namespace with no conventional
|
|
9
|
+
* prefix has no entry.
|
|
10
|
+
*/
|
|
11
|
+
export declare const UNDERSTOOD_PREFIXES: ReadonlySet<string>;
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
//
|
|
5
5
|
// It is the defaultElementNamespaceURI of every generated module of @docx4j/generated-objects-ts
|
|
6
6
|
// (0.3.0), which `mce.test.mjs` asserts so that the next regeneration announces an addition, plus
|
|
7
|
-
//
|
|
8
|
-
// the model binds anyway (attributes, or elements a module of another namespace declares),
|
|
7
|
+
// three groups of hand-kept entries, each marked below: namespaces no module *defaults* to but which
|
|
8
|
+
// the model binds anyway (attributes, or elements a module of another namespace declares), the
|
|
9
9
|
// slicer namespaces of CR-004 phase B, whose content the model does not bind at all but carries
|
|
10
|
-
// through as DOM
|
|
10
|
+
// through as DOM, and the chartex namespaces Office gates a chart's frame with, which nothing in the
|
|
11
|
+
// Choice uses (CR-009 section 8).
|
|
12
|
+
import { NAMESPACE_PREFIXES } from '@docx4j/generated-objects-ts';
|
|
11
13
|
export const UNDERSTOOD_NAMESPACES = new Set([
|
|
12
14
|
'http://schemas.openxmlformats.org/wordprocessingml/2006/main',
|
|
13
15
|
'http://schemas.openxmlformats.org/package/2006/relationships',
|
|
@@ -124,4 +126,24 @@ export const UNDERSTOOD_NAMESPACES = new Set([
|
|
|
124
126
|
'http://schemas.microsoft.com/office/drawing/2010/slicer',
|
|
125
127
|
'http://schemas.microsoft.com/office/drawing/2012/slicer',
|
|
126
128
|
'http://schemas.microsoft.com/office/drawing/2012/timeslicer',
|
|
129
|
+
// Office's chartex charts (CR-009 section 8). Word, Excel and PowerPoint gate a chartex frame with
|
|
130
|
+
// a later chartex namespace than the chart's own: Requires="cx1" (2015/9/8) for a waterfall or a
|
|
131
|
+
// histogram, "cx2" (2015/10/21) for a funnel. Nothing in the Choice is in it: the frame's
|
|
132
|
+
// a:graphicData holds a cx:chart of the 2014 namespace, which the model binds. Nothing binds these
|
|
133
|
+
// two (docx4j has no schema for them), but the Choice round-trips whole, and the Fallback is only a
|
|
134
|
+
// picture of the chart, or in Excel a box saying the chart is not available, so leaving it would
|
|
135
|
+
// lose the chart on any save that re-marshals the part. Office's other chartex namespaces are not
|
|
136
|
+
// here: no file seen gates a Choice with one.
|
|
137
|
+
'http://schemas.microsoft.com/office/drawing/2015/9/8/chartex',
|
|
138
|
+
'http://schemas.microsoft.com/office/drawing/2015/10/21/chartex',
|
|
127
139
|
]);
|
|
140
|
+
/**
|
|
141
|
+
* The conventional prefixes of `UNDERSTOOD_NAMESPACES` (`NAMESPACE_PREFIXES`), which is what a reader of a
|
|
142
|
+
* kept mc:AlternateContent matches `mc:Choice/@Requires` against (CR-010 section 11.5): the unmarshalled
|
|
143
|
+
* model does not keep the declarations in force at the element, so the objects package's `mcBranchOf`
|
|
144
|
+
* compares prefixes, and its default set, every prefix `NAMESPACE_PREFIXES` knows, is wider than what
|
|
145
|
+
* load understands. Through this set a reader takes the branch the preprocessor would have resolved to,
|
|
146
|
+
* wherever the document writes the conventional prefixes. An understood namespace with no conventional
|
|
147
|
+
* prefix has no entry.
|
|
148
|
+
*/
|
|
149
|
+
export const UNDERSTOOD_PREFIXES = new Set([...UNDERSTOOD_NAMESPACES].map((uri) => NAMESPACE_PREFIXES[uri]).filter((prefix) => prefix !== undefined && prefix !== ''));
|
|
@@ -9,6 +9,7 @@ import { Parts } from '../parts/Parts.mjs';
|
|
|
9
9
|
import { RelationshipsPart, type AddPartBehaviour } from '../parts/RelationshipsPart.mjs';
|
|
10
10
|
import type { CustomXmlDataStoragePart } from '../parts/DefaultXmlPart.mjs';
|
|
11
11
|
import { DocPropsCorePart, DocPropsExtendedPart, DocPropsCustomPart } from '../parts/docProps/index.mjs';
|
|
12
|
+
import { type PartWarning } from '../opc/mce/preprocessor.mjs';
|
|
12
13
|
/** Anything `OpcPackage.load` accepts: zip bytes, a flat OPC string, or a container. */
|
|
13
14
|
export type PackageSource = Uint8Array | ArrayBuffer | string | PartStore;
|
|
14
15
|
/**
|
|
@@ -44,6 +45,12 @@ export declare class OpcPackage implements RelationshipSource {
|
|
|
44
45
|
setPartShortcut(part: Part, relationshipType: string): boolean;
|
|
45
46
|
/** The main part: the target of the officeDocument relationship. */
|
|
46
47
|
getMainPart(): Part | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* The warnings of every part parsed so far (each XML part's `warnings`, CR-010 phase 1). A part
|
|
50
|
+
* is parsed when something reads it, so a reader that wants a whole package's warnings reads its
|
|
51
|
+
* parts first; `unmarshalAll()` does, at the cost of re-marshalling every part on save.
|
|
52
|
+
*/
|
|
53
|
+
get warnings(): PartWarning[];
|
|
47
54
|
/** Unmarshals every XML part, so that `contents` is available synchronously everywhere. */
|
|
48
55
|
unmarshalAll(): Promise<void>;
|
|
49
56
|
/** Adds empty core properties if absent (docx4j addDocPropsCorePart). */
|
|
@@ -11,7 +11,7 @@ import { Parts } from '../parts/Parts.mjs';
|
|
|
11
11
|
import { XmlPart } from '../parts/XmlPart.mjs';
|
|
12
12
|
import { RelationshipsPart } from '../parts/RelationshipsPart.mjs';
|
|
13
13
|
import { DocPropsCorePart, DocPropsExtendedPart } from '../parts/docProps/index.mjs';
|
|
14
|
-
import { mcePreprocess } from '../opc/mce/preprocessor.mjs';
|
|
14
|
+
import { mceKeep, mcePreprocess } from '../opc/mce/preprocessor.mjs';
|
|
15
15
|
import { createPackageForContentType, registerGenericPackageClass } from './registry.mjs';
|
|
16
16
|
/**
|
|
17
17
|
* An Open Packaging Conventions package (docx4j org.docx4j.openpackaging.packages.OpcPackage):
|
|
@@ -41,8 +41,8 @@ export class OpcPackage {
|
|
|
41
41
|
static async load(source, options = {}) {
|
|
42
42
|
const store = toStore(source);
|
|
43
43
|
const opts = { ...options };
|
|
44
|
-
if (opts.preprocessor === undefined
|
|
45
|
-
opts.preprocessor = mcePreprocess;
|
|
44
|
+
if (opts.preprocessor === undefined)
|
|
45
|
+
opts.preprocessor = opts.mcePreprocess === true ? mcePreprocess : mceKeep;
|
|
46
46
|
const pkg = await loadPackage(store, createPackageForContentType, opts);
|
|
47
47
|
pkg.loadOptions = opts;
|
|
48
48
|
return pkg;
|
|
@@ -79,6 +79,18 @@ export class OpcPackage {
|
|
|
79
79
|
const rel = (_a = this.relationshipsPart.getRelationshipByType(Namespaces.DOCUMENT)) !== null && _a !== void 0 ? _a : this.relationshipsPart.getRelationshipByType(Namespaces.DOCUMENT_STRICT);
|
|
80
80
|
return rel ? this.relationshipsPart.getPart(rel) : undefined;
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* The warnings of every part parsed so far (each XML part's `warnings`, CR-010 phase 1). A part
|
|
84
|
+
* is parsed when something reads it, so a reader that wants a whole package's warnings reads its
|
|
85
|
+
* parts first; `unmarshalAll()` does, at the cost of re-marshalling every part on save.
|
|
86
|
+
*/
|
|
87
|
+
get warnings() {
|
|
88
|
+
const out = [];
|
|
89
|
+
for (const part of this.parts)
|
|
90
|
+
if (part instanceof XmlPart)
|
|
91
|
+
out.push(...part.warnings);
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
82
94
|
/** Unmarshals every XML part, so that `contents` is available synchronously everywhere. */
|
|
83
95
|
async unmarshalAll() {
|
|
84
96
|
for (const part of this.parts)
|
|
@@ -3,7 +3,7 @@ import type { LoadOptions } from '../opc/Load.mjs';
|
|
|
3
3
|
import type { Part } from '../parts/Part.mjs';
|
|
4
4
|
import { MainDocumentPart, StyleDefinitionsPart, type NumberingDefinitionsPart } from '../parts/wml/index.mjs';
|
|
5
5
|
import { type FontSettings, type DefaultThemeValue } from '../model/fonts/defaultTheme.mjs';
|
|
6
|
-
import
|
|
6
|
+
import { Body, type Address, type Outline, type OutlineParagraph, type 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';
|
|
@@ -14,7 +14,8 @@ import { StylesFacade } from '../model/content/stylesFacade.mjs';
|
|
|
14
14
|
import type { Bound } from '../model/content/binder.mjs';
|
|
15
15
|
import { PropertyResolver } from '../model/properties/PropertyResolver.mjs';
|
|
16
16
|
import type { Emulator } from '../model/listnumbering/Emulator.mjs';
|
|
17
|
-
import type
|
|
17
|
+
import { type TrackedChange, type NoteRemovals } from '../model/content/TrackedChange.mjs';
|
|
18
|
+
import { type RelationshipRemovals } from '../model/content/relationships.mjs';
|
|
18
19
|
import { XmlPart } from '../parts/XmlPart.mjs';
|
|
19
20
|
import type { PartSink } from '../opc/PartStore.mjs';
|
|
20
21
|
import '../parts/wml/comments.mjs';
|
|
@@ -87,6 +88,20 @@ export declare class WordprocessingMLPackage extends OpcPackage implements Track
|
|
|
87
88
|
* the package is saved (check 32; `TrackedChange`). An extension.
|
|
88
89
|
*/
|
|
89
90
|
readonly pendingCommentRemovals: Set<number>;
|
|
91
|
+
/**
|
|
92
|
+
* Footnotes and endnotes whose references went with content a tracked change took away (a rejected
|
|
93
|
+
* insertion, an accepted deletion, a row or cell, a move's other half): their entries go from the
|
|
94
|
+
* notes parts when the package is saved, each unless a reference to it still stands, as Word took
|
|
95
|
+
* them (CR-002 section 44; `TrackedChange`). An extension.
|
|
96
|
+
*/
|
|
97
|
+
readonly pendingNoteRemovals: NoteRemovals;
|
|
98
|
+
/**
|
|
99
|
+
* Relationships, by the name of their part, that a picture removed outright referenced (tracking off,
|
|
100
|
+
* or a picture taken back from its author's own insertion): each goes when the package is saved if
|
|
101
|
+
* nothing then references it, and its part with it when nothing else targets that (CR-002 section
|
|
102
|
+
* 47). Settled on save, not at once, so a picture cut and pasted back keeps its image. An extension.
|
|
103
|
+
*/
|
|
104
|
+
readonly pendingRelationshipRemovals: RelationshipRemovals;
|
|
90
105
|
/**
|
|
91
106
|
* The parts `seedAnnotationIds()` has read, with what it read: `true` for a part still holding its
|
|
92
107
|
* source, whose bytes cannot change, and otherwise the bytes that were set, which stay the same
|
|
@@ -201,6 +216,24 @@ export declare class WordprocessingMLPackage extends OpcPackage implements Track
|
|
|
201
216
|
* unmarshalled, as `body` needs it to be.
|
|
202
217
|
*/
|
|
203
218
|
getTrackedChanges(): TrackedChange[];
|
|
219
|
+
/**
|
|
220
|
+
* Accepts every tracked change in every story: the main document, the footnotes, the endnotes and
|
|
221
|
+
* the comments, the headers and footers, and the text boxes in each. Word's
|
|
222
|
+
* `Document.AcceptAllRevisions` did that on docx4j's CR-043 probe (Word 16.0.20326), and left no
|
|
223
|
+
* revision in any story; `Body.acceptAll` resolves its own story only, as Office JS's `body` does
|
|
224
|
+
* (CR-002 section 49). A story part with no revision markup is not read, so it keeps its bytes.
|
|
225
|
+
* Returns how many changes were listed, story by story. An extension, named after Word's verb.
|
|
226
|
+
*/
|
|
227
|
+
acceptAllRevisions(): Promise<number>;
|
|
228
|
+
/** Rejects every tracked change in every story, as `acceptAllRevisions` accepts them (Word's `Document.RejectAllRevisions`). */
|
|
229
|
+
rejectAllRevisions(): Promise<number>;
|
|
230
|
+
private resolveEveryStory;
|
|
231
|
+
/**
|
|
232
|
+
* The body of every story whose part holds revision markup, in docx4j's order (its CR-043
|
|
233
|
+
* `TrackedChanges.in`): the main document, each footnote, each endnote, each comment, then the
|
|
234
|
+
* headers and footers. A footnote, an endnote or a comment is a body of its own over its content.
|
|
235
|
+
*/
|
|
236
|
+
private revisedStories;
|
|
204
237
|
/** The tracker every content-API mutation asks for; undefined while the mode is `Off`. */
|
|
205
238
|
get changeTracker(): ChangeTracker | undefined;
|
|
206
239
|
/**
|
|
@@ -10,6 +10,7 @@ import { DEFAULT_STYLES_XML } from '../parts/wml/defaultStyles.mjs';
|
|
|
10
10
|
import { ThemePart } from '../parts/dml/index.mjs';
|
|
11
11
|
import { newFontSettings, themeOfSettings, } from '../model/fonts/defaultTheme.mjs';
|
|
12
12
|
import { HeaderPart, FooterPart } from '../parts/wml/index.mjs';
|
|
13
|
+
import { Body, resolveStory } from '../model/content/Body.mjs';
|
|
13
14
|
import { CustomXmlPartCollection } from '../model/customxml/CustomXmlPartCollection.mjs';
|
|
14
15
|
import { DefaultXPathEngine } from '../model/customxml/xpath.mjs';
|
|
15
16
|
import { ChangeTracker, highestAnnotationId } from '../model/content/tracking.mjs';
|
|
@@ -18,6 +19,8 @@ import { StylesFacade } from '../model/content/stylesFacade.mjs';
|
|
|
18
19
|
import { flushPendingCommentRemovals } from '../model/content/Comment.mjs';
|
|
19
20
|
import { PropertyResolver } from '../model/properties/PropertyResolver.mjs';
|
|
20
21
|
import { PropertyResolverNotCreatedException } from '../opc/exceptions.mjs';
|
|
22
|
+
import { flushPendingNoteRemovals } from '../model/content/TrackedChange.mjs';
|
|
23
|
+
import { flushPendingRelationshipRemovals } from '../model/content/relationships.mjs';
|
|
21
24
|
import { XmlPart } from '../parts/XmlPart.mjs';
|
|
22
25
|
// Registers the comment parts with the content API (CR-002 phase G); no cycle: the model half
|
|
23
26
|
// never imports a part, as packages/registry.mts does for the package classes.
|
|
@@ -88,6 +91,20 @@ export class WordprocessingMLPackage extends OpcPackage {
|
|
|
88
91
|
* the package is saved (check 32; `TrackedChange`). An extension.
|
|
89
92
|
*/
|
|
90
93
|
this.pendingCommentRemovals = new Set();
|
|
94
|
+
/**
|
|
95
|
+
* Footnotes and endnotes whose references went with content a tracked change took away (a rejected
|
|
96
|
+
* insertion, an accepted deletion, a row or cell, a move's other half): their entries go from the
|
|
97
|
+
* notes parts when the package is saved, each unless a reference to it still stands, as Word took
|
|
98
|
+
* them (CR-002 section 44; `TrackedChange`). An extension.
|
|
99
|
+
*/
|
|
100
|
+
this.pendingNoteRemovals = { footnote: new Set(), endnote: new Set() };
|
|
101
|
+
/**
|
|
102
|
+
* Relationships, by the name of their part, that a picture removed outright referenced (tracking off,
|
|
103
|
+
* or a picture taken back from its author's own insertion): each goes when the package is saved if
|
|
104
|
+
* nothing then references it, and its part with it when nothing else targets that (CR-002 section
|
|
105
|
+
* 47). Settled on save, not at once, so a picture cut and pasted back keeps its image. An extension.
|
|
106
|
+
*/
|
|
107
|
+
this.pendingRelationshipRemovals = new Map();
|
|
91
108
|
/**
|
|
92
109
|
* The parts `seedAnnotationIds()` has read, with what it read: `true` for a part still holding its
|
|
93
110
|
* source, whose bytes cannot change, and otherwise the bytes that were set, which stay the same
|
|
@@ -220,6 +237,10 @@ export class WordprocessingMLPackage extends OpcPackage {
|
|
|
220
237
|
this.resolver = resolver;
|
|
221
238
|
this.resolverPending = undefined;
|
|
222
239
|
return resolver;
|
|
240
|
+
}, (e) => {
|
|
241
|
+
// not kept: a styles part put right (setXml) is read again on the next call
|
|
242
|
+
this.resolverPending = undefined;
|
|
243
|
+
throw e;
|
|
223
244
|
}));
|
|
224
245
|
return this.resolverPending;
|
|
225
246
|
}
|
|
@@ -399,6 +420,56 @@ export class WordprocessingMLPackage extends OpcPackage {
|
|
|
399
420
|
getTrackedChanges() {
|
|
400
421
|
return this.body.getTrackedChanges();
|
|
401
422
|
}
|
|
423
|
+
/**
|
|
424
|
+
* Accepts every tracked change in every story: the main document, the footnotes, the endnotes and
|
|
425
|
+
* the comments, the headers and footers, and the text boxes in each. Word's
|
|
426
|
+
* `Document.AcceptAllRevisions` did that on docx4j's CR-043 probe (Word 16.0.20326), and left no
|
|
427
|
+
* revision in any story; `Body.acceptAll` resolves its own story only, as Office JS's `body` does
|
|
428
|
+
* (CR-002 section 49). A story part with no revision markup is not read, so it keeps its bytes.
|
|
429
|
+
* Returns how many changes were listed, story by story. An extension, named after Word's verb.
|
|
430
|
+
*/
|
|
431
|
+
async acceptAllRevisions() {
|
|
432
|
+
return this.resolveEveryStory('accept');
|
|
433
|
+
}
|
|
434
|
+
/** Rejects every tracked change in every story, as `acceptAllRevisions` accepts them (Word's `Document.RejectAllRevisions`). */
|
|
435
|
+
async rejectAllRevisions() {
|
|
436
|
+
return this.resolveEveryStory('reject');
|
|
437
|
+
}
|
|
438
|
+
async resolveEveryStory(what) {
|
|
439
|
+
let count = 0;
|
|
440
|
+
for (const body of await this.revisedStories())
|
|
441
|
+
count += resolveStory(body, what);
|
|
442
|
+
return count;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* The body of every story whose part holds revision markup, in docx4j's order (its CR-043
|
|
446
|
+
* `TrackedChanges.in`): the main document, each footnote, each endnote, each comment, then the
|
|
447
|
+
* headers and footers. A footnote, an endnote or a comment is a body of its own over its content.
|
|
448
|
+
*/
|
|
449
|
+
async revisedStories() {
|
|
450
|
+
var _a, _b;
|
|
451
|
+
const main = this.getMainDocumentPart();
|
|
452
|
+
const out = [];
|
|
453
|
+
if (await holdsRevisions(main))
|
|
454
|
+
out.push(await main.getBody());
|
|
455
|
+
for (const [part, kind] of [[main.footnotesPart, 'footnote'], [main.endnotesPart, 'endnote']]) {
|
|
456
|
+
if (!part || !(await holdsRevisions(part)))
|
|
457
|
+
continue;
|
|
458
|
+
const contents = await part.getContents();
|
|
459
|
+
for (const note of (_a = (kind === 'footnote' ? contents.footnote : contents.endnote)) !== null && _a !== void 0 ? _a : []) {
|
|
460
|
+
out.push(new Body(part, note, `${kind}:${note.id}`, this));
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
const comments = main.commentsPart;
|
|
464
|
+
if (comments && (await holdsRevisions(comments))) {
|
|
465
|
+
for (const comment of (_b = (await comments.getContents()).comment) !== null && _b !== void 0 ? _b : [])
|
|
466
|
+
out.push(new Body(comments, comment, `comment:${comment.id}`, this));
|
|
467
|
+
}
|
|
468
|
+
for (const part of [...main.headerParts, ...main.footerParts])
|
|
469
|
+
if (await holdsRevisions(part))
|
|
470
|
+
out.push(await part.getBody());
|
|
471
|
+
return out;
|
|
472
|
+
}
|
|
402
473
|
/** The tracker every content-API mutation asks for; undefined while the mode is `Off`. */
|
|
403
474
|
get changeTracker() {
|
|
404
475
|
const mode = this.changeTrackingMode;
|
|
@@ -506,6 +577,12 @@ export class WordprocessingMLPackage extends OpcPackage {
|
|
|
506
577
|
target.author = { ...this.author };
|
|
507
578
|
for (const id of this.pendingCommentRemovals)
|
|
508
579
|
target.pendingCommentRemovals.add(id);
|
|
580
|
+
for (const id of this.pendingNoteRemovals.footnote)
|
|
581
|
+
target.pendingNoteRemovals.footnote.add(id);
|
|
582
|
+
for (const id of this.pendingNoteRemovals.endnote)
|
|
583
|
+
target.pendingNoteRemovals.endnote.add(id);
|
|
584
|
+
for (const [name, ids] of this.pendingRelationshipRemovals)
|
|
585
|
+
target.pendingRelationshipRemovals.set(name, new Set(ids));
|
|
509
586
|
target.trackedChangeDate = this.trackedChangeDate;
|
|
510
587
|
target.fonts.defaultTheme = this.fonts.defaultTheme;
|
|
511
588
|
target.xpathEngine = this.xpathEngine;
|
|
@@ -514,8 +591,18 @@ export class WordprocessingMLPackage extends OpcPackage {
|
|
|
514
591
|
var _a;
|
|
515
592
|
if (this.trackingPending)
|
|
516
593
|
await this.setChangeTrackingMode((_a = this.trackingMode) !== null && _a !== void 0 ? _a : 'Off');
|
|
517
|
-
|
|
518
|
-
|
|
594
|
+
// what removed content took with it, until nothing more goes: a removed note takes the comments
|
|
595
|
+
// anchored in it, and a removed note or comment the relationships its content held, which the last
|
|
596
|
+
// step settles (CR-002 section 50; docx4j's Removals.flush repeats the same way)
|
|
597
|
+
const notes = this.pendingNoteRemovals;
|
|
598
|
+
for (let round = 0; round < 10 && this.pendingCommentRemovals.size + notes.footnote.size + notes.endnote.size > 0; round++) {
|
|
599
|
+
if (this.pendingCommentRemovals.size > 0)
|
|
600
|
+
await flushPendingCommentRemovals(await this.getBody());
|
|
601
|
+
if (notes.footnote.size + notes.endnote.size > 0)
|
|
602
|
+
await flushPendingNoteRemovals(this.getMainDocumentPart(), notes, this);
|
|
603
|
+
}
|
|
604
|
+
if (this.pendingRelationshipRemovals.size > 0)
|
|
605
|
+
flushPendingRelationshipRemovals(this, this.pendingRelationshipRemovals);
|
|
519
606
|
if (this.requiredStyles.size > 0) {
|
|
520
607
|
await this.styles.ensure([...this.requiredStyles]);
|
|
521
608
|
this.requiredStyles.clear();
|
|
@@ -621,12 +708,22 @@ export class WordprocessingMLPackage extends OpcPackage {
|
|
|
621
708
|
return bodies;
|
|
622
709
|
},
|
|
623
710
|
customXmlRelationshipSource: () => this.getMainDocumentPart(),
|
|
711
|
+
xhtmlHost: this,
|
|
624
712
|
})));
|
|
625
713
|
}
|
|
626
714
|
get progId() {
|
|
627
715
|
return 'Word.Document';
|
|
628
716
|
}
|
|
629
717
|
}
|
|
718
|
+
/** Revision markup, by local name in any prefix: what a story part holds when it has a tracked change to resolve. */
|
|
719
|
+
const REVISION_MARKUP = /<(?:[\w.-]+:)?(?:ins|del|moveFrom|moveTo|rPrChange|pPrChange|sectPrChange|tblPrChange|trPrChange|tcPrChange|tblGridChange|tblPrExChange|cellIns|cellDel|cellMerge|numberingChange|customXmlInsRangeStart|customXmlDelRangeStart)[\s/>]/;
|
|
720
|
+
/**
|
|
721
|
+
* True when a story part may hold a tracked change: always once it is unmarshalled, else when its
|
|
722
|
+
* source has revision markup, read without unmarshalling it, so a part with none keeps its bytes.
|
|
723
|
+
*/
|
|
724
|
+
async function holdsRevisions(part) {
|
|
725
|
+
return part.isUnmarshalled || REVISION_MARKUP.test(await part.getXml());
|
|
726
|
+
}
|
|
630
727
|
/**
|
|
631
728
|
* `w:trackRevisions` is a flag, so a file cannot distinguish `TrackAll` from `TrackMineOnly`;
|
|
632
729
|
* a document that has it on reads as `TrackAll`, and either value writes it.
|