@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
|
@@ -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
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
|
|
2
|
+
import type * as mce from '@docx4j/generated-objects-ts/modules/org_docx4j_mce';
|
|
2
3
|
import { isElement, runItemsOf, type Element } from '@docx4j/generated-objects-ts/builders/wml';
|
|
3
4
|
export { isElement, typeNameOf, walk, find, linkParents, textOf, W_NS, type Element } from '@docx4j/generated-objects-ts/builders/wml';
|
|
4
5
|
/** The two views of a tracked document: as it will read once accepted, or as it read before the changes. */
|
|
@@ -7,6 +8,47 @@ export interface TextViewOptions {
|
|
|
7
8
|
/** 'accepted' (the default: w:ins included, w:del excluded) or 'original' (the other way). */
|
|
8
9
|
view?: TextView;
|
|
9
10
|
}
|
|
11
|
+
/** `segmentsOf`'s options: a view, and whether drawn objects get a segment of their own. */
|
|
12
|
+
export interface SegmentOptions extends TextViewOptions {
|
|
13
|
+
/**
|
|
14
|
+
* True to list each picture or other drawn object (`OBJECT_ITEMS`) as a segment of no text at its
|
|
15
|
+
* place in the text (`object` set), so that a deletion of a span can find it (CR-002 section 48).
|
|
16
|
+
*/
|
|
17
|
+
objects?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Run content that stands in the text and has no text of its own: a `w:drawing` (a picture, a chart, a
|
|
21
|
+
* text box), a VML `w:pict` and an embedded `w:object`. Office JS counts an inline picture as in a
|
|
22
|
+
* range, so a span's deletion takes the objects in it (CR-002 section 48). A kept `mc:AlternateContent`
|
|
23
|
+
* in a run is one such object too, whatever its branches hold (`ALTERNATE_CONTENT`, CR-010 phase 3).
|
|
24
|
+
*/
|
|
25
|
+
export declare const OBJECT_ITEMS: ReadonlySet<string>;
|
|
26
|
+
/** The TYPE_NAME of a kept `mc:AlternateContent` (the default load since CR-010 phase 4; under `KEPT_HOSTS` with `mcePreprocess: true`). */
|
|
27
|
+
export declare const ALTERNATE_CONTENT = "org_docx4j_mce.AlternateContent";
|
|
28
|
+
/**
|
|
29
|
+
* The branch of a kept `mc:AlternateContent` the content API reads (docx4j's `McMode.READ`, CR-010
|
|
30
|
+
* phase 3): the objects package's `mcBranchOf` over the prefixes load understands
|
|
31
|
+
* (`UNDERSTOOD_PREFIXES`), so a reader of the kept element takes the branch the preprocessor would have
|
|
32
|
+
* resolved it to. A mutator walks every branch instead (docx4j's `McMode.ALL`). Undefined for an element
|
|
33
|
+
* with no branches.
|
|
34
|
+
*/
|
|
35
|
+
export declare function selectedBranchOf(value: mce.AlternateContent | undefined): Element[] | undefined;
|
|
36
|
+
/** True for a kept `mc:AlternateContent`: its element pair, or its value. */
|
|
37
|
+
export declare function isAlternateContent(value: unknown): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Which objects at a span's edges are in the span. An object stands between two characters, so one at
|
|
40
|
+
* a span's start or end offset may be inside it or outside it, and offsets alone cannot say which. A
|
|
41
|
+
* paragraph's whole range holds the objects at both of its edges (`WHOLE`); a span of text, such as a
|
|
42
|
+
* search found, holds only the objects strictly inside it (CR-002 section 48).
|
|
43
|
+
*/
|
|
44
|
+
export interface SpanEdges {
|
|
45
|
+
start: boolean;
|
|
46
|
+
end: boolean;
|
|
47
|
+
}
|
|
48
|
+
/** Both edges: the objects at a span's start and at its end are in it. */
|
|
49
|
+
export declare const WHOLE: SpanEdges;
|
|
50
|
+
/** True when an object at offset `at` is in the span [start, end) with these edges. */
|
|
51
|
+
export declare function objectInSpan(at: number, start: number, end: number, edges?: SpanEdges): boolean;
|
|
10
52
|
/** The four run-level revision holders (CR-002 phase F); their runs live under `customXmlOrSmartTagOrSdt`. */
|
|
11
53
|
export type RevisionKind = 'ins' | 'del' | 'moveFrom' | 'moveTo';
|
|
12
54
|
/** The run-level revision a run sits in. */
|
|
@@ -37,6 +79,8 @@ export interface TextSegment {
|
|
|
37
79
|
end: number;
|
|
38
80
|
/** True for w:t: text that can be edited in place. */
|
|
39
81
|
editable: boolean;
|
|
82
|
+
/** True for a drawn object (`OBJECT_ITEMS`, or a kept `mc:AlternateContent`), whose segment has no text: listed only when asked for. */
|
|
83
|
+
object?: boolean;
|
|
40
84
|
/**
|
|
41
85
|
* The nearest enclosing w:ins / w:del / w:moveFrom / w:moveTo, when the run is in one.
|
|
42
86
|
* A deletion of this run goes inside it (`runOwner` is already its item list); an insertion
|
|
@@ -53,9 +97,15 @@ declare function itemText(item: Element): string | undefined;
|
|
|
53
97
|
/**
|
|
54
98
|
* The text segments of a paragraph (or any run holder) in order. The default view is the
|
|
55
99
|
* accepted one: the runs of a `w:ins` or `w:moveTo` count, those of a `w:del` or `w:moveFrom`
|
|
56
|
-
* do not. `{ view: 'original' }` is the other way round (CR-002 phase F).
|
|
100
|
+
* do not. `{ view: 'original' }` is the other way round (CR-002 phase F). `{ objects: true }`
|
|
101
|
+
* lists the drawn objects too, each a segment of no text where it stands (CR-002 section 48).
|
|
102
|
+
*
|
|
103
|
+
* A kept `mc:AlternateContent` in a run is one object segment, whatever its branches hold, so that
|
|
104
|
+
* removing a span removes every branch with it (CR-010 phase 3). A drawing adds no text, so the
|
|
105
|
+
* offsets are a resolved load's. One in a paragraph's content, outside a run, is not listed: Word
|
|
106
|
+
* writes none, and the corpus has none (CR-010 section 11.3).
|
|
57
107
|
*/
|
|
58
|
-
export declare function segmentsOf(container: object, options?:
|
|
108
|
+
export declare function segmentsOf(container: object, options?: SegmentOptions): TextSegment[];
|
|
59
109
|
/** The text of a paragraph or container in one of the two views (`textOf` is the accepted one). */
|
|
60
110
|
export declare function textOfView(container: object, options?: TextViewOptions): string;
|
|
61
111
|
/** The runs of a paragraph, direct and nested in hyperlinks, content controls and insertions, in order. */
|
|
@@ -1,5 +1,36 @@
|
|
|
1
|
-
import { isElement, typeNameOf, runItemsOf } from '@docx4j/generated-objects-ts/builders/wml';
|
|
1
|
+
import { isElement, typeNameOf, runItemsOf, mcBranchOf } from '@docx4j/generated-objects-ts/builders/wml';
|
|
2
|
+
import { UNDERSTOOD_PREFIXES } from '../../opc/mce/understood.mjs';
|
|
2
3
|
export { isElement, typeNameOf, walk, find, linkParents, textOf, W_NS } from '@docx4j/generated-objects-ts/builders/wml';
|
|
4
|
+
/**
|
|
5
|
+
* Run content that stands in the text and has no text of its own: a `w:drawing` (a picture, a chart, a
|
|
6
|
+
* text box), a VML `w:pict` and an embedded `w:object`. Office JS counts an inline picture as in a
|
|
7
|
+
* range, so a span's deletion takes the objects in it (CR-002 section 48). A kept `mc:AlternateContent`
|
|
8
|
+
* in a run is one such object too, whatever its branches hold (`ALTERNATE_CONTENT`, CR-010 phase 3).
|
|
9
|
+
*/
|
|
10
|
+
export const OBJECT_ITEMS = new Set(['drawing', 'pict', 'object']);
|
|
11
|
+
/** The TYPE_NAME of a kept `mc:AlternateContent` (the default load since CR-010 phase 4; under `KEPT_HOSTS` with `mcePreprocess: true`). */
|
|
12
|
+
export const ALTERNATE_CONTENT = 'org_docx4j_mce.AlternateContent';
|
|
13
|
+
/**
|
|
14
|
+
* The branch of a kept `mc:AlternateContent` the content API reads (docx4j's `McMode.READ`, CR-010
|
|
15
|
+
* phase 3): the objects package's `mcBranchOf` over the prefixes load understands
|
|
16
|
+
* (`UNDERSTOOD_PREFIXES`), so a reader of the kept element takes the branch the preprocessor would have
|
|
17
|
+
* resolved it to. A mutator walks every branch instead (docx4j's `McMode.ALL`). Undefined for an element
|
|
18
|
+
* with no branches.
|
|
19
|
+
*/
|
|
20
|
+
export function selectedBranchOf(value) {
|
|
21
|
+
return mcBranchOf(value, { understood: UNDERSTOOD_PREFIXES });
|
|
22
|
+
}
|
|
23
|
+
/** True for a kept `mc:AlternateContent`: its element pair, or its value. */
|
|
24
|
+
export function isAlternateContent(value) {
|
|
25
|
+
const v = isElement(value) ? value.value : value;
|
|
26
|
+
return typeof v === 'object' && v !== null && v.TYPE_NAME === ALTERNATE_CONTENT;
|
|
27
|
+
}
|
|
28
|
+
/** Both edges: the objects at a span's start and at its end are in it. */
|
|
29
|
+
export const WHOLE = Object.freeze({ start: true, end: true });
|
|
30
|
+
/** True when an object at offset `at` is in the span [start, end) with these edges. */
|
|
31
|
+
export function objectInSpan(at, start, end, edges) {
|
|
32
|
+
return (start < at && at < end) || (at === start && (edges === null || edges === void 0 ? void 0 : edges.start) === true) || (at === end && (edges === null || edges === void 0 ? void 0 : edges.end) === true);
|
|
33
|
+
}
|
|
3
34
|
/** Types whose value holds runs (directly or through sdtContent); the revision holders are separate. */
|
|
4
35
|
const RUN_HOLDERS = new Set([
|
|
5
36
|
'org_docx4j_wml.P.Hyperlink', 'org_docx4j_wml.SdtRun', 'org_docx4j_wml.CTSmartTagRun',
|
|
@@ -43,11 +74,18 @@ function itemText(item) {
|
|
|
43
74
|
/**
|
|
44
75
|
* The text segments of a paragraph (or any run holder) in order. The default view is the
|
|
45
76
|
* accepted one: the runs of a `w:ins` or `w:moveTo` count, those of a `w:del` or `w:moveFrom`
|
|
46
|
-
* do not. `{ view: 'original' }` is the other way round (CR-002 phase F).
|
|
77
|
+
* do not. `{ view: 'original' }` is the other way round (CR-002 phase F). `{ objects: true }`
|
|
78
|
+
* lists the drawn objects too, each a segment of no text where it stands (CR-002 section 48).
|
|
79
|
+
*
|
|
80
|
+
* A kept `mc:AlternateContent` in a run is one object segment, whatever its branches hold, so that
|
|
81
|
+
* removing a span removes every branch with it (CR-010 phase 3). A drawing adds no text, so the
|
|
82
|
+
* offsets are a resolved load's. One in a paragraph's content, outside a run, is not listed: Word
|
|
83
|
+
* writes none, and the corpus has none (CR-010 section 11.3).
|
|
47
84
|
*/
|
|
48
85
|
export function segmentsOf(container, options) {
|
|
49
86
|
var _a;
|
|
50
87
|
const view = (_a = options === null || options === void 0 ? void 0 : options.view) !== null && _a !== void 0 ? _a : 'accepted';
|
|
88
|
+
const objects = (options === null || options === void 0 ? void 0 : options.objects) === true;
|
|
51
89
|
const out = [];
|
|
52
90
|
let pos = 0;
|
|
53
91
|
const visitRuns = (items, revision) => {
|
|
@@ -63,8 +101,15 @@ export function segmentsOf(container, options) {
|
|
|
63
101
|
for (let j = 0; j < content.length; j++) {
|
|
64
102
|
const item = content[j];
|
|
65
103
|
const text = itemText(item);
|
|
66
|
-
if (text === undefined)
|
|
104
|
+
if (text === undefined) {
|
|
105
|
+
if (objects && (OBJECT_ITEMS.has(item.name.localPart) || isAlternateContent(item))) {
|
|
106
|
+
const seg = { item, owner: content, index: j, run, runOwner: items, runIndex: i, text: '', start: pos, end: pos, editable: false, object: true };
|
|
107
|
+
if (revision)
|
|
108
|
+
seg.revision = revision;
|
|
109
|
+
out.push(seg);
|
|
110
|
+
}
|
|
67
111
|
continue;
|
|
112
|
+
}
|
|
68
113
|
const seg = {
|
|
69
114
|
item, owner: content, index: j, run, runOwner: items, runIndex: i, text,
|
|
70
115
|
start: pos, end: pos + text.length, editable: item.name.localPart === 't',
|