@docx4j/core-ts 0.3.1 → 0.4.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 +3 -1
- package/dist/html/convert.d.mts +116 -0
- package/dist/html/convert.mjs +541 -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.mjs +9 -1
- package/dist/model/content/ContentControl.d.mts +5 -1
- package/dist/model/content/ContentControl.mjs +18 -5
- package/dist/model/content/InlinePicture.d.mts +18 -3
- package/dist/model/content/InlinePicture.mjs +44 -7
- package/dist/model/content/Paragraph.d.mts +11 -11
- package/dist/model/content/Paragraph.mjs +30 -43
- package/dist/model/content/Range.mjs +3 -2
- package/dist/model/content/Table.d.mts +38 -0
- package/dist/model/content/Table.mjs +80 -2
- package/dist/model/content/index.d.mts +2 -1
- package/dist/model/content/index.mjs +2 -1
- package/dist/model/content/stylesFacade.d.mts +4 -2
- package/dist/model/content/stylesFacade.mjs +23 -6
- package/dist/model/content/tableColumns.d.mts +48 -0
- package/dist/model/content/tableColumns.mjs +484 -0
- package/dist/model/content/tracking.d.mts +14 -0
- package/dist/model/content/tracking.mjs +19 -10
- package/dist/model/customxml/CustomXmlPartCollection.d.mts +6 -1
- package/dist/model/customxml/CustomXmlPartCollection.mjs +19 -3
- package/dist/model/customxml/XmlMapping.d.mts +20 -4
- package/dist/model/customxml/XmlMapping.mjs +44 -6
- package/dist/model/customxml/bindings.d.mts +13 -3
- package/dist/model/customxml/bindings.mjs +38 -7
- package/dist/model/customxml/index.d.mts +4 -1
- package/dist/model/customxml/index.mjs +4 -1
- package/dist/model/customxml/insert.d.mts +10 -0
- package/dist/model/customxml/insert.mjs +13 -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 +54 -0
- package/dist/model/customxml/pictures.mjs +186 -0
- package/dist/model/customxml/xhtml.d.mts +42 -0
- package/dist/model/customxml/xhtml.mjs +162 -0
- package/dist/model/fonts/RunFontSelector.d.mts +8 -1
- package/dist/model/fonts/RunFontSelector.mjs +6 -5
- package/dist/model/properties/PropertyResolver.d.mts +110 -10
- package/dist/model/properties/PropertyResolver.mjs +300 -31
- package/dist/model/properties/index.d.mts +3 -0
- package/dist/model/properties/index.mjs +2 -0
- package/dist/model/properties/table.d.mts +155 -0
- package/dist/model/properties/table.mjs +315 -0
- package/dist/model/properties/tableStyleConditions.d.mts +101 -0
- package/dist/model/properties/tableStyleConditions.mjs +296 -0
- package/dist/packages/WordprocessingMLPackage.mjs +1 -0
- package/dist/parts/wml/knownStyles.d.mts +1 -0
- package/dist/parts/wml/knownStyles.mjs +2233 -0
- package/dist/parts/wml/placeholderStyle.d.mts +3 -0
- package/dist/parts/wml/placeholderStyle.mjs +13 -0
- package/package.json +7 -2
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import { deepCopy } from '@docx4j/generated-objects-ts';
|
|
2
|
+
import { tc as cellElementOf } from '@docx4j/generated-objects-ts/builders/wml';
|
|
3
|
+
import { Docx4JException } from '../../opc/exceptions.mjs';
|
|
4
|
+
import { rowsOf, cellsOf, childrenOf, linkParents, paragraphOf } from './tree.mjs';
|
|
5
|
+
import { wrapInsertedRuns } from './tracking.mjs';
|
|
6
|
+
/** The default text width in twips (A4 with 2.54 cm margins), as the objects package's `tbl` builder uses. */
|
|
7
|
+
export const DEFAULT_TEXT_WIDTH = 9026;
|
|
8
|
+
/** A table's width in fiftieths of a percent: `w:tblW w:type="pct" w:w="5000"` is the text width. */
|
|
9
|
+
const FULL_PCT = 5000;
|
|
10
|
+
/** Which of Word's three layout modes a table is in, read as Word's Table Properties reads the file. */
|
|
11
|
+
export function layoutModeOf(tbl) {
|
|
12
|
+
var _a, _b, _c, _d;
|
|
13
|
+
if (((_b = (_a = tbl.tblPr) === null || _a === void 0 ? void 0 : _a.tblLayout) === null || _b === void 0 ? void 0 : _b.type) === 'fixed')
|
|
14
|
+
return 'fixed';
|
|
15
|
+
if (((_d = (_c = tbl.tblPr) === null || _c === void 0 ? void 0 : _c.tblW) === null || _d === void 0 ? void 0 : _d.type) === 'pct')
|
|
16
|
+
return 'window';
|
|
17
|
+
return 'contents';
|
|
18
|
+
}
|
|
19
|
+
/** The section's text width in twips: the page less its margins and gutter, else the default. */
|
|
20
|
+
export function textWidthTwips(container) {
|
|
21
|
+
var _a, _b, _c, _d, _e, _f;
|
|
22
|
+
const sectPr = container === null || container === void 0 ? void 0 : container.sectPr;
|
|
23
|
+
const pgSz = sectPr === null || sectPr === void 0 ? void 0 : sectPr.pgSz;
|
|
24
|
+
if (!(pgSz === null || pgSz === void 0 ? void 0 : pgSz.w))
|
|
25
|
+
return DEFAULT_TEXT_WIDTH;
|
|
26
|
+
const width = pgSz.w - ((_b = (_a = sectPr === null || sectPr === void 0 ? void 0 : sectPr.pgMar) === null || _a === void 0 ? void 0 : _a.left) !== null && _b !== void 0 ? _b : 0) - ((_d = (_c = sectPr === null || sectPr === void 0 ? void 0 : sectPr.pgMar) === null || _c === void 0 ? void 0 : _c.right) !== null && _d !== void 0 ? _d : 0) - ((_f = (_e = sectPr === null || sectPr === void 0 ? void 0 : sectPr.pgMar) === null || _e === void 0 ? void 0 : _e.gutter) !== null && _f !== void 0 ? _f : 0);
|
|
27
|
+
return width > 0 ? width : DEFAULT_TEXT_WIDTH;
|
|
28
|
+
}
|
|
29
|
+
function rowMember(tr, name) {
|
|
30
|
+
var _a, _b;
|
|
31
|
+
return ((_b = (_a = tr.trPr) === null || _a === void 0 ? void 0 : _a.cnfStyleOrDivIdOrGridBefore) !== null && _b !== void 0 ? _b : []).find((item) => { var _a; return ((_a = item.name) === null || _a === void 0 ? void 0 : _a.localPart) === name; });
|
|
32
|
+
}
|
|
33
|
+
function spanOf(tc) {
|
|
34
|
+
var _a, _b, _c;
|
|
35
|
+
return Number((_c = (_b = (_a = tc.tcPr) === null || _a === void 0 ? void 0 : _a.gridSpan) === null || _b === void 0 ? void 0 : _b.val) !== null && _c !== void 0 ? _c : 1) || 1;
|
|
36
|
+
}
|
|
37
|
+
function setSpan(tc, count) {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
const tcPr = ((_a = tc.tcPr) !== null && _a !== void 0 ? _a : (tc.tcPr = {}));
|
|
40
|
+
if (count > 1)
|
|
41
|
+
tcPr.gridSpan = { ...((_b = tcPr.gridSpan) !== null && _b !== void 0 ? _b : {}), val: count };
|
|
42
|
+
else
|
|
43
|
+
delete tcPr.gridSpan;
|
|
44
|
+
}
|
|
45
|
+
/** The grid's column widths in twips; a table without a `w:tblGrid` gets one from its first row (equal columns over the text width when the cells state none). */
|
|
46
|
+
export function gridWidthsOf(tbl, textWidth) {
|
|
47
|
+
var _a, _b, _c, _d;
|
|
48
|
+
const grid = (_a = tbl.tblGrid) === null || _a === void 0 ? void 0 : _a.gridCol;
|
|
49
|
+
if (grid && grid.length > 0)
|
|
50
|
+
return grid.map((col) => { var _a; return Number((_a = col.w) !== null && _a !== void 0 ? _a : 0); });
|
|
51
|
+
const first = rowsOf(tbl)[0];
|
|
52
|
+
const cells = first ? cellsOf(first.element.value) : [];
|
|
53
|
+
const columns = Math.max(1, cells.reduce((n, cell) => n + spanOf(cell.element.value), 0));
|
|
54
|
+
const each = Math.floor(textWidth / columns);
|
|
55
|
+
const widths = [];
|
|
56
|
+
for (const cell of cells) {
|
|
57
|
+
const tc = cell.element.value;
|
|
58
|
+
const span = spanOf(tc);
|
|
59
|
+
const stated = ((_c = (_b = tc.tcPr) === null || _b === void 0 ? void 0 : _b.tcW) === null || _c === void 0 ? void 0 : _c.type) === 'dxa' ? Number((_d = tc.tcPr.tcW.w) !== null && _d !== void 0 ? _d : 0) : 0;
|
|
60
|
+
for (let i = 0; i < span; i++)
|
|
61
|
+
widths.push(stated > 0 ? Math.floor(stated / span) : each);
|
|
62
|
+
}
|
|
63
|
+
return widths.length > 0 ? widths : [each];
|
|
64
|
+
}
|
|
65
|
+
function writeGrid(tbl, widths) {
|
|
66
|
+
var _a;
|
|
67
|
+
const grid = ((_a = tbl.tblGrid) !== null && _a !== void 0 ? _a : (tbl.tblGrid = {}));
|
|
68
|
+
grid.gridCol = widths.map((w) => ({ TYPE_NAME: 'org_docx4j_wml.TblGridCol', w }));
|
|
69
|
+
linkParents(grid, tbl);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Widths rescaled to a total: each floored, the remainder given to the columns with the largest
|
|
73
|
+
* fractions, the earlier first on a tie, so that equal columns inserted and then removed come
|
|
74
|
+
* back equal (Word's own grid, 3005/3006/3006, is its layout's measurement and not reproduced).
|
|
75
|
+
*/
|
|
76
|
+
function rescaled(widths, total) {
|
|
77
|
+
const sum = widths.reduce((a, b) => a + b, 0);
|
|
78
|
+
if (sum <= 0 || total <= 0)
|
|
79
|
+
return widths.slice();
|
|
80
|
+
const exact = widths.map((w) => (w * total) / sum);
|
|
81
|
+
const out = exact.map((w) => Math.floor(w));
|
|
82
|
+
let remainder = total - out.reduce((a, b) => a + b, 0);
|
|
83
|
+
const order = exact.map((w, i) => ({ i, fraction: w - Math.floor(w) })).sort((a, b) => b.fraction - a.fraction || a.i - b.i);
|
|
84
|
+
for (const { i } of order) {
|
|
85
|
+
if (remainder <= 0)
|
|
86
|
+
break;
|
|
87
|
+
out[i] += 1;
|
|
88
|
+
remainder--;
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
/** A cell's share of the table in fiftieths of a percent over grid columns [start, end): the cumulative shares floored, as Word writes them. */
|
|
93
|
+
function pctShare(widths, start, end) {
|
|
94
|
+
const total = widths.reduce((a, b) => a + b, 0);
|
|
95
|
+
if (total <= 0)
|
|
96
|
+
return 0;
|
|
97
|
+
const cumulative = (to) => Math.floor((FULL_PCT * widths.slice(0, to).reduce((a, b) => a + b, 0)) / total);
|
|
98
|
+
return cumulative(end) - cumulative(start);
|
|
99
|
+
}
|
|
100
|
+
/** The form a cell's width takes: its own `w:tcW` type, else the table's `w:tblW` type, else twips (section 42.2 item 4). */
|
|
101
|
+
export function widthTypeOf(tc, tbl) {
|
|
102
|
+
var _a, _b, _c, _d, _e, _f;
|
|
103
|
+
return (_f = (_c = (_b = (_a = tc === null || tc === void 0 ? void 0 : tc.tcPr) === null || _a === void 0 ? void 0 : _a.tcW) === null || _b === void 0 ? void 0 : _b.type) !== null && _c !== void 0 ? _c : (_e = (_d = tbl.tblPr) === null || _d === void 0 ? void 0 : _d.tblW) === null || _e === void 0 ? void 0 : _e.type) !== null && _f !== void 0 ? _f : 'dxa';
|
|
104
|
+
}
|
|
105
|
+
/** Writes a cell's `w:tcW` for the grid columns it covers, in the form it takes; `auto` and `nil` widths are left as they are. */
|
|
106
|
+
function writeCellWidth(tc, tbl, widths, start, end) {
|
|
107
|
+
var _a, _b, _c;
|
|
108
|
+
const type = widthTypeOf(tc, tbl);
|
|
109
|
+
const tcPr = ((_a = tc.tcPr) !== null && _a !== void 0 ? _a : (tc.tcPr = {}));
|
|
110
|
+
if (type === 'pct')
|
|
111
|
+
tcPr.tcW = { ...((_b = tcPr.tcW) !== null && _b !== void 0 ? _b : {}), w: pctShare(widths, start, end), type: 'pct' };
|
|
112
|
+
else if (type === 'dxa')
|
|
113
|
+
tcPr.tcW = { ...((_c = tcPr.tcW) !== null && _c !== void 0 ? _c : {}), w: widths.slice(start, end).reduce((a, b) => a + b, 0), type: 'dxa' };
|
|
114
|
+
else if (!tcPr.tcW)
|
|
115
|
+
tcPr.tcW = { w: 0, type };
|
|
116
|
+
}
|
|
117
|
+
/** The typed object a row's cell array belongs to: the row, or a cell-level control's content. */
|
|
118
|
+
function ownerOfCells(tr, container) {
|
|
119
|
+
var _a, _b;
|
|
120
|
+
if (tr.content === container)
|
|
121
|
+
return tr;
|
|
122
|
+
for (const item of ((_a = tr.content) !== null && _a !== void 0 ? _a : [])) {
|
|
123
|
+
const v = item.value;
|
|
124
|
+
if (v && typeof v === 'object' && ((_b = v.sdtContent) === null || _b === void 0 ? void 0 : _b.content) === container)
|
|
125
|
+
return v.sdtContent;
|
|
126
|
+
}
|
|
127
|
+
return tr;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The cell properties a new cell takes from the cell to the right of the boundary: its width (the
|
|
131
|
+
* form; the value is written from the grid) and its shading, and nothing else - check 41 saw Word
|
|
132
|
+
* copy the shading and leave the borders and the vertical alignment behind.
|
|
133
|
+
*/
|
|
134
|
+
const COPIED = ['tcW', 'shd'];
|
|
135
|
+
function cellPropertiesFrom(template) {
|
|
136
|
+
if (!(template === null || template === void 0 ? void 0 : template.tcPr))
|
|
137
|
+
return undefined;
|
|
138
|
+
const out = {};
|
|
139
|
+
for (const [key, value] of Object.entries(template.tcPr))
|
|
140
|
+
if (COPIED.includes(key))
|
|
141
|
+
out[key] = deepCopy(value);
|
|
142
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
143
|
+
}
|
|
144
|
+
/** The paragraph properties a new cell's paragraph takes: the template's first paragraph's, its mark's run properties included, less any revision (check 41: centred bold, right-aligned italic, as the neighbour's). */
|
|
145
|
+
function paragraphPropertiesFrom(template) {
|
|
146
|
+
var _a, _b;
|
|
147
|
+
const first = ((_a = childrenOf(template !== null && template !== void 0 ? template : {})) !== null && _a !== void 0 ? _a : []).find((item) => { var _a; return ((_a = item.value) === null || _a === void 0 ? void 0 : _a.TYPE_NAME) === 'org_docx4j_wml.P'; });
|
|
148
|
+
const pPr = (_b = first === null || first === void 0 ? void 0 : first.value) === null || _b === void 0 ? void 0 : _b.pPr;
|
|
149
|
+
if (!pPr)
|
|
150
|
+
return undefined;
|
|
151
|
+
const out = {};
|
|
152
|
+
for (const [key, value] of Object.entries(pPr))
|
|
153
|
+
if (!['pPrChange', 'sectPr', 'PARENT'].includes(key))
|
|
154
|
+
out[key] = deepCopy(value);
|
|
155
|
+
const rPr = out.rPr;
|
|
156
|
+
if (rPr)
|
|
157
|
+
for (const key of ['ins', 'del', 'rPrChange', 'moveFrom', 'moveTo'])
|
|
158
|
+
delete rPr[key];
|
|
159
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
160
|
+
}
|
|
161
|
+
/** The new cells of one row, each a `w:tc` of one paragraph with the template's shading and paragraph properties. */
|
|
162
|
+
function newCells(count, values, template) {
|
|
163
|
+
var _a, _b;
|
|
164
|
+
const out = [];
|
|
165
|
+
for (let i = 0; i < count; i++) {
|
|
166
|
+
// an empty cell holds an empty `w:p`, as Word writes one; a value its text
|
|
167
|
+
const text = (_a = values === null || values === void 0 ? void 0 : values[i]) !== null && _a !== void 0 ? _a : '';
|
|
168
|
+
const cell = cellElementOf(text === '' ? [paragraphOf([])] : text);
|
|
169
|
+
const tcPr = cellPropertiesFrom(template);
|
|
170
|
+
if (tcPr)
|
|
171
|
+
cell.value.tcPr = tcPr;
|
|
172
|
+
else
|
|
173
|
+
delete cell.value.tcPr;
|
|
174
|
+
const pPr = paragraphPropertiesFrom(template);
|
|
175
|
+
const paragraph = ((_b = childrenOf(cell.value)) !== null && _b !== void 0 ? _b : []).find((item) => item.value.TYPE_NAME === 'org_docx4j_wml.P');
|
|
176
|
+
if (pPr && paragraph) {
|
|
177
|
+
paragraph.value.pPr = pPr;
|
|
178
|
+
linkParents(pPr, paragraph.value);
|
|
179
|
+
}
|
|
180
|
+
out.push(cell);
|
|
181
|
+
}
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Inserts `count` grid columns at `gridIndex` (0 to the column count). The new column copies the
|
|
186
|
+
* width of the column to the right of the boundary when there is one, else the left (check 40:
|
|
187
|
+
* Insert Right in a narrow cell beside a wide one took the wide width; Insert Right in a spanning
|
|
188
|
+
* cell took the column after it), and each row's new cell takes the properties of the cell on the
|
|
189
|
+
* same side, less its span and merges. A cell spanning the boundary keeps its span and the new
|
|
190
|
+
* cell goes before it (check 40b1); a row's `w:gridBefore` or `w:gridAfter` covering the boundary
|
|
191
|
+
* grows instead. `values` is by row, then by new column. While changes are tracked the table
|
|
192
|
+
* change is recorded as Word's Office JS records one (check 40c): the old properties, every
|
|
193
|
+
* cell's properties - the new cells' as made - the grid as it is after, and the new cells' text in
|
|
194
|
+
* a `w:ins`, with no mark insertion.
|
|
195
|
+
*/
|
|
196
|
+
export function insertGridColumns(tbl, textWidth, gridIndex, count, values, tracker) {
|
|
197
|
+
var _a, _b;
|
|
198
|
+
if (!Number.isInteger(count) || count < 1)
|
|
199
|
+
throw new Docx4JException(`columnCount must be a positive integer, not ${count}`);
|
|
200
|
+
const old = gridWidthsOf(tbl, textWidth);
|
|
201
|
+
if (gridIndex < 0 || gridIndex > old.length)
|
|
202
|
+
throw new Docx4JException(`No column boundary ${gridIndex} in a grid of ${old.length} columns`);
|
|
203
|
+
const mode = layoutModeOf(tbl);
|
|
204
|
+
const copied = old[gridIndex < old.length ? gridIndex : old.length - 1];
|
|
205
|
+
const unscaled = [...old.slice(0, gridIndex), ...Array.from({ length: count }, () => copied), ...old.slice(gridIndex)];
|
|
206
|
+
const widths = mode === 'window' ? rescaled(unscaled, old.reduce((a, b) => a + b, 0)) : unscaled;
|
|
207
|
+
const rows = rowsOf(tbl).map((row) => row.element.value);
|
|
208
|
+
const plans = rows.map((tr) => planRow(tr, gridIndex));
|
|
209
|
+
// an AutoFit-to-contents add changes no width, and Office JS records nothing for it (check 41c)
|
|
210
|
+
const record = tracker && mode !== 'contents' ? recordTable(tbl, rows, tracker) : undefined;
|
|
211
|
+
const result = { cells: [] };
|
|
212
|
+
plans.forEach((plan, rowIndex) => {
|
|
213
|
+
var _a, _b;
|
|
214
|
+
const tr = plan.tr;
|
|
215
|
+
if (plan.skipped) {
|
|
216
|
+
plan.skipped.value.val = Number((_a = plan.skipped.value.val) !== null && _a !== void 0 ? _a : 0) + count;
|
|
217
|
+
result.cells.push([]);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const cells = newCells(count, values === null || values === void 0 ? void 0 : values[rowIndex], plan.template);
|
|
221
|
+
plan.container.splice(plan.at, 0, ...cells);
|
|
222
|
+
const owner = ownerOfCells(tr, plan.container);
|
|
223
|
+
for (const cell of cells)
|
|
224
|
+
linkParents(cell, owner);
|
|
225
|
+
if (tracker)
|
|
226
|
+
for (const cell of cells)
|
|
227
|
+
for (const block of (_b = childrenOf(cell.value)) !== null && _b !== void 0 ? _b : [])
|
|
228
|
+
wrapInsertedRuns(tracker, childrenOf(block.value), block.value);
|
|
229
|
+
result.cells.push(cells);
|
|
230
|
+
});
|
|
231
|
+
// the grid, the table's width, and the cells' widths in their forms
|
|
232
|
+
writeGrid(tbl, widths);
|
|
233
|
+
const tblW = (_a = tbl.tblPr) === null || _a === void 0 ? void 0 : _a.tblW;
|
|
234
|
+
if (mode === 'fixed' && (tblW === null || tblW === void 0 ? void 0 : tblW.type) === 'dxa')
|
|
235
|
+
tblW.w = Number((_b = tblW.w) !== null && _b !== void 0 ? _b : 0) + copied * count;
|
|
236
|
+
writeCellWidths(tbl, widths, mode === 'window' ? 'all' : result.cells.flat().map((c) => c.value));
|
|
237
|
+
if (record)
|
|
238
|
+
record(result.cells.flat().map((c) => c.value), { unscaled, scaled: widths });
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
/** Where the boundary falls in a row: at or inside a cell (the new cells go before it), in its skipped columns, or after its last cell. */
|
|
242
|
+
function planRow(tr, gridIndex) {
|
|
243
|
+
var _a, _b, _c;
|
|
244
|
+
const before = rowMember(tr, 'gridBefore');
|
|
245
|
+
const after = rowMember(tr, 'gridAfter');
|
|
246
|
+
const skippedBefore = Number((_a = before === null || before === void 0 ? void 0 : before.value.val) !== null && _a !== void 0 ? _a : 0);
|
|
247
|
+
if (before && gridIndex < skippedBefore)
|
|
248
|
+
return { tr, skipped: before };
|
|
249
|
+
const cells = cellsOf(tr);
|
|
250
|
+
let at = skippedBefore;
|
|
251
|
+
for (const cell of cells) {
|
|
252
|
+
const tc = cell.element.value;
|
|
253
|
+
const end = at + spanOf(tc);
|
|
254
|
+
// at the cell's start, or inside its span (check 40b1: the span kept, the new cell before it)
|
|
255
|
+
if (gridIndex >= at && gridIndex < end)
|
|
256
|
+
return { tr, container: cell.container, at: cell.container.indexOf(cell.element), template: tc };
|
|
257
|
+
at = end;
|
|
258
|
+
}
|
|
259
|
+
const skippedAfter = Number((_b = after === null || after === void 0 ? void 0 : after.value.val) !== null && _b !== void 0 ? _b : 0);
|
|
260
|
+
if (after && gridIndex < at + skippedAfter)
|
|
261
|
+
return { tr, skipped: after };
|
|
262
|
+
const last = cells[cells.length - 1];
|
|
263
|
+
if (!last)
|
|
264
|
+
return { tr, container: ((_c = tr.content) !== null && _c !== void 0 ? _c : (tr.content = [])), at: tr.content.length, template: undefined };
|
|
265
|
+
return { tr, container: last.container, at: last.container.indexOf(last.element) + 1, template: last.element.value };
|
|
266
|
+
}
|
|
267
|
+
/** Every cell's `w:tcW` from the grid (`all`), or only the given cells', each in the form it takes. */
|
|
268
|
+
function writeCellWidths(tbl, widths, which) {
|
|
269
|
+
var _a, _b;
|
|
270
|
+
for (const row of rowsOf(tbl)) {
|
|
271
|
+
const tr = row.element.value;
|
|
272
|
+
let at = Number((_b = (_a = rowMember(tr, 'gridBefore')) === null || _a === void 0 ? void 0 : _a.value.val) !== null && _b !== void 0 ? _b : 0);
|
|
273
|
+
for (const cell of cellsOf(tr)) {
|
|
274
|
+
const tc = cell.element.value;
|
|
275
|
+
const end = at + spanOf(tc);
|
|
276
|
+
if (which === 'all' || which.includes(tc))
|
|
277
|
+
writeCellWidth(tc, tbl, widths, at, end);
|
|
278
|
+
at = end;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* What Word records for a column added or removed through Office JS with tracking on (checks 40c
|
|
284
|
+
* and 41c, the form of section 35's table change): a `w:tblPrChange` with the properties as they
|
|
285
|
+
* were, a `w:tcPrChange` on every cell with its properties as they were - a new cell's as made -
|
|
286
|
+
* and a `w:tblGridChange` holding the grid **as it is after the change**. Where the change
|
|
287
|
+
* rescaled the columns (AutoFit to window), the recorded grid is instead the old and new column
|
|
288
|
+
* edges together - the unscaled grid's edges and the scaled grid's, merged - with every cell's
|
|
289
|
+
* record spanning the merged columns its unscaled extent covers, and a new cell's recorded width a
|
|
290
|
+
* placeholder (`1 pct`; check 27's form, as Word wrote it in check 41c), so that rejecting puts
|
|
291
|
+
* the old widths back over a grid that `collapseGrid` then folds. Taken before the change and
|
|
292
|
+
* written after it, so that `TrackedChange` lists the whole as one table change. A record already
|
|
293
|
+
* there (an earlier pending change) is kept, so the original state stays the recorded one.
|
|
294
|
+
*/
|
|
295
|
+
function recordTable(tbl, rows, tracker) {
|
|
296
|
+
var _a, _b;
|
|
297
|
+
const tblPr = tbl.tblPr;
|
|
298
|
+
const oldTblPr = tblPr && !tblPr.tblPrChange ? without(tblPr, ['tblPrChange']) : undefined;
|
|
299
|
+
const recordGrid = !((_a = tbl.tblGrid) === null || _a === void 0 ? void 0 : _a.tblGridChange);
|
|
300
|
+
const oldCells = [];
|
|
301
|
+
for (const tr of rows)
|
|
302
|
+
for (const cell of cellsOf(tr)) {
|
|
303
|
+
const tc = cell.element.value;
|
|
304
|
+
if (!((_b = tc.tcPr) === null || _b === void 0 ? void 0 : _b.tcPrChange))
|
|
305
|
+
oldCells.push([tc, recordedCellProperties(tc)]);
|
|
306
|
+
}
|
|
307
|
+
return (added = [], rescale) => {
|
|
308
|
+
var _a, _b, _c;
|
|
309
|
+
if (oldTblPr && tbl.tblPr) {
|
|
310
|
+
tbl.tblPr.tblPrChange = { TYPE_NAME: 'org_docx4j_wml.CTTblPrChange', ...tracker.markup(), tblPr: { TYPE_NAME: 'org_docx4j_wml.CTTblPrBase', ...oldTblPr } };
|
|
311
|
+
linkParents(tbl.tblPr.tblPrChange, tbl.tblPr);
|
|
312
|
+
}
|
|
313
|
+
const merged = rescale && rescale.unscaled.some((w, i) => w !== rescale.scaled[i]) ? mergedEdges(rescale.unscaled, rescale.scaled) : undefined;
|
|
314
|
+
if (recordGrid && tbl.tblGrid) {
|
|
315
|
+
const gridCol = merged ? merged.widths.map((w) => ({ TYPE_NAME: 'org_docx4j_wml.TblGridCol', w })) : ((_a = tbl.tblGrid.gridCol) !== null && _a !== void 0 ? _a : []).map((col) => deepCopy(col));
|
|
316
|
+
tbl.tblGrid.tblGridChange = { TYPE_NAME: 'org_docx4j_wml.CTTblGridChange', id: tracker.nextId(), tblGrid: { TYPE_NAME: 'org_docx4j_wml.TblGridBase', gridCol } };
|
|
317
|
+
linkParents(tbl.tblGrid.tblGridChange, tbl.tblGrid);
|
|
318
|
+
}
|
|
319
|
+
const extents = merged ? cellExtents(tbl) : undefined;
|
|
320
|
+
const cells = [...oldCells.filter(([tc]) => isInTable(tc, tbl)), ...added.map((tc) => [tc, recordedCellProperties(tc)])];
|
|
321
|
+
for (const [tc, old] of cells) {
|
|
322
|
+
const tcPr = ((_b = tc.tcPr) !== null && _b !== void 0 ? _b : (tc.tcPr = {}));
|
|
323
|
+
const recorded = { ...old };
|
|
324
|
+
if (merged && extents) {
|
|
325
|
+
// over the merged grid: the columns the cell's unscaled extent covers
|
|
326
|
+
const extent = extents.get(tc);
|
|
327
|
+
if (extent) {
|
|
328
|
+
const span = merged.index(unscaledEdge(rescale.unscaled, extent.end)) - merged.index(unscaledEdge(rescale.unscaled, extent.start));
|
|
329
|
+
if (span > 1)
|
|
330
|
+
recorded.gridSpan = { val: span };
|
|
331
|
+
else
|
|
332
|
+
delete recorded.gridSpan;
|
|
333
|
+
}
|
|
334
|
+
if (added.includes(tc) && ((_c = recorded.tcW) === null || _c === void 0 ? void 0 : _c.type) === 'pct')
|
|
335
|
+
recorded.tcW = { w: 1, type: 'pct' };
|
|
336
|
+
}
|
|
337
|
+
tcPr.tcPrChange = { TYPE_NAME: 'org_docx4j_wml.CTTcPrChange', ...tracker.markup(), tcPr: { TYPE_NAME: 'org_docx4j_wml.TcPrInner', ...recorded } };
|
|
338
|
+
linkParents(tcPr.tcPrChange, tcPr);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
/** The cumulative edge, in twips, of a column index over a grid. */
|
|
343
|
+
function unscaledEdge(widths, column) {
|
|
344
|
+
return widths.slice(0, column).reduce((a, b) => a + b, 0);
|
|
345
|
+
}
|
|
346
|
+
/** The old and new grids' edges together, as merged columns, and where an edge falls among them. */
|
|
347
|
+
function mergedEdges(unscaled, scaled) {
|
|
348
|
+
const edges = new Set([0]);
|
|
349
|
+
let at = 0;
|
|
350
|
+
for (const w of unscaled) {
|
|
351
|
+
at += w;
|
|
352
|
+
edges.add(at);
|
|
353
|
+
}
|
|
354
|
+
at = 0;
|
|
355
|
+
for (const w of scaled) {
|
|
356
|
+
at += w;
|
|
357
|
+
edges.add(at);
|
|
358
|
+
}
|
|
359
|
+
const sorted = [...edges].sort((a, b) => a - b);
|
|
360
|
+
return { widths: sorted.slice(1).map((edge, i) => edge - sorted[i]), index: (edge) => sorted.indexOf(edge) };
|
|
361
|
+
}
|
|
362
|
+
/** Each cell's column extent in the grid as it is now, through the rows' spans and skipped columns. */
|
|
363
|
+
function cellExtents(tbl) {
|
|
364
|
+
var _a, _b;
|
|
365
|
+
const out = new Map();
|
|
366
|
+
for (const row of rowsOf(tbl)) {
|
|
367
|
+
const tr = row.element.value;
|
|
368
|
+
let at = Number((_b = (_a = rowMember(tr, 'gridBefore')) === null || _a === void 0 ? void 0 : _a.value.val) !== null && _b !== void 0 ? _b : 0);
|
|
369
|
+
for (const cell of cellsOf(tr)) {
|
|
370
|
+
const tc = cell.element.value;
|
|
371
|
+
const end = at + spanOf(tc);
|
|
372
|
+
out.set(tc, { start: at, end });
|
|
373
|
+
at = end;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return out;
|
|
377
|
+
}
|
|
378
|
+
/** A cell's properties as a record holds them: everything but the markers and an earlier record. */
|
|
379
|
+
function recordedCellProperties(tc) {
|
|
380
|
+
return without(tc.tcPr, ['tcPrChange', 'cellIns', 'cellDel', 'cellMerge']);
|
|
381
|
+
}
|
|
382
|
+
/** Whether a cell is still in the table (a deleted column's cells are not). */
|
|
383
|
+
function isInTable(tc, tbl) {
|
|
384
|
+
return rowsOf(tbl).some((row) => cellsOf(row.element.value).some((cell) => cell.element.value === tc));
|
|
385
|
+
}
|
|
386
|
+
function without(value, keys) {
|
|
387
|
+
const out = {};
|
|
388
|
+
for (const [key, v] of Object.entries(value !== null && value !== void 0 ? value : {}))
|
|
389
|
+
if (!keys.includes(key) && key !== 'PARENT')
|
|
390
|
+
out[key] = deepCopy(v);
|
|
391
|
+
return out;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Removes `count` grid columns from `columnIndex`: a cell covering only removed columns goes, one
|
|
395
|
+
* spanning further shrinks, a row's skipped columns shrink; the grid loses the columns, a `w:tblW`
|
|
396
|
+
* in twips loses their width, and in window mode the grid is rescaled to the width it had and the
|
|
397
|
+
* cells' percentages written again. While changes are tracked the cells still go, untracked, as
|
|
398
|
+
* Word's own Delete Columns and Office JS's `deleteColumns` leave them (known issues, entry 2;
|
|
399
|
+
* check 40c), and the table change is recorded as Office JS records it: the old properties, the
|
|
400
|
+
* remaining cells' properties, the grid as it is after.
|
|
401
|
+
*/
|
|
402
|
+
export function deleteGridColumns(tbl, textWidth, columnIndex, count, tracker) {
|
|
403
|
+
var _a, _b, _c, _d;
|
|
404
|
+
if (!Number.isInteger(count) || count < 1)
|
|
405
|
+
throw new Docx4JException(`columnCount must be a positive integer, not ${count}`);
|
|
406
|
+
const old = gridWidthsOf(tbl, textWidth);
|
|
407
|
+
if (columnIndex < 0 || columnIndex + count > old.length)
|
|
408
|
+
throw new Docx4JException(`No columns ${columnIndex} to ${columnIndex + count - 1} in a grid of ${old.length} columns`);
|
|
409
|
+
const mode = layoutModeOf(tbl);
|
|
410
|
+
const record = tracker && mode !== 'contents' ? recordTable(tbl, rowsOf(tbl).map((row) => row.element.value), tracker) : undefined;
|
|
411
|
+
const from = columnIndex;
|
|
412
|
+
const to = columnIndex + count;
|
|
413
|
+
const overlap = (start, end) => Math.max(0, Math.min(end, to) - Math.max(start, from));
|
|
414
|
+
for (const row of rowsOf(tbl)) {
|
|
415
|
+
const tr = row.element.value;
|
|
416
|
+
const before = rowMember(tr, 'gridBefore');
|
|
417
|
+
const skippedBefore = Number((_a = before === null || before === void 0 ? void 0 : before.value.val) !== null && _a !== void 0 ? _a : 0);
|
|
418
|
+
if (before)
|
|
419
|
+
before.value.val = skippedBefore - overlap(0, skippedBefore);
|
|
420
|
+
let at = skippedBefore;
|
|
421
|
+
for (const cell of cellsOf(tr)) {
|
|
422
|
+
const tc = cell.element.value;
|
|
423
|
+
const span = spanOf(tc);
|
|
424
|
+
const end = at + span;
|
|
425
|
+
const covered = overlap(at, end);
|
|
426
|
+
if (covered === span) {
|
|
427
|
+
const i = cell.container.indexOf(cell.element);
|
|
428
|
+
if (i >= 0)
|
|
429
|
+
cell.container.splice(i, 1);
|
|
430
|
+
}
|
|
431
|
+
else if (covered > 0) {
|
|
432
|
+
setSpan(tc, span - covered);
|
|
433
|
+
}
|
|
434
|
+
at = end;
|
|
435
|
+
}
|
|
436
|
+
const after = rowMember(tr, 'gridAfter');
|
|
437
|
+
if (after) {
|
|
438
|
+
const skippedAfter = Number((_b = after.value.val) !== null && _b !== void 0 ? _b : 0);
|
|
439
|
+
after.value.val = skippedAfter - overlap(at, at + skippedAfter);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
const removed = old.slice(from, to).reduce((a, b) => a + b, 0);
|
|
443
|
+
let widths = [...old.slice(0, from), ...old.slice(to)];
|
|
444
|
+
if (mode === 'window')
|
|
445
|
+
widths = rescaled(widths, old.reduce((a, b) => a + b, 0));
|
|
446
|
+
writeGrid(tbl, widths);
|
|
447
|
+
const tblW = (_c = tbl.tblPr) === null || _c === void 0 ? void 0 : _c.tblW;
|
|
448
|
+
if ((tblW === null || tblW === void 0 ? void 0 : tblW.type) === 'dxa')
|
|
449
|
+
tblW.w = Math.max(0, Number((_d = tblW.w) !== null && _d !== void 0 ? _d : 0) - removed);
|
|
450
|
+
writeCellWidths(tbl, widths, mode === 'window' ? 'all' : shrunkCells(tbl));
|
|
451
|
+
if (record)
|
|
452
|
+
record();
|
|
453
|
+
}
|
|
454
|
+
/** The cells whose span no longer matches their stated twips width: those that shrank. */
|
|
455
|
+
function shrunkCells(tbl) {
|
|
456
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
457
|
+
const out = [];
|
|
458
|
+
const widths = (_c = (_b = (_a = tbl.tblGrid) === null || _a === void 0 ? void 0 : _a.gridCol) === null || _b === void 0 ? void 0 : _b.map((col) => { var _a; return Number((_a = col.w) !== null && _a !== void 0 ? _a : 0); })) !== null && _c !== void 0 ? _c : [];
|
|
459
|
+
for (const row of rowsOf(tbl)) {
|
|
460
|
+
const tr = row.element.value;
|
|
461
|
+
let at = Number((_e = (_d = rowMember(tr, 'gridBefore')) === null || _d === void 0 ? void 0 : _d.value.val) !== null && _e !== void 0 ? _e : 0);
|
|
462
|
+
for (const cell of cellsOf(tr)) {
|
|
463
|
+
const tc = cell.element.value;
|
|
464
|
+
const end = at + spanOf(tc);
|
|
465
|
+
const tcW = (_f = tc.tcPr) === null || _f === void 0 ? void 0 : _f.tcW;
|
|
466
|
+
if ((tcW === null || tcW === void 0 ? void 0 : tcW.type) === 'dxa' && Number((_g = tcW.w) !== null && _g !== void 0 ? _g : 0) !== widths.slice(at, end).reduce((a, b) => a + b, 0))
|
|
467
|
+
out.push(tc);
|
|
468
|
+
at = end;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return out;
|
|
472
|
+
}
|
|
473
|
+
/** The grid columns a cell covers: its start and the one past its end. */
|
|
474
|
+
export function gridRangeOf(tr, tc) {
|
|
475
|
+
var _a, _b;
|
|
476
|
+
let at = Number((_b = (_a = rowMember(tr, 'gridBefore')) === null || _a === void 0 ? void 0 : _a.value.val) !== null && _b !== void 0 ? _b : 0);
|
|
477
|
+
for (const cell of cellsOf(tr)) {
|
|
478
|
+
const end = at + spanOf(cell.element.value);
|
|
479
|
+
if (cell.element.value === tc)
|
|
480
|
+
return { start: at, end };
|
|
481
|
+
at = end;
|
|
482
|
+
}
|
|
483
|
+
return undefined;
|
|
484
|
+
}
|
|
@@ -92,6 +92,20 @@ export declare function canonical(value: unknown): string;
|
|
|
92
92
|
export interface TrackedPart {
|
|
93
93
|
readonly contents?: unknown;
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Makes sure the part's root lists a prefix in `mc:Ignorable`, appending it when absent, so that
|
|
97
|
+
* content in that prefix's namespace may be written in it; the facade then declares the prefix.
|
|
98
|
+
* This is docx4j's pattern - the writer of the content adds its prefix
|
|
99
|
+
* (`Paginate.declareW14Ignorable`) - and Jason's decision (CR-002 section 29). Since objects 0.3.1
|
|
100
|
+
* (its CR-008) the marshaller does it for every writer as well; these calls stay as the writer's
|
|
101
|
+
* own statement. False when there is no unmarshalled root to put it on. Every part a
|
|
102
|
+
* `Body` is over (document, header, footer, notes, comments) has a root that binds `mc:Ignorable`.
|
|
103
|
+
*
|
|
104
|
+
* The prefix must be the facade's conventional one for its namespace (`w15`, `w16du`): an
|
|
105
|
+
* application that does not know the namespace (Word 2010 and `w15`) opens the part only when the
|
|
106
|
+
* prefix is ignorable, as Word's own files declare it (CR-002 section 40).
|
|
107
|
+
*/
|
|
108
|
+
export declare function declareIgnorable(part: TrackedPart | undefined, prefix: string): boolean;
|
|
95
109
|
/**
|
|
96
110
|
* The per-package writer of revision markup. Created by the package while
|
|
97
111
|
* `changeTrackingMode` is not `Off`; every content-API mutation asks its `Body` for one and,
|
|
@@ -105,24 +105,33 @@ export function canonical(value) {
|
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
107
|
/**
|
|
108
|
-
* Makes sure the part's root lists
|
|
109
|
-
*
|
|
110
|
-
* pattern - the writer of the content adds its prefix
|
|
111
|
-
* decision (CR-002 section 29)
|
|
112
|
-
*
|
|
113
|
-
*
|
|
108
|
+
* Makes sure the part's root lists a prefix in `mc:Ignorable`, appending it when absent, so that
|
|
109
|
+
* content in that prefix's namespace may be written in it; the facade then declares the prefix.
|
|
110
|
+
* This is docx4j's pattern - the writer of the content adds its prefix
|
|
111
|
+
* (`Paginate.declareW14Ignorable`) - and Jason's decision (CR-002 section 29). Since objects 0.3.1
|
|
112
|
+
* (its CR-008) the marshaller does it for every writer as well; these calls stay as the writer's
|
|
113
|
+
* own statement. False when there is no unmarshalled root to put it on. Every part a
|
|
114
|
+
* `Body` is over (document, header, footer, notes, comments) has a root that binds `mc:Ignorable`.
|
|
115
|
+
*
|
|
116
|
+
* The prefix must be the facade's conventional one for its namespace (`w15`, `w16du`): an
|
|
117
|
+
* application that does not know the namespace (Word 2010 and `w15`) opens the part only when the
|
|
118
|
+
* prefix is ignorable, as Word's own files declare it (CR-002 section 40).
|
|
114
119
|
*/
|
|
115
|
-
function
|
|
120
|
+
export function declareIgnorable(part, prefix) {
|
|
116
121
|
var _a;
|
|
117
122
|
const contents = part === null || part === void 0 ? void 0 : part.contents;
|
|
118
123
|
if (!contents || typeof contents !== 'object')
|
|
119
124
|
return false;
|
|
120
125
|
const root = (contents.value && typeof contents.value === 'object' ? contents.value : contents);
|
|
121
|
-
const prefixes = ((_a = root.ignorable) !== null && _a !== void 0 ? _a : '').split(/\s+/).filter((
|
|
122
|
-
if (!prefixes.includes(
|
|
123
|
-
root.ignorable = [...prefixes,
|
|
126
|
+
const prefixes = ((_a = root.ignorable) !== null && _a !== void 0 ? _a : '').split(/\s+/).filter((p) => p.length > 0);
|
|
127
|
+
if (!prefixes.includes(prefix))
|
|
128
|
+
root.ignorable = [...prefixes, prefix].join(' ');
|
|
124
129
|
return true;
|
|
125
130
|
}
|
|
131
|
+
/** `w16du` in the part's `mc:Ignorable`, so that `w16du:dateUtc` may be written in it. */
|
|
132
|
+
function declareDateUtc(part) {
|
|
133
|
+
return declareIgnorable(part, 'w16du');
|
|
134
|
+
}
|
|
126
135
|
function element(localPart, value) {
|
|
127
136
|
return { name: { namespaceURI: W_NS, localPart }, value };
|
|
128
137
|
}
|
|
@@ -5,6 +5,7 @@ import { CustomXmlPart, type CustomXmlPartOwner } from './CustomXmlPart.mjs';
|
|
|
5
5
|
import type { CustomXmlPartLookup } from './XmlMapping.mjs';
|
|
6
6
|
import type { XPathEngine } from './xpath.mjs';
|
|
7
7
|
import { type BindingResult } from './bindings.mjs';
|
|
8
|
+
import { type XhtmlHost, type XhtmlBindingOptions } from './xhtml.mjs';
|
|
8
9
|
/**
|
|
9
10
|
* What the collection needs of its package. A structural interface, so that this module does not
|
|
10
11
|
* import `WordprocessingMLPackage` (which imports it): the package hands itself in.
|
|
@@ -18,6 +19,8 @@ export interface CustomXmlHost {
|
|
|
18
19
|
boundBodies(): Body[];
|
|
19
20
|
/** Where a new custom XML part's relationship goes: the main document part (Word drops parts related from the package alone). */
|
|
20
21
|
customXmlRelationshipSource(): Part;
|
|
22
|
+
/** The package, for the XHTML pass (list definitions, styles, relationships); absent, XHTML controls are left and noted. */
|
|
23
|
+
readonly xhtmlHost?: XhtmlHost;
|
|
21
24
|
}
|
|
22
25
|
/** A subset of Office JS `Word.CustomXmlPartCollection`. */
|
|
23
26
|
export declare class CustomXmlPartCollection implements CustomXmlPartOwner, CustomXmlPartLookup {
|
|
@@ -62,7 +65,9 @@ export declare class CustomXmlPartCollection implements CustomXmlPartOwner, Cust
|
|
|
62
65
|
* which is what Word does when it opens the document. Unmarshals the main document part (and the
|
|
63
66
|
* headers and footers it has) and parses the custom XML parts.
|
|
64
67
|
*/
|
|
65
|
-
applyBindings(
|
|
68
|
+
applyBindings(options?: {
|
|
69
|
+
html?: XhtmlBindingOptions;
|
|
70
|
+
}): Promise<BindingResult>;
|
|
66
71
|
/**
|
|
67
72
|
* docx4j `UpdateXmlFromDocumentSurface.updateCustomXmlParts`: writes what the controls now show
|
|
68
73
|
* back into the custom XML. The reverse of `applyBindings`, and what makes an edit to a bound
|
|
@@ -14,6 +14,8 @@ import { parseXml, decodeXmlText } from '../../xml/dom.mjs';
|
|
|
14
14
|
import { createDatastoreItem } from '@docx4j/generated-objects-ts/factory/org_docx4j_customXmlProperties';
|
|
15
15
|
import { CustomXmlPart } from './CustomXmlPart.mjs';
|
|
16
16
|
import { applyBindingsTo, updateFromControls } from './bindings.mjs';
|
|
17
|
+
import { applyXhtmlBindingsTo, isXhtmlBound } from './xhtml.mjs';
|
|
18
|
+
import { applyPictureHandlersTo, updateFromPictureControls } from './pictures.mjs';
|
|
17
19
|
/** A subset of Office JS `Word.CustomXmlPartCollection`. */
|
|
18
20
|
export class CustomXmlPartCollection {
|
|
19
21
|
constructor(host) {
|
|
@@ -133,9 +135,19 @@ export class CustomXmlPartCollection {
|
|
|
133
135
|
* which is what Word does when it opens the document. Unmarshals the main document part (and the
|
|
134
136
|
* headers and footers it has) and parses the custom XML parts.
|
|
135
137
|
*/
|
|
136
|
-
async applyBindings() {
|
|
138
|
+
async applyBindings(options = {}) {
|
|
139
|
+
var _a, _b;
|
|
137
140
|
await this.load();
|
|
138
|
-
|
|
141
|
+
const controls = this.controlsOf(await this.host.getBoundBodies());
|
|
142
|
+
const result = applyBindingsTo(controls);
|
|
143
|
+
// od:Handler=picture through the tag's od:xpath entry (CR-005 section 9): after the text bindings, which took the picture controls.
|
|
144
|
+
applyPictureHandlersTo(controls, this, result);
|
|
145
|
+
// Escaped XHTML through the tag's od:xpath entry (CR-005 section 8.2): after the text bindings, asynchronous.
|
|
146
|
+
if (this.host.xhtmlHost)
|
|
147
|
+
await applyXhtmlBindingsTo(controls, this, this.host.xhtmlHost, result, (_a = options.html) !== null && _a !== void 0 ? _a : {});
|
|
148
|
+
else if (controls.some(isXhtmlBound))
|
|
149
|
+
((_b = result.notes) !== null && _b !== void 0 ? _b : (result.notes = [])).push('XHTML-bound controls left: the collection has no package to make their lists, styles and links');
|
|
150
|
+
return result;
|
|
139
151
|
}
|
|
140
152
|
/**
|
|
141
153
|
* docx4j `UpdateXmlFromDocumentSurface.updateCustomXmlParts`: writes what the controls now show
|
|
@@ -144,7 +156,11 @@ export class CustomXmlPartCollection {
|
|
|
144
156
|
*/
|
|
145
157
|
async updateFromContentControls() {
|
|
146
158
|
await this.load();
|
|
147
|
-
|
|
159
|
+
const controls = this.controlsOf(await this.host.getBoundBodies());
|
|
160
|
+
const result = updateFromControls(controls);
|
|
161
|
+
// a mapped picture control's image back to its node, as Word 15 writes it (CR-005 section 9, check 38)
|
|
162
|
+
await updateFromPictureControls(controls, result);
|
|
163
|
+
return result;
|
|
148
164
|
}
|
|
149
165
|
controlsOf(bodies) {
|
|
150
166
|
return bodies.flatMap((body) => body.contentControls);
|