@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,182 @@
|
|
|
1
|
+
import { highlightNameOf } from './convert.mjs';
|
|
2
|
+
/** Word's character style for a link's text, as `Range.hyperlink` gives it. */
|
|
3
|
+
export const HYPERLINK_STYLE = 'Hyperlink';
|
|
4
|
+
function walkBlocks(blocks, onParagraph) {
|
|
5
|
+
for (const b of blocks) {
|
|
6
|
+
if (b.kind === 'paragraph')
|
|
7
|
+
onParagraph(b);
|
|
8
|
+
else
|
|
9
|
+
for (const row of b.rows)
|
|
10
|
+
for (const c of row)
|
|
11
|
+
walkBlocks(c.blocks, onParagraph);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function needsOf(blocks) {
|
|
15
|
+
const lists = new Map();
|
|
16
|
+
const links = new Set();
|
|
17
|
+
walkBlocks(blocks, (p) => {
|
|
18
|
+
if (p.list && !lists.has(p.list.key))
|
|
19
|
+
lists.set(p.list.key, p.list.bullet);
|
|
20
|
+
for (const i of p.inlines)
|
|
21
|
+
if ('text' in i && i.link !== undefined)
|
|
22
|
+
links.add(i.link);
|
|
23
|
+
});
|
|
24
|
+
return { lists: [...lists].map(([key, bullet]) => ({ key, bullet })), links: [...links] };
|
|
25
|
+
}
|
|
26
|
+
const esc = (text) => text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
27
|
+
const attr = (value) => String(value).replace(/&/g, '&').replace(/</g, '<').replace(/"/g, '"');
|
|
28
|
+
function rPrXml(run, linked) {
|
|
29
|
+
const out = [];
|
|
30
|
+
// In the schema's order: rStyle, rFonts, b, i, strike, color, sz, szCs, highlight, u, vertAlign.
|
|
31
|
+
if (linked)
|
|
32
|
+
out.push(`<w:rStyle w:val="${HYPERLINK_STYLE}"/>`);
|
|
33
|
+
else if (run.style)
|
|
34
|
+
out.push(`<w:rStyle w:val="${attr(run.style)}"/>`);
|
|
35
|
+
if (run.fontName)
|
|
36
|
+
out.push(`<w:rFonts w:ascii="${attr(run.fontName)}" w:hAnsi="${attr(run.fontName)}" w:cs="${attr(run.fontName)}"/>`);
|
|
37
|
+
if (run.bold)
|
|
38
|
+
out.push('<w:b/>');
|
|
39
|
+
if (run.italic)
|
|
40
|
+
out.push('<w:i/>');
|
|
41
|
+
if (run.strike)
|
|
42
|
+
out.push('<w:strike/>');
|
|
43
|
+
// A link's colour is its style's, as `Range.hyperlink` leaves no direct colour on a link.
|
|
44
|
+
if (run.color && !linked)
|
|
45
|
+
out.push(`<w:color w:val="${attr(run.color === 'auto' ? 'auto' : run.color.replace(/^#/, ''))}"/>`);
|
|
46
|
+
if (run.fontSize !== undefined) {
|
|
47
|
+
const half = Math.round(run.fontSize * 2);
|
|
48
|
+
out.push(`<w:sz w:val="${half}"/><w:szCs w:val="${half}"/>`);
|
|
49
|
+
}
|
|
50
|
+
if (run.highlight) {
|
|
51
|
+
const name = highlightNameOf(run.highlight);
|
|
52
|
+
if (name)
|
|
53
|
+
out.push(`<w:highlight w:val="${name}"/>`);
|
|
54
|
+
}
|
|
55
|
+
if (run.underline)
|
|
56
|
+
out.push('<w:u w:val="single"/>');
|
|
57
|
+
if (run.vertAlign)
|
|
58
|
+
out.push(`<w:vertAlign w:val="${run.vertAlign}"/>`);
|
|
59
|
+
return out.length > 0 ? `<w:rPr>${out.join('')}</w:rPr>` : '';
|
|
60
|
+
}
|
|
61
|
+
function runXml(run, linked) {
|
|
62
|
+
if (run.text === '')
|
|
63
|
+
return '';
|
|
64
|
+
const space = /^\s|\s$|\s\s/.test(run.text) ? ' xml:space="preserve"' : '';
|
|
65
|
+
return `<w:r>${rPrXml(run, linked)}<w:t${space}>${esc(run.text)}</w:t></w:r>`;
|
|
66
|
+
}
|
|
67
|
+
function inlinesXml(inlines, resolve) {
|
|
68
|
+
var _a;
|
|
69
|
+
const out = [];
|
|
70
|
+
let i = 0;
|
|
71
|
+
while (i < inlines.length) {
|
|
72
|
+
const inline = inlines[i];
|
|
73
|
+
if ('br' in inline) {
|
|
74
|
+
out.push('<w:r><w:br/></w:r>');
|
|
75
|
+
i++;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if ('tab' in inline) {
|
|
79
|
+
out.push('<w:r><w:tab/></w:r>');
|
|
80
|
+
i++;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const rId = inline.link !== undefined ? (_a = resolve.relId) === null || _a === void 0 ? void 0 : _a.call(resolve, inline.link) : undefined;
|
|
84
|
+
if (rId === undefined) {
|
|
85
|
+
out.push(runXml(inline, false));
|
|
86
|
+
i++;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
// The runs of one address under one w:hyperlink, in Word's look.
|
|
90
|
+
const runs = [];
|
|
91
|
+
while (i < inlines.length) {
|
|
92
|
+
const next = inlines[i];
|
|
93
|
+
if (!('text' in next) || next.link !== inline.link)
|
|
94
|
+
break;
|
|
95
|
+
runs.push(runXml(next, true));
|
|
96
|
+
i++;
|
|
97
|
+
}
|
|
98
|
+
out.push(`<w:hyperlink r:id="${attr(rId)}">${runs.join('')}</w:hyperlink>`);
|
|
99
|
+
}
|
|
100
|
+
return out.join('');
|
|
101
|
+
}
|
|
102
|
+
function pPrXml(p, resolve) {
|
|
103
|
+
var _a;
|
|
104
|
+
const out = [];
|
|
105
|
+
// In the schema's order: pStyle, numPr, spacing, ind, jc.
|
|
106
|
+
if (p.style)
|
|
107
|
+
out.push(`<w:pStyle w:val="${attr(p.style)}"/>`);
|
|
108
|
+
const numId = p.list ? (_a = resolve.numId) === null || _a === void 0 ? void 0 : _a.call(resolve, p.list.key) : undefined;
|
|
109
|
+
if (p.list && numId !== undefined)
|
|
110
|
+
out.push(`<w:numPr><w:ilvl w:val="${p.list.level}"/><w:numId w:val="${attr(numId)}"/></w:numPr>`);
|
|
111
|
+
if (p.spacing)
|
|
112
|
+
out.push(`<w:spacing w:before="${p.spacing.before}"/>`);
|
|
113
|
+
if (p.ind) {
|
|
114
|
+
const a = [];
|
|
115
|
+
if (p.ind.start !== undefined)
|
|
116
|
+
a.push(`w:left="${p.ind.start}"`);
|
|
117
|
+
if (p.ind.end !== undefined)
|
|
118
|
+
a.push(`w:right="${p.ind.end}"`);
|
|
119
|
+
if (p.ind.hanging !== undefined)
|
|
120
|
+
a.push(`w:hanging="${p.ind.hanging}"`);
|
|
121
|
+
else if (p.ind.firstLine !== undefined)
|
|
122
|
+
a.push(`w:firstLine="${p.ind.firstLine}"`);
|
|
123
|
+
if (a.length > 0)
|
|
124
|
+
out.push(`<w:ind ${a.join(' ')}/>`);
|
|
125
|
+
}
|
|
126
|
+
if (p.jc)
|
|
127
|
+
out.push(`<w:jc w:val="${attr(p.jc)}"/>`);
|
|
128
|
+
return out.length > 0 ? `<w:pPr>${out.join('')}</w:pPr>` : '';
|
|
129
|
+
}
|
|
130
|
+
function paragraphXml(p, resolve) {
|
|
131
|
+
return `<w:p>${pPrXml(p, resolve)}${inlinesXml(p.inlines, resolve)}</w:p>`;
|
|
132
|
+
}
|
|
133
|
+
/** Word's default text width shared equally where the table does not say (A4 or Letter with Word's margins: 9,026 twips). */
|
|
134
|
+
const TEXT_WIDTH = 9026;
|
|
135
|
+
function tableXml(t, resolve) {
|
|
136
|
+
const columns = Math.max(1, ...t.rows.map((r) => r.reduce((n, c) => n + c.colspan, 0)));
|
|
137
|
+
const widths = Array.from({ length: columns }, () => Math.floor(TEXT_WIDTH / columns));
|
|
138
|
+
const first = t.rows[0];
|
|
139
|
+
if (first && first.every((c) => c.width !== undefined && c.colspan === 1) && first.length === columns)
|
|
140
|
+
first.forEach((c, i) => { widths[i] = c.width; });
|
|
141
|
+
const widthOf = (col, span) => { var _a; let w = 0; for (let k = 0; k < span; k++)
|
|
142
|
+
w += (_a = widths[col + k]) !== null && _a !== void 0 ? _a : widths[0]; return w; };
|
|
143
|
+
const tcPr = (col, span, merge) => `<w:tcPr><w:tcW w:w="${widthOf(col, span)}" w:type="dxa"/>${span > 1 ? `<w:gridSpan w:val="${span}"/>` : ''}${merge === 'restart' ? '<w:vMerge w:val="restart"/>' : merge === 'continue' ? '<w:vMerge/>' : ''}</w:tcPr>`;
|
|
144
|
+
const cellXml = (c, col) => {
|
|
145
|
+
const blocks = c.blocks.length > 0 ? blocksToXml(c.blocks, resolve) : '<w:p/>';
|
|
146
|
+
return `<w:tc>${tcPr(col, c.colspan, c.rowspan > 1 ? 'restart' : undefined)}${blocks}</w:tc>`;
|
|
147
|
+
};
|
|
148
|
+
// A cell spanning rows holds its place in the rows below with a continuation cell, as WordprocessingML writes a vertical merge.
|
|
149
|
+
const pending = new Map();
|
|
150
|
+
const rows = t.rows.map((cells, r) => {
|
|
151
|
+
const out = [];
|
|
152
|
+
let col = 0;
|
|
153
|
+
const continuation = () => {
|
|
154
|
+
const p = pending.get(col);
|
|
155
|
+
if (!p)
|
|
156
|
+
return;
|
|
157
|
+
out.push(`<w:tc>${tcPr(col, p.span, 'continue')}<w:p/></w:tc>`);
|
|
158
|
+
if (--p.rows <= 0)
|
|
159
|
+
pending.delete(col);
|
|
160
|
+
col += p.span;
|
|
161
|
+
};
|
|
162
|
+
for (const c of cells) {
|
|
163
|
+
while (pending.has(col))
|
|
164
|
+
continuation();
|
|
165
|
+
out.push(cellXml(c, col));
|
|
166
|
+
if (c.rowspan > 1)
|
|
167
|
+
pending.set(col, { rows: c.rowspan - 1, span: c.colspan });
|
|
168
|
+
col += c.colspan;
|
|
169
|
+
}
|
|
170
|
+
while (col < columns && pending.has(col))
|
|
171
|
+
continuation();
|
|
172
|
+
const trPr = r === 0 && t.headerRow ? '<w:trPr><w:tblHeader/></w:trPr>' : '';
|
|
173
|
+
return `<w:tr>${trPr}${out.join('')}</w:tr>`;
|
|
174
|
+
});
|
|
175
|
+
const tblPr = `<w:tblPr>${t.style ? `<w:tblStyle w:val="${attr(t.style)}"/>` : ''}<w:tblW w:w="0" w:type="auto"/></w:tblPr>`;
|
|
176
|
+
const grid = `<w:tblGrid>${widths.map((w) => `<w:gridCol w:w="${w}"/>`).join('')}</w:tblGrid>`;
|
|
177
|
+
return `<w:tbl>${tblPr}${grid}${rows.join('')}</w:tbl>`;
|
|
178
|
+
}
|
|
179
|
+
/** The blocks as a WordprocessingML fragment: `w:p` and `w:tbl` elements, the prefixes undeclared (`insertXml` declares them). */
|
|
180
|
+
export function blocksToXml(blocks, resolve = {}) {
|
|
181
|
+
return blocks.map((b) => (b.kind === 'table' ? tableXml(b, resolve) : paragraphXml(b, resolve))).join('');
|
|
182
|
+
}
|
|
@@ -192,7 +192,8 @@ export declare class Body {
|
|
|
192
192
|
replaceText(find: string, replace: string, options?: SearchOptions): number;
|
|
193
193
|
/**
|
|
194
194
|
* Removes everything (section properties stay). While the package tracks changes, every
|
|
195
|
-
* paragraph is marked deleted and every row's `w:trPr` takes a `w:del` instead.
|
|
195
|
+
* paragraph is marked deleted and every row's `w:trPr` takes a `w:del` instead. Removed outright,
|
|
196
|
+
* the relationships the content held (its pictures' images) are settled on save (CR-002 section 47).
|
|
196
197
|
*/
|
|
197
198
|
clear(): void;
|
|
198
199
|
/**
|
|
@@ -256,3 +257,14 @@ export declare class Body {
|
|
|
256
257
|
*/
|
|
257
258
|
insertContentControl(kind?: ContentControlType): ContentControl;
|
|
258
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* A story's tracked changes resolved (`Body.acceptAll` or `rejectAll`), then those of each text box in
|
|
262
|
+
* it - a `w:txbxContent`, in a DrawingML shape or a VML one - which Word counts as a story of its own
|
|
263
|
+
* (CR-002 section 49). A text box inside a text box is reached through its own story. Returns how many
|
|
264
|
+
* changes were listed.
|
|
265
|
+
*
|
|
266
|
+
* In a kept `mc:AlternateContent` the text box of every branch is resolved, so that each says the same
|
|
267
|
+
* afterwards (docx4j's `McMode.ALL`), and the changes are counted in the selected branch alone, so a
|
|
268
|
+
* text box held in both counts once, as a resolved load's does (CR-010 phase 3).
|
|
269
|
+
*/
|
|
270
|
+
export declare function resolveStory(body: Body, what: 'accept' | 'reject'): number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Docx4JException, PropertyResolverNotCreatedException } from '../../opc/exceptions.mjs';
|
|
2
|
-
import { typeNameOf, childrenOf, linkParents, BLOCK_LEVEL_TYPES, textOf, textOfView, rowsOf, cellsOf } from './tree.mjs';
|
|
2
|
+
import { typeNameOf, childrenOf, linkParents, BLOCK_LEVEL_TYPES, textOf, textOfView, rowsOf, cellsOf, selectedBranchOf, ALTERNATE_CONTENT } from './tree.mjs';
|
|
3
3
|
import { r as textRun, br as breakItem, tbl as tableOf } from '@docx4j/generated-objects-ts/builders/wml';
|
|
4
4
|
import { runOf, paragraphOf, runsOf } from './tree.mjs';
|
|
5
5
|
import { Paragraph } from './Paragraph.mjs';
|
|
@@ -9,10 +9,11 @@ import { sdt as sdtOf, nextSdtId } from '@docx4j/generated-objects-ts/builders/w
|
|
|
9
9
|
import { controlIdScope, sdtKindFor, declareSdtExtensions } from '../customxml/insert.mjs';
|
|
10
10
|
import { InlinePicture, addImage, writableWidthEmu } from './InlinePicture.mjs';
|
|
11
11
|
import { contentOf } from './ooxml.mjs';
|
|
12
|
+
import { relationshipIdsIn, queueRelationshipRemovals } from './relationships.mjs';
|
|
12
13
|
import { expandReplacement } from './search.mjs';
|
|
13
14
|
import { commentApi } from './comments.mjs';
|
|
14
15
|
import { trackerOf, trackInsertedBlock, markHolderInserted } from './tracking.mjs';
|
|
15
|
-
import { trackedChangesOfRow, trackedChangeTokensOfParagraph, groupTouching, BREAK, tablePropertiesChangeOf, cellChangeOf, bodySectionChangeOf, resolveUnlisted, holderInsertionAt } from './TrackedChange.mjs';
|
|
16
|
+
import { trackedChangesOfRow, trackedChangeTokensOfParagraph, groupTouching, BREAK, tablePropertiesChangeOf, cellChangeOf, bodySectionChangeOf, resolveUnlisted, holderInsertionAt, resolving } from './TrackedChange.mjs';
|
|
16
17
|
import { listsOf, listLabelsOf } from './List.mjs';
|
|
17
18
|
/**
|
|
18
19
|
* A subset of Office JS `Word.Body` over any container with block-level content: the main
|
|
@@ -239,11 +240,18 @@ export class Body {
|
|
|
239
240
|
* `values` fills it row by row. The columns are equal over the section's text width.
|
|
240
241
|
*/
|
|
241
242
|
insertTable(rowCount, columnCount, location, values) {
|
|
243
|
+
var _a;
|
|
244
|
+
var _b;
|
|
242
245
|
if (rowCount < 1 || columnCount < 1)
|
|
243
246
|
throw new Docx4JException(`A table needs at least one row and one column; got ${rowCount} by ${columnCount}`);
|
|
244
247
|
const rows = Array.from({ length: rowCount }, (_, r) => Array.from({ length: columnCount }, (_, c) => { var _a, _b; return (_b = (_a = values === null || values === void 0 ? void 0 : values[r]) === null || _a === void 0 ? void 0 : _a[c]) !== null && _b !== void 0 ? _b : ''; }));
|
|
245
248
|
const width = writableWidthTwips(this.container);
|
|
246
249
|
const element = tableOf(rows, width ? { width } : {});
|
|
250
|
+
// Word's own default (Insert Table, fixed column width Auto; the editor's check 42, Word 365): an
|
|
251
|
+
// automatic table width, no w:tblLayout, the cells in twips sharing the text width - the grid and
|
|
252
|
+
// cells the builder wrote, the table's width made automatic (CR-002 section 42.5 item 7).
|
|
253
|
+
const tblPr = ((_a = (_b = element.value).tblPr) !== null && _a !== void 0 ? _a : (_b.tblPr = {}));
|
|
254
|
+
tblPr.tblW = { w: 0, type: 'auto' };
|
|
247
255
|
this.insertElement(element, location);
|
|
248
256
|
return new Table(element, this.content, this);
|
|
249
257
|
}
|
|
@@ -257,8 +265,11 @@ export class Body {
|
|
|
257
265
|
throw new Docx4JException('This body has no part, so an image cannot be related to it');
|
|
258
266
|
const { run, drawing } = addImage(this.part, base64, this.container, writableWidthEmu(this.container), options);
|
|
259
267
|
const paragraph = this.insertParagraph('', location);
|
|
260
|
-
paragraph
|
|
261
|
-
|
|
268
|
+
// under tracking the picture is an insertion, as its paragraph's mark is (insertBreak's form)
|
|
269
|
+
const tracker = paragraph.changeTracker;
|
|
270
|
+
const item = tracker ? tracker.ins([run]) : run;
|
|
271
|
+
paragraph.p.content.push(item);
|
|
272
|
+
linkParents(item, paragraph.p);
|
|
262
273
|
return new InlinePicture(drawing, run, paragraph);
|
|
263
274
|
}
|
|
264
275
|
insertBreak(type, location) {
|
|
@@ -363,11 +374,16 @@ export class Body {
|
|
|
363
374
|
}
|
|
364
375
|
/**
|
|
365
376
|
* Removes everything (section properties stay). While the package tracks changes, every
|
|
366
|
-
* paragraph is marked deleted and every row's `w:trPr` takes a `w:del` instead.
|
|
377
|
+
* paragraph is marked deleted and every row's `w:trPr` takes a `w:del` instead. Removed outright,
|
|
378
|
+
* the relationships the content held (its pictures' images) are settled on save (CR-002 section 47).
|
|
367
379
|
*/
|
|
368
380
|
clear() {
|
|
381
|
+
var _a;
|
|
369
382
|
const tracker = this.changeTracker;
|
|
370
383
|
if (!tracker) {
|
|
384
|
+
const ids = new Set();
|
|
385
|
+
relationshipIdsIn(this.content, ids);
|
|
386
|
+
queueRelationshipRemovals((_a = this.package_) === null || _a === void 0 ? void 0 : _a.pendingRelationshipRemovals, this.part, ids);
|
|
371
387
|
this.content.length = 0;
|
|
372
388
|
return;
|
|
373
389
|
}
|
|
@@ -420,14 +436,14 @@ export class Body {
|
|
|
420
436
|
// belongs to that change, which accepts or rejects it along with the row.
|
|
421
437
|
const tokens = [];
|
|
422
438
|
for (const cell of cellsOf(row.element.value)) {
|
|
423
|
-
const cellChange = cellChangeOf(cell.element, cell.container, row.element.value);
|
|
439
|
+
const cellChange = cellChangeOf(cell.element, cell.container, row.element.value, this);
|
|
424
440
|
if (cellChange)
|
|
425
441
|
tokens.push(cellChange, BREAK);
|
|
426
442
|
visit((_b = childrenOf(cell.element.value)) !== null && _b !== void 0 ? _b : [], tokens);
|
|
427
443
|
tokens.push(BREAK);
|
|
428
444
|
}
|
|
429
445
|
const inner = groupTouching(tokens);
|
|
430
|
-
const rowChanges = trackedChangesOfRow(row.element, row.container, inner, { element: el, container: items });
|
|
446
|
+
const rowChanges = trackedChangesOfRow(row.element, row.container, inner, { element: el, container: items }, this);
|
|
431
447
|
if (rowChanges.length > 0)
|
|
432
448
|
into.push(...rowChanges);
|
|
433
449
|
else {
|
|
@@ -474,18 +490,22 @@ export class Body {
|
|
|
474
490
|
* pass that resolves nothing ends it.
|
|
475
491
|
*/
|
|
476
492
|
resolveAll(what) {
|
|
477
|
-
let
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
493
|
+
let count = 0;
|
|
494
|
+
// one resolution for the lot, so the bookmarks are read before the first and after the last
|
|
495
|
+
resolving(this.package_, this, () => {
|
|
496
|
+
let changes = this.getTrackedChanges();
|
|
497
|
+
count = changes.length;
|
|
498
|
+
while (changes.length > 0) {
|
|
499
|
+
for (let i = changes.length - 1; i >= 0; i--)
|
|
500
|
+
changes[i][what]();
|
|
501
|
+
const left = this.getTrackedChanges();
|
|
502
|
+
if (left.length >= changes.length)
|
|
503
|
+
break;
|
|
504
|
+
changes = left;
|
|
505
|
+
}
|
|
506
|
+
// and what is never listed: a cell merge, a numbering change, records that change nothing
|
|
507
|
+
resolveUnlisted(this.content, what);
|
|
508
|
+
});
|
|
489
509
|
return count;
|
|
490
510
|
}
|
|
491
511
|
/** Every `w:tr` in this body's tables, in document order (rows of nested tables included). */
|
|
@@ -770,3 +790,61 @@ function describe(el) {
|
|
|
770
790
|
const tn = typeNameOf(el);
|
|
771
791
|
return `${(_b = (_a = el.name) === null || _a === void 0 ? void 0 : _a.localPart) !== null && _b !== void 0 ? _b : '?'} (${tn !== null && tn !== void 0 ? tn : 'no TYPE_NAME'})`;
|
|
772
792
|
}
|
|
793
|
+
/**
|
|
794
|
+
* A story's tracked changes resolved (`Body.acceptAll` or `rejectAll`), then those of each text box in
|
|
795
|
+
* it - a `w:txbxContent`, in a DrawingML shape or a VML one - which Word counts as a story of its own
|
|
796
|
+
* (CR-002 section 49). A text box inside a text box is reached through its own story. Returns how many
|
|
797
|
+
* changes were listed.
|
|
798
|
+
*
|
|
799
|
+
* In a kept `mc:AlternateContent` the text box of every branch is resolved, so that each says the same
|
|
800
|
+
* afterwards (docx4j's `McMode.ALL`), and the changes are counted in the selected branch alone, so a
|
|
801
|
+
* text box held in both counts once, as a resolved load's does (CR-010 phase 3).
|
|
802
|
+
*/
|
|
803
|
+
export function resolveStory(body, what) {
|
|
804
|
+
let count = what === 'accept' ? body.acceptAll() : body.rejectAll();
|
|
805
|
+
for (const { box, selected } of textBoxesIn(body.container)) {
|
|
806
|
+
const resolved = resolveStory(body.sub(box), what);
|
|
807
|
+
if (selected)
|
|
808
|
+
count += resolved;
|
|
809
|
+
}
|
|
810
|
+
return count;
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* The `w:txbxContent` of every text box in a story's content, not descending into one found, each with
|
|
814
|
+
* whether it is in a branch a reader takes: false inside a branch of a kept `mc:AlternateContent` other
|
|
815
|
+
* than its selected one (`selectedBranchOf`).
|
|
816
|
+
*/
|
|
817
|
+
function textBoxesIn(container) {
|
|
818
|
+
const out = [];
|
|
819
|
+
const seen = new Set();
|
|
820
|
+
const visit = (o, selected) => {
|
|
821
|
+
var _a;
|
|
822
|
+
if (typeof o !== 'object' || o === null || seen.has(o))
|
|
823
|
+
return;
|
|
824
|
+
if (typeof o.nodeType === 'number')
|
|
825
|
+
return; // DOM the model does not type
|
|
826
|
+
seen.add(o);
|
|
827
|
+
if (Array.isArray(o)) {
|
|
828
|
+
for (const item of o)
|
|
829
|
+
visit(item, selected);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
const typeName = o.TYPE_NAME;
|
|
833
|
+
if (typeName === 'org_docx4j_wml.CTTxbxContent') {
|
|
834
|
+
out.push({ box: o, selected });
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
if (typeName === ALTERNATE_CONTENT) {
|
|
838
|
+
const ac = o;
|
|
839
|
+
const read = selectedBranchOf(ac);
|
|
840
|
+
for (const branch of [...((_a = ac.choice) !== null && _a !== void 0 ? _a : []), ...(ac.fallback ? [ac.fallback] : [])])
|
|
841
|
+
visit(branch.any, selected && branch.any === read);
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
for (const key in o)
|
|
845
|
+
if (key !== 'PARENT')
|
|
846
|
+
visit(o[key], selected);
|
|
847
|
+
};
|
|
848
|
+
visit(container.content, true);
|
|
849
|
+
return out;
|
|
850
|
+
}
|
|
@@ -9,6 +9,7 @@ import { Paragraph } from './Paragraph.mjs';
|
|
|
9
9
|
import { Range } from './Range.mjs';
|
|
10
10
|
import { runOf, paragraphOf, typeNameOf } from './tree.mjs';
|
|
11
11
|
import { revisionDateOf, wordCalendarOf, copyRPr } from './tracking.mjs';
|
|
12
|
+
import { relationshipIdsIn, queueRelationshipRemovals } from './relationships.mjs';
|
|
12
13
|
import { COMMENT_REFERENCE_STYLE, COMMENT_TEXT_STYLE, commentExOf, commentIdsOf, commentPartsAccess, commentParaId, initialsOf, isDone, markersOf, markersOfParagraph, nextCommentId, paraIdTaken, randomHexId, removeMarkers, setCommentApi, } from './comments.mjs';
|
|
13
14
|
/**
|
|
14
15
|
* A subset of Office JS `Word.Comment` over a `w:comment` and the markers that anchor it. A view:
|
|
@@ -191,6 +192,12 @@ export class Comment {
|
|
|
191
192
|
if (i >= 0)
|
|
192
193
|
comments.splice(i, 1);
|
|
193
194
|
}
|
|
195
|
+
// what the comment's own content related to - a picture's image - goes on save when nothing else
|
|
196
|
+
// uses it, as for a picture deleted outright (CR-002 section 50; docx4j's Removals)
|
|
197
|
+
const released = new Set();
|
|
198
|
+
relationshipIdsIn(this.element.content, released);
|
|
199
|
+
const pkg = this.body.package_;
|
|
200
|
+
queueRelationshipRemovals(pkg === null || pkg === void 0 ? void 0 : pkg.pendingRelationshipRemovals, this.parts.commentsPart, released);
|
|
194
201
|
const paraId = this.paraId;
|
|
195
202
|
if (paraId === undefined)
|
|
196
203
|
return;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
|
|
2
|
+
import type * as dml from '@docx4j/generated-objects-ts/modules/org_docx4j_dml';
|
|
2
3
|
import type * as wp from '@docx4j/generated-objects-ts/modules/org_docx4j_dml_wordprocessingDrawing';
|
|
3
4
|
import { IMAGE_CONTENT_TYPES_BY_EXTENSION } from '../../opc/ContentTypes.mjs';
|
|
4
5
|
import { ImagePart } from '../../parts/BinaryPart.mjs';
|
|
@@ -22,7 +23,9 @@ export interface ImageInfo {
|
|
|
22
23
|
* The size and kind of an image, from its header: PNG (IHDR and pHYs), JPEG (SOFn and the JFIF
|
|
23
24
|
* APP0 densities), GIF (the logical screen descriptor) and BMP (the DIB header, with its pixels
|
|
24
25
|
* per metre). docx4j reads these through XML Graphics Commons' ImageInfo; this is the same
|
|
25
|
-
* information, and the same 96 dpi default when a file declares none.
|
|
26
|
+
* information, and the same 96 dpi default when a file declares none. An SVG document is told by
|
|
27
|
+
* its root element and has no pixel size (both 0): it can be the part behind an existing picture
|
|
28
|
+
* (CR-005 section 9, a binding that keeps the drawing) but not be placed as a new one.
|
|
26
29
|
*/
|
|
27
30
|
export declare function imageInfoOf(bytes: Uint8Array): ImageInfo;
|
|
28
31
|
/** The natural size of an image in EMU: pixels over the resolution it declares. */
|
|
@@ -49,17 +52,56 @@ export interface NewPicture {
|
|
|
49
52
|
imagePart: ImagePart;
|
|
50
53
|
relId: string;
|
|
51
54
|
}
|
|
55
|
+
/** A new image part, its relationship from the part it was added to, and what its header says. */
|
|
56
|
+
export interface NewImagePart {
|
|
57
|
+
imagePart: ImagePart;
|
|
58
|
+
relId: string;
|
|
59
|
+
info: ImageInfo;
|
|
60
|
+
}
|
|
52
61
|
/**
|
|
53
62
|
* Adds the image as a part of `source` (the main document part, or the header or footer the body
|
|
54
|
-
* belongs to) with a relationship, and
|
|
55
|
-
* `
|
|
63
|
+
* belongs to) with a relationship, and nothing else: docx4j `BinaryPartAbstractImage.createImagePart`
|
|
64
|
+
* (`BindingTraverserXSLT.createImagePartReturnRelId`, where a picture binding replaces an `r:embed`
|
|
65
|
+
* and keeps the drawing, CR-005 section 9). Throws, and adds nothing, when the bytes are not an
|
|
66
|
+
* image this package reads.
|
|
67
|
+
*/
|
|
68
|
+
export declare function addImagePart(source: Part, base64: string): NewImagePart;
|
|
69
|
+
/**
|
|
70
|
+
* Adds the image as a part of `source` with a relationship (`addImagePart`), and builds the run
|
|
71
|
+
* that shows it. docx4j: `BinaryPartAbstractImage.createImagePart` then `createImageInline`.
|
|
56
72
|
*/
|
|
57
73
|
export declare function addImage(source: Part, base64: string, scope: object, maxCx: number | undefined, options?: InlinePictureOptions): NewPicture;
|
|
58
74
|
/** The writable width of a section in EMU (page width less the margins), for scaling. */
|
|
59
75
|
export declare function writableWidthEmu(container: object): number | undefined;
|
|
76
|
+
/** The `asvg:svgBlip` in a blip's `a:extLst`, if any: an SVG picture's SVG (CR-002 section 52). */
|
|
77
|
+
export declare function svgBlipOf(blip: dml.CTBlip | undefined): {
|
|
78
|
+
embed?: string;
|
|
79
|
+
} | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Points a blip at a new image the way Word's own picture binding does (test/README.md checks 46 and
|
|
82
|
+
* 47; CR-002 section 52). The blip's effects and other extensions stay.
|
|
83
|
+
* - A raster image goes in `r:embed`, and an `asvg:svgBlip` is taken away. Left in place, Word would go
|
|
84
|
+
* on drawing its old SVG, and would replace the new image with a PNG of that SVG when it saves.
|
|
85
|
+
* - An SVG goes under the `asvg:svgBlip`, and `r:embed` is taken away. Word puts a PNG of the SVG it
|
|
86
|
+
* makes itself in `r:embed`, which this package cannot make, and Word 365 draws the SVG without one.
|
|
87
|
+
* `r:link` is left as it was.
|
|
88
|
+
*/
|
|
89
|
+
export declare function setBlipImage(blip: dml.CTBlip, relId: string, svg: boolean): void;
|
|
90
|
+
/**
|
|
91
|
+
* The inline pictures a run's item holds: the item itself when it is a `w:drawing` whose `wp:inline`
|
|
92
|
+
* holds a `pic:pic`, and those in a kept `mc:AlternateContent`'s selected branch (`selectedBranchOf`,
|
|
93
|
+
* CR-010 phase 3). A drawing of anything else, such as a chart, a text box, a group or SmartArt, is no
|
|
94
|
+
* inline picture: Office JS lists none for an inline text box (CR-010 section 12.6, test/README.md
|
|
95
|
+
* check 42).
|
|
96
|
+
*/
|
|
97
|
+
export declare function picturesIn(item: Element): Element<wml.Drawing>[];
|
|
60
98
|
/**
|
|
61
|
-
* A subset of Office JS `Word.InlinePicture` over a `w:drawing` holding a `wp:inline
|
|
62
|
-
* the drawing, the run it is in and the paragraph. Sizes are points, as Office JS reports them.
|
|
99
|
+
* A subset of Office JS `Word.InlinePicture` over a `w:drawing` holding a `wp:inline` with a `pic:pic`.
|
|
100
|
+
* A view: the drawing, the run it is in and the paragraph. Sizes are points, as Office JS reports them.
|
|
101
|
+
*
|
|
102
|
+
* A picture in a kept `mc:AlternateContent`'s selected branch has the element as its `holder`. Reads and
|
|
103
|
+
* the setters are the selected branch's drawing; `delete` removes the element whole, every branch with
|
|
104
|
+
* it (CR-010 phase 3).
|
|
63
105
|
*/
|
|
64
106
|
export declare class InlinePicture {
|
|
65
107
|
/** The `w:drawing` element pair. */
|
|
@@ -67,11 +109,15 @@ export declare class InlinePicture {
|
|
|
67
109
|
/** The run holding the drawing, and the array holding the run. */
|
|
68
110
|
readonly run: Element<wml.R>;
|
|
69
111
|
readonly paragraph: Paragraph;
|
|
112
|
+
/** The run's item the drawing stands in, where that is not the drawing: a kept `mc:AlternateContent`. */
|
|
113
|
+
readonly holder?: Element | undefined;
|
|
70
114
|
constructor(
|
|
71
115
|
/** The `w:drawing` element pair. */
|
|
72
116
|
element: Element<wml.Drawing>,
|
|
73
117
|
/** The run holding the drawing, and the array holding the run. */
|
|
74
|
-
run: Element<wml.R>, paragraph: Paragraph
|
|
118
|
+
run: Element<wml.R>, paragraph: Paragraph,
|
|
119
|
+
/** The run's item the drawing stands in, where that is not the drawing: a kept `mc:AlternateContent`. */
|
|
120
|
+
holder?: Element | undefined);
|
|
75
121
|
/** The wp:inline; a floating picture (wp:anchor) is not an InlinePicture, and throws. */
|
|
76
122
|
get inline(): wp.Inline;
|
|
77
123
|
/** Width in points, as Office JS. */
|
|
@@ -85,18 +131,51 @@ export declare class InlinePicture {
|
|
|
85
131
|
/** wp:docPr/@title (typed since objects 0.1.3; before that the attribute was not marshalled, CR-002 section 8). */
|
|
86
132
|
get altTextTitle(): string;
|
|
87
133
|
set altTextTitle(text: string);
|
|
88
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* The kind of image, from the content type of the part `getBase64` reads: `Svg` for an SVG picture
|
|
136
|
+
* (`svgImagePart`), else the `a:blip`'s part's. Office JS gives `null` for an SVG picture, though its
|
|
137
|
+
* typings declare the property non-null and `Word.ImageFormat` has `Svg` (test/README.md check 45);
|
|
138
|
+
* this package gives `Svg`, a departure (CR-002 section 52).
|
|
139
|
+
*/
|
|
89
140
|
get imageFormat(): ImageFormat;
|
|
90
141
|
/** The relationship id of the embedded image (r:embed), if any (extension). */
|
|
91
142
|
get relId(): string | undefined;
|
|
92
|
-
/**
|
|
143
|
+
/**
|
|
144
|
+
* The relationship id of the SVG the picture embeds beside its image, if any (extension): the
|
|
145
|
+
* `r:embed` of an `asvg:svgBlip` in the `a:blip`'s `a:extLst`, as Office 2016 and later write an SVG
|
|
146
|
+
* picture, with the `a:blip`'s own image a PNG of it for readers that don't understand SVG.
|
|
147
|
+
*/
|
|
148
|
+
get svgRelId(): string | undefined;
|
|
149
|
+
/**
|
|
150
|
+
* The image's bytes as base64 (Office JS getBase64ImageSrc). For an SVG picture, the SVG
|
|
151
|
+
* (`svgImagePart`), as Office JS gives it (test/README.md check 43); else the `a:blip`'s image.
|
|
152
|
+
*/
|
|
93
153
|
getBase64(): Promise<string>;
|
|
94
154
|
/** Office JS's name for `getBase64()`. */
|
|
95
155
|
getBase64ImageSrc(): Promise<string>;
|
|
96
|
-
/** The image part this picture embeds (extension). */
|
|
156
|
+
/** The image part this picture embeds: the `a:blip`'s, which is an SVG picture's PNG (extension). */
|
|
97
157
|
imagePart(): ImagePart | undefined;
|
|
98
|
-
/**
|
|
158
|
+
/** The SVG part an SVG picture embeds beside its image (`svgRelId`), if any (extension). */
|
|
159
|
+
svgImagePart(): ImagePart | undefined;
|
|
160
|
+
private partFor;
|
|
161
|
+
/**
|
|
162
|
+
* Removes the picture; the run goes too when the picture was all it held. A picture in a kept
|
|
163
|
+
* `mc:AlternateContent` takes the element with it, every branch (`holder`).
|
|
164
|
+
*
|
|
165
|
+
* While the package tracks changes it is marked deleted instead: its run goes into a `w:del`, and
|
|
166
|
+
* when the run held more than the picture, the picture is first given a run of its own with the
|
|
167
|
+
* same properties, so the deletion holds it alone. Accepting the deletion then takes it, and its
|
|
168
|
+
* image part with it (CR-002 section 46). A picture its author inserted under tracking is taken
|
|
169
|
+
* back, as `Paragraph.delete` takes back its own text, and one inside a deletion is refused.
|
|
170
|
+
*
|
|
171
|
+
* A picture removed outright (tracking off, or taken back) queues its image relationship on the
|
|
172
|
+
* package. On save the relationship goes if nothing then references it, and the image part with it
|
|
173
|
+
* when nothing else targets it. Waiting for the save lets a picture cut and pasted back keep its
|
|
174
|
+
* image.
|
|
175
|
+
*/
|
|
99
176
|
delete(): void;
|
|
177
|
+
/** The picture's run in a `w:del`, the picture split into a run of its own when the run held more. */
|
|
178
|
+
private markDeleted;
|
|
100
179
|
private pic;
|
|
101
180
|
private resize;
|
|
102
181
|
}
|