@docx4j/core-ts 0.4.0 → 0.6.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/NOTICE +4 -0
- package/README.md +34 -3
- package/dist/html/convert.mjs +40 -15
- package/dist/html/elements.mjs +2 -1
- package/dist/model/color/ColorMath.d.mts +29 -0
- package/dist/model/color/ColorMath.mjs +146 -0
- package/dist/model/color/ColorTables.d.mts +6 -0
- package/dist/model/color/ColorTables.mjs +83 -0
- package/dist/model/color/DrawingMLColors.d.mts +52 -0
- package/dist/model/color/DrawingMLColors.mjs +374 -0
- package/dist/model/color/ResolvedColor.d.mts +36 -0
- package/dist/model/color/ResolvedColor.mjs +65 -0
- package/dist/model/color/SpreadsheetColors.d.mts +36 -0
- package/dist/model/color/SpreadsheetColors.mjs +84 -0
- package/dist/model/color/StyleFill.d.mts +28 -0
- package/dist/model/color/StyleFill.mjs +21 -0
- package/dist/model/color/ThemeColors.d.mts +86 -0
- package/dist/model/color/ThemeColors.mjs +304 -0
- package/dist/model/color/WordColors.d.mts +71 -0
- package/dist/model/color/WordColors.mjs +121 -0
- package/dist/model/color/index.d.mts +6 -0
- package/dist/model/color/index.mjs +30 -0
- package/dist/model/content/Body.d.mts +153 -19
- package/dist/model/content/Body.mjs +570 -131
- package/dist/model/content/Comment.d.mts +21 -3
- package/dist/model/content/Comment.mjs +202 -19
- package/dist/model/content/ContentControl.d.mts +197 -18
- package/dist/model/content/ContentControl.mjs +664 -95
- package/dist/model/content/Font.d.mts +67 -17
- package/dist/model/content/Font.mjs +114 -35
- package/dist/model/content/InlinePicture.d.mts +82 -7
- package/dist/model/content/InlinePicture.mjs +221 -28
- package/dist/model/content/List.mjs +13 -1
- package/dist/model/content/Paragraph.d.mts +213 -14
- package/dist/model/content/Paragraph.mjs +936 -100
- package/dist/model/content/Range.d.mts +242 -11
- package/dist/model/content/Range.mjs +975 -119
- package/dist/model/content/ResolvedView.d.mts +104 -0
- package/dist/model/content/ResolvedView.mjs +596 -0
- package/dist/model/content/Table.d.mts +29 -3
- package/dist/model/content/Table.mjs +109 -33
- package/dist/model/content/TrackedChange.d.mts +301 -16
- package/dist/model/content/TrackedChange.mjs +1284 -158
- package/dist/model/content/binder.d.mts +2 -1
- package/dist/model/content/binder.mjs +48 -1
- package/dist/model/content/comments.d.mts +20 -6
- package/dist/model/content/comments.mjs +67 -57
- package/dist/model/content/generalException.d.mts +18 -0
- package/dist/model/content/generalException.mjs +24 -0
- package/dist/model/content/index.d.mts +8 -3
- package/dist/model/content/index.mjs +7 -2
- package/dist/model/content/mathText.d.mts +21 -0
- package/dist/model/content/mathText.mjs +267 -0
- package/dist/model/content/officeText.d.mts +27 -0
- package/dist/model/content/officeText.mjs +74 -0
- package/dist/model/content/outline.d.mts +5 -0
- package/dist/model/content/outline.mjs +13 -0
- package/dist/model/content/placeholders.d.mts +110 -0
- package/dist/model/content/placeholders.mjs +253 -0
- package/dist/model/content/relationships.d.mts +30 -0
- package/dist/model/content/relationships.mjs +133 -0
- package/dist/model/content/search.d.mts +72 -0
- package/dist/model/content/search.mjs +204 -2
- package/dist/model/content/spanEdits.d.mts +159 -0
- package/dist/model/content/spanEdits.mjs +728 -0
- package/dist/model/content/story.d.mts +44 -0
- package/dist/model/content/story.mjs +92 -0
- package/dist/model/content/tableColumns.d.mts +1 -1
- package/dist/model/content/tableColumns.mjs +108 -80
- package/dist/model/content/tableRevisions.d.mts +11 -3
- package/dist/model/content/tableRevisions.mjs +72 -42
- package/dist/model/content/tableRules.d.mts +30 -0
- package/dist/model/content/tableRules.mjs +100 -0
- package/dist/model/content/textBoxes.d.mts +18 -0
- package/dist/model/content/textBoxes.mjs +105 -0
- package/dist/model/content/trackedChangeFilter.d.mts +39 -0
- package/dist/model/content/trackedChangeFilter.mjs +81 -0
- package/dist/model/content/tracking.d.mts +28 -0
- package/dist/model/content/tracking.mjs +149 -10
- package/dist/model/content/tree.d.mts +212 -13
- package/dist/model/content/tree.mjs +356 -35
- package/dist/model/customxml/CustomXmlPartCollection.d.mts +8 -0
- package/dist/model/customxml/CustomXmlPartCollection.mjs +34 -11
- package/dist/model/customxml/XmlMapping.d.mts +1 -1
- package/dist/model/customxml/XmlMapping.mjs +11 -0
- package/dist/model/customxml/bindings.d.mts +21 -2
- package/dist/model/customxml/bindings.mjs +76 -32
- package/dist/model/customxml/kinds.d.mts +8 -3
- package/dist/model/customxml/kinds.mjs +26 -5
- package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
- package/dist/model/customxml/picturePlaceholder.mjs +32 -0
- package/dist/model/customxml/pictures.d.mts +9 -5
- package/dist/model/customxml/pictures.mjs +31 -13
- package/dist/model/customxml/xhtml.mjs +8 -2
- 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/index.d.mts +2 -0
- package/dist/model/index.mjs +4 -0
- package/dist/model/listnumbering/Emulator.d.mts +37 -5
- package/dist/model/listnumbering/Emulator.mjs +63 -9
- package/dist/model/listnumbering/definitions.mjs +18 -0
- package/dist/model/properties/PropertyResolver.d.mts +87 -7
- package/dist/model/properties/PropertyResolver.mjs +197 -17
- package/dist/model/properties/catalogue.d.mts +32 -4
- package/dist/model/properties/catalogue.mjs +115 -36
- package/dist/model/properties/log.d.mts +1 -1
- package/dist/model/properties/log.mjs +5 -5
- package/dist/model/properties/table.d.mts +16 -2
- package/dist/model/properties/table.mjs +17 -5
- package/dist/model/text/Extractor.d.mts +289 -0
- package/dist/model/text/Extractor.mjs +2162 -0
- package/dist/model/text/LinkTargets.d.mts +45 -0
- package/dist/model/text/LinkTargets.mjs +170 -0
- package/dist/model/text/MarkdownExporter.d.mts +140 -0
- package/dist/model/text/MarkdownExporter.mjs +255 -0
- package/dist/model/text/PlainText.d.mts +29 -0
- package/dist/model/text/PlainText.mjs +539 -0
- package/dist/model/text/TextContext.d.mts +161 -0
- package/dist/model/text/TextContext.mjs +425 -0
- package/dist/model/text/TextOptions.d.mts +150 -0
- package/dist/model/text/TextOptions.mjs +278 -0
- package/dist/model/text/TextResult.d.mts +504 -0
- package/dist/model/text/TextResult.mjs +526 -0
- package/dist/model/text/Texts.d.mts +99 -0
- package/dist/model/text/Texts.mjs +187 -0
- package/dist/model/text/WmlToMarkdown.d.mts +120 -0
- package/dist/model/text/WmlToMarkdown.mjs +911 -0
- package/dist/model/text/commonmark.d.mts +121 -0
- package/dist/model/text/commonmark.mjs +655 -0
- package/dist/model/text/index.d.mts +7 -0
- package/dist/model/text/index.mjs +8 -0
- package/dist/model/text/normalize.d.mts +8 -0
- package/dist/model/text/normalize.mjs +13 -0
- package/dist/model/text/outline.d.mts +4 -0
- package/dist/model/text/outline.mjs +71 -0
- package/dist/office-js/fragment.d.mts +7 -5
- package/dist/office-js/fragment.mjs +63 -14
- package/dist/office-js/proxy.d.mts +8 -0
- package/dist/office-js/proxy.mjs +29 -0
- package/dist/office-js/run.mjs +2 -0
- package/dist/office-js/supported.generated.mjs +16 -0
- package/dist/office-js/toApiScript.mjs +21 -5
- package/dist/opc/Load.d.mts +9 -2
- package/dist/opc/Save.d.mts +17 -0
- package/dist/opc/Save.mjs +12 -2
- package/dist/opc/index.d.mts +3 -3
- package/dist/opc/index.mjs +2 -2
- package/dist/opc/mce/preprocessor.d.mts +88 -7
- package/dist/opc/mce/preprocessor.mjs +144 -37
- package/dist/opc/mce/understood.d.mts +34 -0
- package/dist/opc/mce/understood.mjs +55 -3
- package/dist/packages/OpcPackage.d.mts +20 -0
- package/dist/packages/OpcPackage.mjs +16 -3
- package/dist/packages/WordprocessingMLPackage.d.mts +77 -7
- package/dist/packages/WordprocessingMLPackage.mjs +242 -11
- package/dist/parts/DefaultXmlPart.mjs +2 -2
- package/dist/parts/Part.d.mts +10 -2
- package/dist/parts/XmlPart.d.mts +35 -32
- package/dist/parts/XmlPart.mjs +107 -107
- package/dist/parts/wml/comments.mjs +30 -0
- package/dist/parts/wml/index.mjs +12 -0
- package/dist/xml/dom.d.mts +7 -0
- package/dist/xml/dom.mjs +18 -0
- package/package.json +2 -2
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import * as el from '@docx4j/generated-objects-ts/el/org_docx4j_wml';
|
|
2
|
+
import * as f from '@docx4j/generated-objects-ts/factory/org_docx4j_wml';
|
|
3
|
+
import { sdtProperty, sdtKindOf, textOf } from '@docx4j/generated-objects-ts/builders/wml';
|
|
4
|
+
import { deepCopy } from '@docx4j/generated-objects-ts';
|
|
5
|
+
import { linkParents } from './tree.mjs';
|
|
6
|
+
import { uuid } from '../customxml/CustomXmlPartCollection.mjs';
|
|
7
|
+
import { PLACEHOLDER_STYLE, placeholderRun } from '../customxml/bindings.mjs';
|
|
8
|
+
import { placeholderStyle } from '../../parts/wml/placeholderStyle.mjs';
|
|
9
|
+
/** Word's default placeholder entry, which a control Word makes names (checks 64 to 66). */
|
|
10
|
+
export const DEFAULT_PLACEHOLDER_ENTRY = 'DefaultPlaceholder_-1854013440';
|
|
11
|
+
/** The default entry's text, as Word 365 writes it. */
|
|
12
|
+
export const DEFAULT_PLACEHOLDER_TEXT = 'Click or tap here to enter text.';
|
|
13
|
+
/**
|
|
14
|
+
* Word's default placeholder entry for each kind of control that has one, as Word 365 makes them from the Developer tab
|
|
15
|
+
* (Jason's `placeholders.docx`, 2026-10-25; CR-002 section 63.22, as docx4j's CR-063 D8 has it): each in the glossary
|
|
16
|
+
* with the default entry's `w:docPartPr`, its body one run in the Placeholder Text style. A picture, a check box and a
|
|
17
|
+
* repeating section have none; the group, citation, equation, bibliography and `w:docPartObj` kinds are unmeasured.
|
|
18
|
+
*/
|
|
19
|
+
export const DEFAULT_ENTRIES = {
|
|
20
|
+
text: { name: DEFAULT_PLACEHOLDER_ENTRY, text: DEFAULT_PLACEHOLDER_TEXT },
|
|
21
|
+
date: { name: 'DefaultPlaceholder_-1854013437', text: 'Click or tap to enter a date.' },
|
|
22
|
+
list: { name: 'DefaultPlaceholder_-1854013438', text: 'Choose an item.' },
|
|
23
|
+
buildingBlock: { name: 'DefaultPlaceholder_-1854013436', text: 'Choose a building block.' },
|
|
24
|
+
repeatingSectionItem: {
|
|
25
|
+
name: 'DefaultPlaceholder_-1854013435',
|
|
26
|
+
text: 'Enter any content that you want to repeat, including other content controls. You can also insert this control around table rows in order to repeat parts of a table.',
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
/** The default entry Word gives a control of this kind (`DEFAULT_ENTRIES`); undefined for a kind it gives none. */
|
|
30
|
+
export function defaultEntryFor(sdtPr) {
|
|
31
|
+
switch (sdtKindOf(sdtPr)) {
|
|
32
|
+
case 'RichText':
|
|
33
|
+
case 'PlainText': return DEFAULT_ENTRIES.text;
|
|
34
|
+
case 'DatePicker': return DEFAULT_ENTRIES.date;
|
|
35
|
+
case 'DropDownList':
|
|
36
|
+
case 'ComboBox': return DEFAULT_ENTRIES.list;
|
|
37
|
+
case 'BuildingBlockGallery': return sdtProperty(sdtPr, 'docPartList') ? DEFAULT_ENTRIES.buildingBlock : undefined;
|
|
38
|
+
case 'RepeatingSectionItem': return DEFAULT_ENTRIES.repeatingSectionItem;
|
|
39
|
+
default: return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** The glossary entry a control's `w:placeholder` names; undefined where it names none (the bare form). */
|
|
43
|
+
export function placeholderEntryOf(sdtPr) {
|
|
44
|
+
var _a, _b, _c;
|
|
45
|
+
const value = (_a = sdtProperty(sdtPr, 'placeholder')) === null || _a === void 0 ? void 0 : _a.value;
|
|
46
|
+
return value === undefined ? undefined : (_c = (_b = value.docPart) === null || _b === void 0 ? void 0 : _b.val) !== null && _c !== void 0 ? _c : '';
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Names `entry` in the control's `w:placeholder`, where Word writes it: after `w:id`, before `w:showingPlcHdr` and the
|
|
50
|
+
* binding (check 64's controls, as Word saved them).
|
|
51
|
+
*/
|
|
52
|
+
export function namePlaceholderEntry(sdtPr, entry) {
|
|
53
|
+
var _a;
|
|
54
|
+
const items = ((_a = sdtPr.rPrOrAliasOrLock) !== null && _a !== void 0 ? _a : (sdtPr.rPrOrAliasOrLock = []));
|
|
55
|
+
const element = el.placeholder(f.createCTPlaceholder({ docPart: f.createCTPlaceholderDocPart({ val: entry }) }));
|
|
56
|
+
const at = items.findIndex((i) => i.name.localPart === 'placeholder');
|
|
57
|
+
if (at >= 0) {
|
|
58
|
+
items[at] = element;
|
|
59
|
+
linkParents(element, sdtPr);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const after = new Set(['showingPlcHdr', 'dataBinding', 'temporary']);
|
|
63
|
+
const before = items.findIndex((i) => after.has(i.name.localPart));
|
|
64
|
+
items.splice(before < 0 ? items.length : before, 0, element);
|
|
65
|
+
linkParents(element, sdtPr);
|
|
66
|
+
}
|
|
67
|
+
/** A new entry's name, as Word names one: 32 upper-case hex digits, a new GUID's (check 65). */
|
|
68
|
+
export function newEntryName() {
|
|
69
|
+
return uuid().replace(/-/g, '').toUpperCase();
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The placeholder entries of a package's glossary: those its glossary document part holds, read privately (the part
|
|
73
|
+
* stays untouched), and those written since, which go into the glossary when the package is saved (`flush`).
|
|
74
|
+
*/
|
|
75
|
+
export class GlossaryEntries {
|
|
76
|
+
constructor() {
|
|
77
|
+
/** Entries to write on save: name, text. */
|
|
78
|
+
this.pending = new Map();
|
|
79
|
+
}
|
|
80
|
+
/** Reads the glossary's entries, privately; again only where the part changed. */
|
|
81
|
+
async load(glossary) {
|
|
82
|
+
if (glossary === this.source && this.read !== undefined)
|
|
83
|
+
return;
|
|
84
|
+
this.source = glossary;
|
|
85
|
+
this.read = glossary ? entriesOf(await glossary.readContents().catch(() => undefined)) : new Map();
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* An entry's text, `null` where neither the glossary nor this session holds it, as Office JS reads it (check 65). An
|
|
89
|
+
* entry's paragraphs are a `\r` apart, as a control's are.
|
|
90
|
+
*/
|
|
91
|
+
textOf(name) {
|
|
92
|
+
var _a, _b;
|
|
93
|
+
return (_b = (_a = this.entry(name)) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : null;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* An entry's body, the blocks of its `w:docPartBody` (not copies), `null` where neither the glossary nor this session
|
|
97
|
+
* holds it; an entry written this session is one paragraph, one run of its text in the Placeholder Text style.
|
|
98
|
+
*/
|
|
99
|
+
bodyOf(name) {
|
|
100
|
+
var _a, _b;
|
|
101
|
+
return (_b = (_a = this.entry(name)) === null || _a === void 0 ? void 0 : _a.body) !== null && _b !== void 0 ? _b : null;
|
|
102
|
+
}
|
|
103
|
+
/** True once the glossary has been read (`load`): until then an entry it holds reads as missing. */
|
|
104
|
+
get loaded() {
|
|
105
|
+
var _a;
|
|
106
|
+
return this.read !== undefined || ((_a = this.source) === null || _a === void 0 ? void 0 : _a.isUnmarshalled) === true;
|
|
107
|
+
}
|
|
108
|
+
entry(name) {
|
|
109
|
+
var _a;
|
|
110
|
+
const pending = this.pending.get(name);
|
|
111
|
+
if (pending !== undefined)
|
|
112
|
+
return { text: pending, body: [el.p({ content: [placeholderRun(undefined, pending)] })] };
|
|
113
|
+
const part = this.source;
|
|
114
|
+
if (part === null || part === void 0 ? void 0 : part.isUnmarshalled)
|
|
115
|
+
return entriesOf(part.contents).get(name);
|
|
116
|
+
return (_a = this.read) === null || _a === void 0 ? void 0 : _a.get(name);
|
|
117
|
+
}
|
|
118
|
+
/** Whether the glossary holds an entry of this name, or one is waiting to be written. */
|
|
119
|
+
has(name) {
|
|
120
|
+
return this.textOf(name) !== null;
|
|
121
|
+
}
|
|
122
|
+
/** An entry to write on save, where the glossary doesn't hold one of that name. */
|
|
123
|
+
require(name, text) {
|
|
124
|
+
if (!this.has(name))
|
|
125
|
+
this.pending.set(name, text);
|
|
126
|
+
}
|
|
127
|
+
/** True while entries wait for the save. */
|
|
128
|
+
get waiting() {
|
|
129
|
+
return this.pending.size > 0;
|
|
130
|
+
}
|
|
131
|
+
/** Entries waiting for the save, for a clone. */
|
|
132
|
+
copyTo(target) {
|
|
133
|
+
for (const [name, text] of this.pending)
|
|
134
|
+
target.pending.set(name, text);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Writes the waiting entries into the glossary, making it where the file has none (D9 (a): the glossary document
|
|
138
|
+
* part and a styles part holding Placeholder Text, related from it), and puts Placeholder Text into a glossary's
|
|
139
|
+
* styles part that lacks it: an entry whose style its glossary lacks loses it in Word's save (check 65's 65b).
|
|
140
|
+
*/
|
|
141
|
+
async flush(host, make) {
|
|
142
|
+
var _a;
|
|
143
|
+
if (this.pending.size === 0)
|
|
144
|
+
return;
|
|
145
|
+
const main = host.getMainDocumentPart();
|
|
146
|
+
let glossary = main.glossaryDocumentPart;
|
|
147
|
+
if (!glossary) {
|
|
148
|
+
glossary = make.glossary();
|
|
149
|
+
glossary.setContents(f.createGlossaryDocument({ docParts: f.createCTDocParts({ docPart: [] }) }));
|
|
150
|
+
main.addTargetPart(glossary);
|
|
151
|
+
}
|
|
152
|
+
const doc = await glossary.getContents();
|
|
153
|
+
const docParts = ((_a = doc.docParts) !== null && _a !== void 0 ? _a : (doc.docParts = f.createCTDocParts({ docPart: [] })));
|
|
154
|
+
linkParents(docParts, doc);
|
|
155
|
+
const present = entriesOf(doc);
|
|
156
|
+
for (const [name, text] of this.pending) {
|
|
157
|
+
if (present.has(name))
|
|
158
|
+
continue;
|
|
159
|
+
const part = docPartOf(name, text);
|
|
160
|
+
docParts.docPart.push(part);
|
|
161
|
+
linkParents(part, docParts);
|
|
162
|
+
}
|
|
163
|
+
this.pending.clear();
|
|
164
|
+
this.source = glossary;
|
|
165
|
+
await ensureGlossaryStyles(glossary, make.styles);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** A glossary's entries by name. */
|
|
169
|
+
function entriesOf(doc) {
|
|
170
|
+
var _a, _b, _c, _d, _e;
|
|
171
|
+
const out = new Map();
|
|
172
|
+
for (const part of (_b = (_a = doc === null || doc === void 0 ? void 0 : doc.docParts) === null || _a === void 0 ? void 0 : _a.docPart) !== null && _b !== void 0 ? _b : []) {
|
|
173
|
+
const name = (_c = part.docPartPr) === null || _c === void 0 ? void 0 : _c.nameOrStyleOrCategory.find((i) => i.TYPE_NAME === 'org_docx4j_wml.CTDocPartName');
|
|
174
|
+
if (!(name === null || name === void 0 ? void 0 : name.val))
|
|
175
|
+
continue;
|
|
176
|
+
const body = ((_e = (_d = part.docPartBody) === null || _d === void 0 ? void 0 : _d.content) !== null && _e !== void 0 ? _e : []);
|
|
177
|
+
const paragraphs = body.filter((e) => e.value.TYPE_NAME === 'org_docx4j_wml.P');
|
|
178
|
+
out.set(name.val, { text: paragraphs.map((p) => textOf(p.value)).join('\r'), body });
|
|
179
|
+
}
|
|
180
|
+
return out;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* What a control shows of an entry (CR-002 section 63.18, as revised by 63.21): a copy of each of its body's paragraphs'
|
|
184
|
+
* run-level content, every run as the glossary holds it. Word brought an entry back so, the control's own run properties
|
|
185
|
+
* not over it (check 68, cases 21 to 24), and doesn't restyle it (check 65's 65b, case 9). The paragraphs' properties stay
|
|
186
|
+
* in the glossary, whose styles they name.
|
|
187
|
+
*/
|
|
188
|
+
export function entryContentOf(body) {
|
|
189
|
+
var _a;
|
|
190
|
+
const out = [];
|
|
191
|
+
for (const block of body) {
|
|
192
|
+
if (block.value.TYPE_NAME !== 'org_docx4j_wml.P')
|
|
193
|
+
continue;
|
|
194
|
+
const items = ((_a = block.value.content) !== null && _a !== void 0 ? _a : []).filter((e) => { var _a, _b; return !['proofErr', 'bookmarkStart', 'bookmarkEnd'].includes((_b = (_a = e.name) === null || _a === void 0 ? void 0 : _a.localPart) !== null && _b !== void 0 ? _b : ''); });
|
|
195
|
+
out.push(items.map((item) => ({ name: item.name, value: deepCopy(item.value) })));
|
|
196
|
+
}
|
|
197
|
+
return out;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* A placeholder entry as Word writes one (check 65): the default entry's `w:docPartPr` (category General, gallery
|
|
201
|
+
* placeholder, type `bbPlcHdr`, behaviour content) with a `w:guid` of its own, and a body of one paragraph, one run in
|
|
202
|
+
* the Placeholder Text style.
|
|
203
|
+
*/
|
|
204
|
+
function docPartOf(name, text) {
|
|
205
|
+
const pr = f.createCTDocPartPr({
|
|
206
|
+
nameOrStyleOrCategory: [
|
|
207
|
+
f.createCTDocPartName({ val: name }),
|
|
208
|
+
f.createCTDocPartCategory({ name: f.createCTDocPartCategoryName({ val: 'General' }), gallery: f.createCTDocPartGallery({ val: 'placeholder' }) }),
|
|
209
|
+
f.createCTDocPartTypes({ type: [f.createCTDocPartType({ val: 'bbPlcHdr' })] }),
|
|
210
|
+
f.createCTDocPartBehaviors({ behavior: [f.createCTDocPartBehavior({ val: 'content' })] }),
|
|
211
|
+
f.createCTGuid({ val: `{${uuid().toUpperCase()}}` }),
|
|
212
|
+
],
|
|
213
|
+
});
|
|
214
|
+
const body = f.createBody({ content: [el.p({ content: [placeholderRun(undefined, text)] })] });
|
|
215
|
+
return f.createCTDocPart({ docPartPr: pr, docPartBody: body });
|
|
216
|
+
}
|
|
217
|
+
/** Default Paragraph Font as Word defines it, which Placeholder Text is based on (check 34's glossary). */
|
|
218
|
+
function defaultParagraphFont() {
|
|
219
|
+
return f.createStyle({
|
|
220
|
+
type: 'character', _default: true, styleId: 'DefaultParagraphFont',
|
|
221
|
+
name: f.createStyleName({ val: 'Default Paragraph Font' }),
|
|
222
|
+
uiPriority: f.createStyleUiPriority({ val: 1 }),
|
|
223
|
+
semiHidden: f.createBooleanDefaultTrue(),
|
|
224
|
+
unhideWhenUsed: f.createBooleanDefaultTrue(),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
/** The glossary's styles part holds Placeholder Text and the style it is based on, made where the glossary has none. */
|
|
228
|
+
async function ensureGlossaryStyles(glossary, make) {
|
|
229
|
+
var _a, _b;
|
|
230
|
+
const holder = glossary;
|
|
231
|
+
let part = holder.styleDefinitionsPart;
|
|
232
|
+
if (!part) {
|
|
233
|
+
part = make();
|
|
234
|
+
part.setContents(f.createStyles({ style: [] }));
|
|
235
|
+
holder.addTargetPart(part);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
// a styles part that has them already stays untouched
|
|
239
|
+
const read = await part.readContents().catch(() => undefined);
|
|
240
|
+
const ids = new Set(((_a = read === null || read === void 0 ? void 0 : read.style) !== null && _a !== void 0 ? _a : []).map((s) => s.styleId));
|
|
241
|
+
if (ids.has('DefaultParagraphFont') && ids.has(PLACEHOLDER_STYLE))
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const styles = await part.getContents();
|
|
245
|
+
const list = ((_b = styles.style) !== null && _b !== void 0 ? _b : (styles.style = []));
|
|
246
|
+
const ids = new Set(list.map((s) => s.styleId));
|
|
247
|
+
for (const style of [defaultParagraphFont(), placeholderStyle()]) {
|
|
248
|
+
if (ids.has(style.styleId))
|
|
249
|
+
continue;
|
|
250
|
+
list.push(style);
|
|
251
|
+
linkParents(style, styles);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Part } from '../../parts/Part.mjs';
|
|
2
|
+
/**
|
|
3
|
+
* Adds to `into` every relationship id a tree references: the `r:` attributes of its typed objects (read
|
|
4
|
+
* off the model's mapping, as docx4j reads its fields' `@XmlAttribute`), those kept in an
|
|
5
|
+
* `otherAttributes` map, and those of any DOM it holds.
|
|
6
|
+
*/
|
|
7
|
+
export declare function relationshipIdsIn(tree: unknown, into: Set<string>): void;
|
|
8
|
+
/**
|
|
9
|
+
* The relationships of `part` among `candidates` that nothing left in the part references are removed,
|
|
10
|
+
* and the part each targeted when no relationship in the package targets it any more, with what that
|
|
11
|
+
* part alone related to (`RelationshipsPart.removeParts`). A part not unmarshalled can't be read for
|
|
12
|
+
* what still references, so nothing is removed from it.
|
|
13
|
+
*/
|
|
14
|
+
export declare function dropRelationshipsUnused(part: Part, candidates: Set<string>): void;
|
|
15
|
+
/**
|
|
16
|
+
* Relationship ids, by the name of the part they belong to, that content removed outside a
|
|
17
|
+
* resolution referenced: a picture deleted with tracking off, or taken back from its author's own
|
|
18
|
+
* insertion. They are settled when the package is saved rather than at once, so that content put
|
|
19
|
+
* back in the meantime (a cut and a paste of the same drawing) keeps its relationship.
|
|
20
|
+
*/
|
|
21
|
+
export type RelationshipRemovals = Map<string, Set<string>>;
|
|
22
|
+
/** Queues `ids` of `part` for `flushPendingRelationshipRemovals`. */
|
|
23
|
+
export declare function queueRelationshipRemovals(pending: RelationshipRemovals | undefined, part: Part | undefined, ids: Iterable<string>): void;
|
|
24
|
+
/** What `flushPendingRelationshipRemovals` needs of the package. */
|
|
25
|
+
interface PartsHost {
|
|
26
|
+
getPart(name: string): Part | undefined;
|
|
27
|
+
}
|
|
28
|
+
/** Settles the queued ids against each part as it now stands (`dropRelationshipsUnused`); then `pending` is empty. */
|
|
29
|
+
export declare function flushPendingRelationshipRemovals(pkg: PartsHost, pending: RelationshipRemovals): void;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The relationships content references, and the removal of those nothing references any more
|
|
3
|
+
* (CR-002 section 46, docx4j CR-043's D7): shared by the tracked-change resolution
|
|
4
|
+
* (`TrackedChange.mts`) and the pictures (`InlinePicture.delete`, section 47).
|
|
5
|
+
*/
|
|
6
|
+
import { getContextSync } from '@docx4j/generated-objects-ts';
|
|
7
|
+
const R_NS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships';
|
|
8
|
+
/** Per type, the properties that are attributes in the relationships namespace (`r:id`, `r:embed`, `r:link`, ...). */
|
|
9
|
+
const relationshipAttributes = new Map();
|
|
10
|
+
function relationshipAttributesOf(typeName) {
|
|
11
|
+
var _a, _b;
|
|
12
|
+
let names = relationshipAttributes.get(typeName);
|
|
13
|
+
if (names)
|
|
14
|
+
return names;
|
|
15
|
+
const found = [];
|
|
16
|
+
let info;
|
|
17
|
+
try {
|
|
18
|
+
info = getContextSync().getTypeInfoByName(typeName);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
info = undefined;
|
|
22
|
+
}
|
|
23
|
+
for (let ti = info; ti; ti = ti.baseTypeInfo) {
|
|
24
|
+
for (const property of (_a = ti.properties) !== null && _a !== void 0 ? _a : []) {
|
|
25
|
+
if (((_b = property.attributeName) === null || _b === void 0 ? void 0 : _b.namespaceURI) === R_NS && property.name)
|
|
26
|
+
found.push(property.name);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
relationshipAttributes.set(typeName, names = found);
|
|
30
|
+
return names;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Adds to `into` every relationship id a tree references: the `r:` attributes of its typed objects (read
|
|
34
|
+
* off the model's mapping, as docx4j reads its fields' `@XmlAttribute`), those kept in an
|
|
35
|
+
* `otherAttributes` map, and those of any DOM it holds.
|
|
36
|
+
*/
|
|
37
|
+
export function relationshipIdsIn(tree, into) {
|
|
38
|
+
const seen = new Set();
|
|
39
|
+
const visitDom = (node) => {
|
|
40
|
+
if (node.nodeType !== 1)
|
|
41
|
+
return;
|
|
42
|
+
const attributes = node.attributes;
|
|
43
|
+
for (let i = 0; i < attributes.length; i++) {
|
|
44
|
+
const a = attributes[i];
|
|
45
|
+
if (a.namespaceURI === R_NS && a.value)
|
|
46
|
+
into.add(a.value);
|
|
47
|
+
}
|
|
48
|
+
for (let c = node.firstChild; c; c = c.nextSibling)
|
|
49
|
+
visitDom(c);
|
|
50
|
+
};
|
|
51
|
+
const visit = (o) => {
|
|
52
|
+
if (typeof o !== 'object' || o === null || seen.has(o))
|
|
53
|
+
return;
|
|
54
|
+
seen.add(o);
|
|
55
|
+
if (Array.isArray(o)) {
|
|
56
|
+
for (const item of o)
|
|
57
|
+
visit(item);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (typeof o.nodeType === 'number') {
|
|
61
|
+
visitDom(o);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const record = o;
|
|
65
|
+
const typeName = record.TYPE_NAME;
|
|
66
|
+
if (typeof typeName === 'string') {
|
|
67
|
+
for (const name of relationshipAttributesOf(typeName)) {
|
|
68
|
+
const value = record[name];
|
|
69
|
+
if (typeof value === 'string' && value)
|
|
70
|
+
into.add(value);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const other = record.otherAttributes;
|
|
74
|
+
if (other && typeof other === 'object') {
|
|
75
|
+
for (const [key, value] of Object.entries(other))
|
|
76
|
+
if (key.startsWith(`{${R_NS}}`) && typeof value === 'string')
|
|
77
|
+
into.add(value);
|
|
78
|
+
}
|
|
79
|
+
for (const key in record)
|
|
80
|
+
if (key !== 'PARENT' && key !== 'otherAttributes')
|
|
81
|
+
visit(record[key]);
|
|
82
|
+
};
|
|
83
|
+
visit(tree);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The relationships of `part` among `candidates` that nothing left in the part references are removed,
|
|
87
|
+
* and the part each targeted when no relationship in the package targets it any more, with what that
|
|
88
|
+
* part alone related to (`RelationshipsPart.removeParts`). A part not unmarshalled can't be read for
|
|
89
|
+
* what still references, so nothing is removed from it.
|
|
90
|
+
*/
|
|
91
|
+
export function dropRelationshipsUnused(part, candidates) {
|
|
92
|
+
var _a, _b;
|
|
93
|
+
const rels = part.relationshipsPart;
|
|
94
|
+
const contents = part.isUnmarshalled
|
|
95
|
+
? part.contents : undefined;
|
|
96
|
+
if (!rels || contents === undefined)
|
|
97
|
+
return;
|
|
98
|
+
const used = new Set();
|
|
99
|
+
relationshipIdsIn(contents, used);
|
|
100
|
+
for (const id of candidates) {
|
|
101
|
+
if (used.has(id))
|
|
102
|
+
continue;
|
|
103
|
+
const rel = rels.getRelationshipById(id);
|
|
104
|
+
if (!rel)
|
|
105
|
+
continue;
|
|
106
|
+
const target = rels.getPart(rel);
|
|
107
|
+
rels.removeRelationship(rel);
|
|
108
|
+
if (target && target.sourceRelationships.length === 0) {
|
|
109
|
+
(_a = target.relationshipsPart) === null || _a === void 0 ? void 0 : _a.removeParts();
|
|
110
|
+
(_b = target.package) === null || _b === void 0 ? void 0 : _b.parts.remove(target.partName);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** Queues `ids` of `part` for `flushPendingRelationshipRemovals`. */
|
|
115
|
+
export function queueRelationshipRemovals(pending, part, ids) {
|
|
116
|
+
if (!pending || !part)
|
|
117
|
+
return;
|
|
118
|
+
let set = pending.get(part.partName.name);
|
|
119
|
+
for (const id of ids) {
|
|
120
|
+
if (!set)
|
|
121
|
+
pending.set(part.partName.name, set = new Set());
|
|
122
|
+
set.add(id);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** Settles the queued ids against each part as it now stands (`dropRelationshipsUnused`); then `pending` is empty. */
|
|
126
|
+
export function flushPendingRelationshipRemovals(pkg, pending) {
|
|
127
|
+
for (const [name, ids] of pending) {
|
|
128
|
+
const part = pkg.getPart(name);
|
|
129
|
+
if (part)
|
|
130
|
+
dropRelationshipsUnused(part, ids);
|
|
131
|
+
}
|
|
132
|
+
pending.clear();
|
|
133
|
+
}
|
|
@@ -43,3 +43,75 @@ export declare function matchesOf(text: string, pattern: RegExp): RegExpExecArra
|
|
|
43
43
|
* to nothing, as both languages do.
|
|
44
44
|
*/
|
|
45
45
|
export declare function expandReplacement(replacement: string, match: RegExpExecArray, options?: SearchOptions): string;
|
|
46
|
+
/** What `officeMatches` reads of a paragraph: its segments in Office JS's characters, the objects and hidden items listed. */
|
|
47
|
+
export interface SearchSegment {
|
|
48
|
+
text: string;
|
|
49
|
+
start: number;
|
|
50
|
+
object?: boolean;
|
|
51
|
+
hidden?: boolean;
|
|
52
|
+
item: {
|
|
53
|
+
name: {
|
|
54
|
+
localPart: string;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** One match, as offsets in `Paragraph.text`, with the expression's match over the search's own text. */
|
|
59
|
+
export interface OfficeMatch {
|
|
60
|
+
start: number;
|
|
61
|
+
end: number;
|
|
62
|
+
match: RegExpExecArray;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Every match of a query in a paragraph's text by Office JS's rules (check 48, Word's default view): the
|
|
66
|
+
* text is `Paragraph.text` (hidden text left out, so a query matches across it and never in it), where a
|
|
67
|
+
* picture is a place of no text that a plain query doesn't match across and `^g` matches, and an optional
|
|
68
|
+
* hyphen is passed over unless the query names it (`^-`). A plain query takes Word's Find codes
|
|
69
|
+
* (`FIND_CODES`), and its `-` matches a non-breaking hyphen too. Wildcards read the same text, the codes
|
|
70
|
+
* not translated; a regular expression (`matchRegExp`, this package's extension) reads `Paragraph.text`
|
|
71
|
+
* itself. Only text in [from, to) is read: a range's search.
|
|
72
|
+
*/
|
|
73
|
+
export declare function officeMatches(segments: readonly SearchSegment[], query: string, options?: SearchOptions, from?: number, to?: number): OfficeMatch[];
|
|
74
|
+
/** A paragraph's part in a wildcard search across its marks: its segments, the [from, to) of its text read, its text's length, whether its mark follows. */
|
|
75
|
+
export interface MarkedSegments {
|
|
76
|
+
segments: readonly SearchSegment[];
|
|
77
|
+
from: number;
|
|
78
|
+
to: number;
|
|
79
|
+
length: number;
|
|
80
|
+
mark: boolean;
|
|
81
|
+
}
|
|
82
|
+
/** A hit of a wildcard search across marks: from offset `start` in piece `first` to `end` in piece `last`, which holds its mark where `mark`. */
|
|
83
|
+
export interface MarkedMatch {
|
|
84
|
+
first: number;
|
|
85
|
+
start: number;
|
|
86
|
+
last: number;
|
|
87
|
+
end: number;
|
|
88
|
+
mark: boolean;
|
|
89
|
+
match: RegExpExecArray;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A wildcard query's hits over consecutive paragraphs, each followed by its mark where `mark` says, as Word's wildcards
|
|
93
|
+
* match a paragraph mark (check 59; CR-002 section 32, K7): `?`, `*` and a class cross one, `^13` is one. The text read
|
|
94
|
+
* in each paragraph is `officeMatches`' for wildcards; the caller gives only paragraphs a hit may span (one cell's, or
|
|
95
|
+
* none's) and only the marks it may hold (a paragraph's own, never a cell's or a row's end).
|
|
96
|
+
*/
|
|
97
|
+
export declare function wildcardMatchesAcross(pieces: readonly MarkedSegments[], query: string, options: SearchOptions): MarkedMatch[];
|
|
98
|
+
/**
|
|
99
|
+
* Refuses a wildcard replacement where a hit holds a paragraph mark (CR-002 section 32, K7): `search` finds it, as
|
|
100
|
+
* Word's does (check 59), but what replacing a mark leaves isn't measured, as for a plain `^p` (`refuseMarkReplacement`).
|
|
101
|
+
*/
|
|
102
|
+
export declare function refuseWildcardMarkReplacement(className: string, hits: readonly {
|
|
103
|
+
spansParagraphs: boolean;
|
|
104
|
+
holdsEndMark: boolean;
|
|
105
|
+
}[]): void;
|
|
106
|
+
/**
|
|
107
|
+
* A plain query split at Word's paragraph mark code, `^p`: its pieces, one more than the marks, each a plain
|
|
108
|
+
* query of its own (`^^` stays an escaped caret); undefined where the query holds no `^p`. What a `Range`'s search
|
|
109
|
+
* across paragraphs matches piece by piece (CR-014 phase 3).
|
|
110
|
+
*/
|
|
111
|
+
export declare function paragraphMarkPieces(query: string): string[] | undefined;
|
|
112
|
+
/**
|
|
113
|
+
* Refuses a replacement whose query holds `^p` (CR-014 phase 3): `search` finds such a hit, but what Office JS's
|
|
114
|
+
* replacement of a paragraph mark leaves isn't measured, and `insertText('Replace')` keeps a last mark (check 52).
|
|
115
|
+
* Until phase 3 such a query matched the two characters `^p`, where Word matches a mark.
|
|
116
|
+
*/
|
|
117
|
+
export declare function refuseMarkReplacement(className: string, find: string, options?: SearchOptions): void;
|