@docx4j/core-ts 0.3.2 → 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 +7 -0
- package/dist/model/content/InlinePicture.d.mts +18 -3
- package/dist/model/content/InlinePicture.mjs +44 -7
- 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 +20 -6
- package/dist/model/content/tableColumns.d.mts +48 -0
- package/dist/model/content/tableColumns.mjs +484 -0
- 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 +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/packages/WordprocessingMLPackage.mjs +1 -0
- package/dist/parts/wml/knownStyles.d.mts +1 -0
- package/dist/parts/wml/knownStyles.mjs +2233 -0
- package/package.json +6 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PasteBlock } from './convert.mjs';
|
|
2
|
+
/** Word's character style for a link's text, as `Range.hyperlink` gives it. */
|
|
3
|
+
export declare const HYPERLINK_STYLE = "Hyperlink";
|
|
4
|
+
/** What the blocks need from the document before they are built: a definition per list key, a relationship per link address. */
|
|
5
|
+
export interface BuildNeeds {
|
|
6
|
+
lists: {
|
|
7
|
+
key: string;
|
|
8
|
+
bullet: boolean;
|
|
9
|
+
}[];
|
|
10
|
+
links: string[];
|
|
11
|
+
}
|
|
12
|
+
/** The document's answers: `w:numId` per list key, `r:id` per address; undefined drops the list or the link (its text kept). */
|
|
13
|
+
export interface BuildResolvers {
|
|
14
|
+
numId?: (key: string) => string | undefined;
|
|
15
|
+
relId?: (address: string) => string | undefined;
|
|
16
|
+
}
|
|
17
|
+
export declare function needsOf(blocks: readonly PasteBlock[]): BuildNeeds;
|
|
18
|
+
/** The blocks as a WordprocessingML fragment: `w:p` and `w:tbl` elements, the prefixes undeclared (`insertXml` declares them). */
|
|
19
|
+
export declare function blocksToXml(blocks: readonly PasteBlock[], resolve?: BuildResolvers): string;
|
|
@@ -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
|
+
}
|
|
@@ -239,11 +239,18 @@ export class Body {
|
|
|
239
239
|
* `values` fills it row by row. The columns are equal over the section's text width.
|
|
240
240
|
*/
|
|
241
241
|
insertTable(rowCount, columnCount, location, values) {
|
|
242
|
+
var _a;
|
|
243
|
+
var _b;
|
|
242
244
|
if (rowCount < 1 || columnCount < 1)
|
|
243
245
|
throw new Docx4JException(`A table needs at least one row and one column; got ${rowCount} by ${columnCount}`);
|
|
244
246
|
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
247
|
const width = writableWidthTwips(this.container);
|
|
246
248
|
const element = tableOf(rows, width ? { width } : {});
|
|
249
|
+
// Word's own default (Insert Table, fixed column width Auto; the editor's check 42, Word 365): an
|
|
250
|
+
// automatic table width, no w:tblLayout, the cells in twips sharing the text width - the grid and
|
|
251
|
+
// cells the builder wrote, the table's width made automatic (CR-002 section 42.5 item 7).
|
|
252
|
+
const tblPr = ((_a = (_b = element.value).tblPr) !== null && _a !== void 0 ? _a : (_b.tblPr = {}));
|
|
253
|
+
tblPr.tblW = { w: 0, type: 'auto' };
|
|
247
254
|
this.insertElement(element, location);
|
|
248
255
|
return new Table(element, this.content, this);
|
|
249
256
|
}
|
|
@@ -22,7 +22,9 @@ export interface ImageInfo {
|
|
|
22
22
|
* The size and kind of an image, from its header: PNG (IHDR and pHYs), JPEG (SOFn and the JFIF
|
|
23
23
|
* APP0 densities), GIF (the logical screen descriptor) and BMP (the DIB header, with its pixels
|
|
24
24
|
* 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.
|
|
25
|
+
* information, and the same 96 dpi default when a file declares none. An SVG document is told by
|
|
26
|
+
* its root element and has no pixel size (both 0): it can be the part behind an existing picture
|
|
27
|
+
* (CR-005 section 9, a binding that keeps the drawing) but not be placed as a new one.
|
|
26
28
|
*/
|
|
27
29
|
export declare function imageInfoOf(bytes: Uint8Array): ImageInfo;
|
|
28
30
|
/** The natural size of an image in EMU: pixels over the resolution it declares. */
|
|
@@ -49,10 +51,23 @@ export interface NewPicture {
|
|
|
49
51
|
imagePart: ImagePart;
|
|
50
52
|
relId: string;
|
|
51
53
|
}
|
|
54
|
+
/** A new image part, its relationship from the part it was added to, and what its header says. */
|
|
55
|
+
export interface NewImagePart {
|
|
56
|
+
imagePart: ImagePart;
|
|
57
|
+
relId: string;
|
|
58
|
+
info: ImageInfo;
|
|
59
|
+
}
|
|
52
60
|
/**
|
|
53
61
|
* 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
|
-
* `
|
|
62
|
+
* belongs to) with a relationship, and nothing else: docx4j `BinaryPartAbstractImage.createImagePart`
|
|
63
|
+
* (`BindingTraverserXSLT.createImagePartReturnRelId`, where a picture binding replaces an `r:embed`
|
|
64
|
+
* and keeps the drawing, CR-005 section 9). Throws, and adds nothing, when the bytes are not an
|
|
65
|
+
* image this package reads.
|
|
66
|
+
*/
|
|
67
|
+
export declare function addImagePart(source: Part, base64: string): NewImagePart;
|
|
68
|
+
/**
|
|
69
|
+
* Adds the image as a part of `source` with a relationship (`addImagePart`), and builds the run
|
|
70
|
+
* that shows it. docx4j: `BinaryPartAbstractImage.createImagePart` then `createImageInline`.
|
|
56
71
|
*/
|
|
57
72
|
export declare function addImage(source: Part, base64: string, scope: object, maxCx: number | undefined, options?: InlinePictureOptions): NewPicture;
|
|
58
73
|
/** The writable width of a section in EMU (page width less the margins), for scaling. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { inlinePicture } from '@docx4j/generated-objects-ts/builders/wml';
|
|
2
2
|
import { Docx4JException } from '../../opc/exceptions.mjs';
|
|
3
3
|
import { ContentTypes, IMAGE_CONTENT_TYPES_BY_EXTENSION } from '../../opc/ContentTypes.mjs';
|
|
4
|
-
import { base64Decode, base64Encode } from '../../xml/dom.mjs';
|
|
4
|
+
import { base64Decode, base64Encode, decodeXmlText } from '../../xml/dom.mjs';
|
|
5
5
|
import { ImagePart } from '../../parts/BinaryPart.mjs';
|
|
6
6
|
import { PartName } from '../../opc/PartName.mjs';
|
|
7
7
|
import { find, runOf } from './tree.mjs';
|
|
@@ -15,7 +15,9 @@ const EMU_PER_POINT = 12700;
|
|
|
15
15
|
* The size and kind of an image, from its header: PNG (IHDR and pHYs), JPEG (SOFn and the JFIF
|
|
16
16
|
* APP0 densities), GIF (the logical screen descriptor) and BMP (the DIB header, with its pixels
|
|
17
17
|
* per metre). docx4j reads these through XML Graphics Commons' ImageInfo; this is the same
|
|
18
|
-
* information, and the same 96 dpi default when a file declares none.
|
|
18
|
+
* information, and the same 96 dpi default when a file declares none. An SVG document is told by
|
|
19
|
+
* its root element and has no pixel size (both 0): it can be the part behind an existing picture
|
|
20
|
+
* (CR-005 section 9, a binding that keeps the drawing) but not be placed as a new one.
|
|
19
21
|
*/
|
|
20
22
|
export function imageInfoOf(bytes) {
|
|
21
23
|
const png = pngInfo(bytes);
|
|
@@ -30,8 +32,27 @@ export function imageInfoOf(bytes) {
|
|
|
30
32
|
const bmp = bmpInfo(bytes);
|
|
31
33
|
if (bmp)
|
|
32
34
|
return bmp;
|
|
35
|
+
const svg = svgInfo(bytes);
|
|
36
|
+
if (svg)
|
|
37
|
+
return svg;
|
|
33
38
|
const head = Array.from(bytes.subarray(0, 8)).map((b) => b.toString(16).padStart(2, '0')).join(' ');
|
|
34
|
-
throw new Docx4JException(`Unsupported image format (first bytes ${head}); PNG, JPEG, GIF and
|
|
39
|
+
throw new Docx4JException(`Unsupported image format (first bytes ${head}); PNG, JPEG, GIF, BMP and SVG are read here`);
|
|
40
|
+
}
|
|
41
|
+
/** An SVG document: an XML declaration, comments and a doctype allowed before the `svg` root. */
|
|
42
|
+
function svgInfo(b) {
|
|
43
|
+
if (b.length < 5)
|
|
44
|
+
return undefined;
|
|
45
|
+
let head;
|
|
46
|
+
try {
|
|
47
|
+
head = decodeXmlText(b.subarray(0, Math.min(b.length, 4096)));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const body = head.replace(/^\uFEFF/, '').replace(/<\?xml[^>]*\?>/, '').replace(/<!--[\s\S]*?-->/g, '').replace(/<!DOCTYPE[^>]*>/i, '').trimStart();
|
|
53
|
+
if (!/^<(\w+:)?svg[\s>]/.test(body))
|
|
54
|
+
return undefined;
|
|
55
|
+
return { contentType: ContentTypes.IMAGE_SVG, extension: 'svg', widthPx: 0, heightPx: 0, dpiX: DEFAULT_DPI, dpiY: DEFAULT_DPI };
|
|
35
56
|
}
|
|
36
57
|
function be16(b, at) { return (b[at] << 8) | b[at + 1]; }
|
|
37
58
|
function be32(b, at) { return ((b[at] << 24) >>> 0) + (b[at + 1] << 16) + (b[at + 2] << 8) + b[at + 3]; }
|
|
@@ -144,11 +165,12 @@ export function naturalSizeEmu(info) {
|
|
|
144
165
|
}
|
|
145
166
|
/**
|
|
146
167
|
* Adds the image as a part of `source` (the main document part, or the header or footer the body
|
|
147
|
-
* belongs to) with a relationship, and
|
|
148
|
-
* `
|
|
168
|
+
* belongs to) with a relationship, and nothing else: docx4j `BinaryPartAbstractImage.createImagePart`
|
|
169
|
+
* (`BindingTraverserXSLT.createImagePartReturnRelId`, where a picture binding replaces an `r:embed`
|
|
170
|
+
* and keeps the drawing, CR-005 section 9). Throws, and adds nothing, when the bytes are not an
|
|
171
|
+
* image this package reads.
|
|
149
172
|
*/
|
|
150
|
-
export function
|
|
151
|
-
var _a, _b;
|
|
173
|
+
export function addImagePart(source, base64) {
|
|
152
174
|
const pkg = source.package;
|
|
153
175
|
if (!pkg)
|
|
154
176
|
throw new Docx4JException('This part is not in a package yet; add it before adding an image');
|
|
@@ -157,6 +179,21 @@ export function addImage(source, base64, scope, maxCx, options = {}) {
|
|
|
157
179
|
const imagePart = new ImagePart(freeImageName(pkg, info.extension), info.contentType);
|
|
158
180
|
imagePart.setBytes(bytes);
|
|
159
181
|
const rel = source.addTargetPart(imagePart);
|
|
182
|
+
return { imagePart, relId: rel.id, info };
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Adds the image as a part of `source` with a relationship (`addImagePart`), and builds the run
|
|
186
|
+
* that shows it. docx4j: `BinaryPartAbstractImage.createImagePart` then `createImageInline`.
|
|
187
|
+
*/
|
|
188
|
+
export function addImage(source, base64, scope, maxCx, options = {}) {
|
|
189
|
+
var _a, _b;
|
|
190
|
+
const bytes = base64Decode(base64);
|
|
191
|
+
const info = imageInfoOf(bytes);
|
|
192
|
+
if (info.widthPx === 0 || info.heightPx === 0) {
|
|
193
|
+
throw new Docx4JException(`A ${info.extension.toUpperCase()} image has no pixel size to place it at; PNG, JPEG, GIF and BMP can be placed`);
|
|
194
|
+
}
|
|
195
|
+
const { imagePart, relId } = addImagePart(source, base64);
|
|
196
|
+
const rel = { id: relId };
|
|
160
197
|
let { cx, cy } = naturalSizeEmu(info);
|
|
161
198
|
if (options.width !== undefined) {
|
|
162
199
|
const wanted = Math.round(options.width * EMU_PER_POINT);
|
|
@@ -4,6 +4,7 @@ import type { Body } from './Body.mjs';
|
|
|
4
4
|
import type { Paragraph } from './Paragraph.mjs';
|
|
5
5
|
import type { Range } from './Range.mjs';
|
|
6
6
|
import { type ChangeTracker } from './tracking.mjs';
|
|
7
|
+
import { type TableLayoutMode } from './tableColumns.mjs';
|
|
7
8
|
/**
|
|
8
9
|
* A subset of Office JS `Word.Table` over a `w:tbl`. Rows are read through row-level content
|
|
9
10
|
* controls (an OpenDoPE repeat wraps its `w:tr` in a `w:sdt`), so `rows` is what Word shows.
|
|
@@ -51,6 +52,30 @@ export declare class Table {
|
|
|
51
52
|
* tracks changes the rows stay and take a `w:trPr/w:del` instead (CR-002 phase F).
|
|
52
53
|
*/
|
|
53
54
|
deleteRows(rowIndex: number, rowCount?: number): void;
|
|
55
|
+
/**
|
|
56
|
+
* Adds columns at the start or the end (Office JS `Table.addColumns`), the new cells taking the
|
|
57
|
+
* properties and width of the column beside them - the one to the right of the boundary when
|
|
58
|
+
* there is one, else the left - in the table's layout mode as Word 365 writes it (CR-002 section
|
|
59
|
+
* 42, checks 39 and 40): fixed copies the width and grows the table past the margin, AutoFit to
|
|
60
|
+
* window rescales the grid and writes the cells' percentages again, AutoFit to contents stays
|
|
61
|
+
* automatic. `values` is by row, then by new column. Returns the new cells per row, an extension
|
|
62
|
+
* (Office JS returns nothing); a row whose skipped columns grew instead has none. Tracked, the
|
|
63
|
+
* table change is recorded as Office JS records one and the new cells' text is an insertion.
|
|
64
|
+
*/
|
|
65
|
+
addColumns(location: 'Start' | 'End', columnCount: number, values?: string[][]): TableCell[][];
|
|
66
|
+
/**
|
|
67
|
+
* Removes `columnCount` grid columns from `columnIndex` (one by default; Office JS
|
|
68
|
+
* `Table.deleteColumns`): a cell covering only those columns goes, one spanning further shrinks.
|
|
69
|
+
* Tracked, the cells still go and the table change is recorded, as Office JS's deletion leaves
|
|
70
|
+
* it (section 42.3; check 40c; Word's own is not tracked at all, known issues entry 2).
|
|
71
|
+
*/
|
|
72
|
+
deleteColumns(columnIndex: number, columnCount?: number): void;
|
|
73
|
+
/** Which of Word's three layout modes the table is in: fixed, AutoFit to window, or AutoFit to contents (extension). */
|
|
74
|
+
get layoutMode(): TableLayoutMode;
|
|
75
|
+
/** The section's text width in twips, for the window mode and a percentage width. */
|
|
76
|
+
textWidth(): number;
|
|
77
|
+
/** Views of new cells, row by row. */
|
|
78
|
+
cellViewsOf(cells: Element<wml.Tc>[][]): TableCell[][];
|
|
54
79
|
/** Removes the table from its container; tracked, every row is marked deleted instead. */
|
|
55
80
|
delete(): void;
|
|
56
81
|
/** The package's change tracker while `changeTrackingMode` is on (CR-002 phase F). */
|
|
@@ -118,7 +143,20 @@ export declare class TableCell {
|
|
|
118
143
|
insertText(text: string, location: 'Start' | 'End' | 'Replace'): Range;
|
|
119
144
|
/** The cell width in points (w:tcW in twips), as Office JS reports it; 0 when it is not a fixed width. */
|
|
120
145
|
get width(): number;
|
|
146
|
+
/**
|
|
147
|
+
* Written in the form the cell states, else its table, else twips (CR-002 section 42.2 item 4): a
|
|
148
|
+
* cell in percent takes the points as a share of the section's text width; `auto` becomes twips,
|
|
149
|
+
* a stated width being what was asked for.
|
|
150
|
+
*/
|
|
121
151
|
set width(points: number);
|
|
152
|
+
/**
|
|
153
|
+
* New columns before or after this cell (Office JS `TableCell.insertColumns`), the new cells
|
|
154
|
+
* taking this cell's properties and width, as `Table.addColumns` does. Returns the new cells per
|
|
155
|
+
* row (extension).
|
|
156
|
+
*/
|
|
157
|
+
insertColumns(location: 'Before' | 'After', columnCount: number, values?: string[][]): TableCell[][];
|
|
158
|
+
/** Removes the grid columns this cell covers (Office JS `TableCell.deleteColumn`); tracked as `Table.deleteColumns` is. */
|
|
159
|
+
deleteColumn(): void;
|
|
122
160
|
/** Office JS TableCell.columnWidth, the same value. */
|
|
123
161
|
get columnWidth(): number;
|
|
124
162
|
set columnWidth(points: number);
|
|
@@ -4,6 +4,7 @@ import { Docx4JException } from '../../opc/exceptions.mjs';
|
|
|
4
4
|
import { typeNameOf, childrenOf, linkParents, textOf, rowsOf, cellsOf } from './tree.mjs';
|
|
5
5
|
import { builtInOf, idOfBuiltIn, styleNameOf, styleIdOf } from './styles.mjs';
|
|
6
6
|
import { trackInsertedBlocks } from './tracking.mjs';
|
|
7
|
+
import { insertGridColumns, deleteGridColumns, gridRangeOf, textWidthTwips, widthTypeOf, layoutModeOf } from './tableColumns.mjs';
|
|
7
8
|
/** The default table width in twips (A4 with 2.54 cm margins), as the objects package's `tbl` builder uses. */
|
|
8
9
|
const DEFAULT_WIDTH = 9026;
|
|
9
10
|
const TWIPS_PER_POINT = 20;
|
|
@@ -128,6 +129,51 @@ export class Table {
|
|
|
128
129
|
row.container.splice(i, 1);
|
|
129
130
|
}
|
|
130
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Adds columns at the start or the end (Office JS `Table.addColumns`), the new cells taking the
|
|
134
|
+
* properties and width of the column beside them - the one to the right of the boundary when
|
|
135
|
+
* there is one, else the left - in the table's layout mode as Word 365 writes it (CR-002 section
|
|
136
|
+
* 42, checks 39 and 40): fixed copies the width and grows the table past the margin, AutoFit to
|
|
137
|
+
* window rescales the grid and writes the cells' percentages again, AutoFit to contents stays
|
|
138
|
+
* automatic. `values` is by row, then by new column. Returns the new cells per row, an extension
|
|
139
|
+
* (Office JS returns nothing); a row whose skipped columns grew instead has none. Tracked, the
|
|
140
|
+
* table change is recorded as Office JS records one and the new cells' text is an insertion.
|
|
141
|
+
*/
|
|
142
|
+
addColumns(location, columnCount, values) {
|
|
143
|
+
const at = location === 'Start' ? 0 : this.columnWidths().length;
|
|
144
|
+
const inserted = insertGridColumns(this.tbl, this.textWidth(), at, columnCount, values, this.changeTracker);
|
|
145
|
+
return this.cellViewsOf(inserted.cells);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Removes `columnCount` grid columns from `columnIndex` (one by default; Office JS
|
|
149
|
+
* `Table.deleteColumns`): a cell covering only those columns goes, one spanning further shrinks.
|
|
150
|
+
* Tracked, the cells still go and the table change is recorded, as Office JS's deletion leaves
|
|
151
|
+
* it (section 42.3; check 40c; Word's own is not tracked at all, known issues entry 2).
|
|
152
|
+
*/
|
|
153
|
+
deleteColumns(columnIndex, columnCount = 1) {
|
|
154
|
+
var _a, _b, _c;
|
|
155
|
+
deleteGridColumns(this.tbl, this.textWidth(), columnIndex, columnCount, this.changeTracker);
|
|
156
|
+
if (((_c = (_b = (_a = this.tbl.tblGrid) === null || _a === void 0 ? void 0 : _a.gridCol) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0) === 0 || rowsOf(this.tbl).every((row) => cellsOf(row.element.value).length === 0))
|
|
157
|
+
this.delete();
|
|
158
|
+
}
|
|
159
|
+
/** Which of Word's three layout modes the table is in: fixed, AutoFit to window, or AutoFit to contents (extension). */
|
|
160
|
+
get layoutMode() {
|
|
161
|
+
return layoutModeOf(this.tbl);
|
|
162
|
+
}
|
|
163
|
+
/** The section's text width in twips, for the window mode and a percentage width. */
|
|
164
|
+
textWidth() {
|
|
165
|
+
return textWidthTwips(this.parentBody.container);
|
|
166
|
+
}
|
|
167
|
+
/** Views of new cells, row by row. */
|
|
168
|
+
cellViewsOf(cells) {
|
|
169
|
+
const rows = this.rows;
|
|
170
|
+
return cells.map((rowCells, i) => {
|
|
171
|
+
const row = rows[i];
|
|
172
|
+
if (!row || rowCells.length === 0)
|
|
173
|
+
return [];
|
|
174
|
+
return row.cells.filter((cell) => rowCells.includes(cell.element));
|
|
175
|
+
});
|
|
176
|
+
}
|
|
131
177
|
/** Removes the table from its container; tracked, every row is marked deleted instead. */
|
|
132
178
|
delete() {
|
|
133
179
|
const tracker = this.changeTracker;
|
|
@@ -311,12 +357,44 @@ export class TableCell {
|
|
|
311
357
|
const w = (_a = this.tc.tcPr) === null || _a === void 0 ? void 0 : _a.tcW;
|
|
312
358
|
return (w === null || w === void 0 ? void 0 : w.type) === 'dxa' ? ((_b = w.w) !== null && _b !== void 0 ? _b : 0) / TWIPS_PER_POINT : 0;
|
|
313
359
|
}
|
|
360
|
+
/**
|
|
361
|
+
* Written in the form the cell states, else its table, else twips (CR-002 section 42.2 item 4): a
|
|
362
|
+
* cell in percent takes the points as a share of the section's text width; `auto` becomes twips,
|
|
363
|
+
* a stated width being what was asked for.
|
|
364
|
+
*/
|
|
314
365
|
set width(points) {
|
|
315
366
|
var _a, _b;
|
|
316
367
|
var _c, _d;
|
|
368
|
+
const twips = Math.round(points * TWIPS_PER_POINT);
|
|
317
369
|
const tcW = ((_b = (_d = ((_a = (_c = this.tc).tcPr) !== null && _a !== void 0 ? _a : (_c.tcPr = {}))).tcW) !== null && _b !== void 0 ? _b : (_d.tcW = {}));
|
|
318
|
-
|
|
319
|
-
|
|
370
|
+
if (widthTypeOf(this.tc, this.parentTable.tbl) === 'pct') {
|
|
371
|
+
tcW.w = Math.round((twips * 5000) / this.parentTable.textWidth());
|
|
372
|
+
tcW.type = 'pct';
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
tcW.w = twips;
|
|
376
|
+
tcW.type = 'dxa';
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* New columns before or after this cell (Office JS `TableCell.insertColumns`), the new cells
|
|
381
|
+
* taking this cell's properties and width, as `Table.addColumns` does. Returns the new cells per
|
|
382
|
+
* row (extension).
|
|
383
|
+
*/
|
|
384
|
+
insertColumns(location, columnCount, values) {
|
|
385
|
+
const range = gridRangeOf(this.parentRow.tr, this.tc);
|
|
386
|
+
if (!range)
|
|
387
|
+
throw new Docx4JException('This cell is not in its row');
|
|
388
|
+
const table = this.parentTable;
|
|
389
|
+
const inserted = insertGridColumns(table.tbl, table.textWidth(), location === 'Before' ? range.start : range.end, columnCount, values, table.changeTracker);
|
|
390
|
+
return table.cellViewsOf(inserted.cells);
|
|
391
|
+
}
|
|
392
|
+
/** Removes the grid columns this cell covers (Office JS `TableCell.deleteColumn`); tracked as `Table.deleteColumns` is. */
|
|
393
|
+
deleteColumn() {
|
|
394
|
+
const range = gridRangeOf(this.parentRow.tr, this.tc);
|
|
395
|
+
if (!range)
|
|
396
|
+
throw new Docx4JException('This cell is not in its row');
|
|
397
|
+
this.parentTable.deleteColumns(range.start, range.end - range.start);
|
|
320
398
|
}
|
|
321
399
|
/** Office JS TableCell.columnWidth, the same value. */
|
|
322
400
|
get columnWidth() {
|
|
@@ -7,7 +7,8 @@ export { List, ListItem, ListItemNotFoundError, NumberingFacade, listsOf, listLa
|
|
|
7
7
|
export { type Author, type CommentContent, type CommentOptions, type CommentEntry, type CommentParts, type CommentPartsAccess, type CommentsExtensible, type CommentMarker, COMMENT_TEXT_STYLE, COMMENT_REFERENCE_STYLE, markersOf, markersOfParagraph, commentIdsOf } from './comments.mjs';
|
|
8
8
|
export { Font, type UnderlineType, type RPrHolder, type FontTracking } from './Font.mjs';
|
|
9
9
|
export { Table, TableRow, TableCell, cellOf } from './Table.mjs';
|
|
10
|
-
export {
|
|
10
|
+
export { layoutModeOf, textWidthTwips, gridWidthsOf, widthTypeOf, insertGridColumns, deleteGridColumns, gridRangeOf, type TableLayoutMode, type ColumnInsertion } from './tableColumns.mjs';
|
|
11
|
+
export { InlinePicture, imageInfoOf, naturalSizeEmu, addImage, addImagePart, writableWidthEmu, type ImageFormat, type ImageInfo, type InlinePictureOptions, type NewPicture, type NewImagePart } from './InlinePicture.mjs';
|
|
11
12
|
export { ContentControl, collectControls, collectRunControls, W14_NS, W15_NS, type ContentControlType, type ContentControlForm, type ContentControlAppearance } from './ContentControl.mjs';
|
|
12
13
|
export { contentOf, isFlatOpc, rewriteRelationshipIds, type OoxmlOptions } from './ooxml.mjs';
|
|
13
14
|
export { type SearchOptions, searchPattern, findAll, matchesOf, expandReplacement } from './search.mjs';
|
|
@@ -9,7 +9,8 @@ export { List, ListItem, ListItemNotFoundError, NumberingFacade, listsOf, listLa
|
|
|
9
9
|
export { COMMENT_TEXT_STYLE, COMMENT_REFERENCE_STYLE, markersOf, markersOfParagraph, commentIdsOf } from './comments.mjs';
|
|
10
10
|
export { Font } from './Font.mjs';
|
|
11
11
|
export { Table, TableRow, TableCell, cellOf } from './Table.mjs';
|
|
12
|
-
export {
|
|
12
|
+
export { layoutModeOf, textWidthTwips, gridWidthsOf, widthTypeOf, insertGridColumns, deleteGridColumns, gridRangeOf } from './tableColumns.mjs';
|
|
13
|
+
export { InlinePicture, imageInfoOf, naturalSizeEmu, addImage, addImagePart, writableWidthEmu } from './InlinePicture.mjs';
|
|
13
14
|
export { ContentControl, collectControls, collectRunControls, W14_NS, W15_NS } from './ContentControl.mjs';
|
|
14
15
|
export { contentOf, isFlatOpc, rewriteRelationshipIds } from './ooxml.mjs';
|
|
15
16
|
export { searchPattern, findAll, matchesOf, expandReplacement } from './search.mjs';
|
|
@@ -23,8 +23,10 @@ export declare class StylesFacade {
|
|
|
23
23
|
*
|
|
24
24
|
* A style already in the document is left exactly as it is - the document's own definition wins
|
|
25
25
|
* over the default, always, since a consumer must not silently restyle a document it was asked to
|
|
26
|
-
* add a footnote to. An id
|
|
27
|
-
*
|
|
26
|
+
* add a footnote to. An id none of the sources carry - the defaults, the nine spliceable styles,
|
|
27
|
+
* the comment and placeholder styles, and Word's built-ins from docx4j's `KnownStyles.xml`
|
|
28
|
+
* (section 41) - throws a `Docx4JException` naming it: the caller asked for a definition nothing
|
|
29
|
+
* here has, and inventing one would be worse than saying so.
|
|
28
30
|
* The whole closure is worked out before anything is written, so a call that throws has changed
|
|
29
31
|
* nothing (CR-002 section 31).
|
|
30
32
|
*
|
|
@@ -5,13 +5,16 @@ import { DEFAULT_STYLES_XML } from '../../parts/wml/defaultStyles.mjs';
|
|
|
5
5
|
import { SPLICEABLE_STYLES_XML } from '../../parts/wml/spliceableStyles.mjs';
|
|
6
6
|
import { commentStyles } from '../../parts/wml/commentStyles.mjs';
|
|
7
7
|
import { placeholderStyle } from '../../parts/wml/placeholderStyle.mjs';
|
|
8
|
+
import { KNOWN_STYLES_XML } from '../../parts/wml/knownStyles.mjs';
|
|
8
9
|
import { Docx4JException } from '../../opc/exceptions.mjs';
|
|
9
10
|
/**
|
|
10
11
|
* Where a definition is looked up, unmarshalled once and never handed out (a copy is spliced in):
|
|
11
12
|
* the defaults `createPackage()` writes, and then the nine styles docx4j's own `styles.xml` carries
|
|
12
13
|
* **commented out** - which is why a created document has no `FootnoteText` although the file
|
|
13
14
|
* appears to contain one, and why this facade exists (CR-002 section 22.2) - with Word's two comment
|
|
14
|
-
* styles and its `PlaceholderText` after them
|
|
15
|
+
* styles and its `PlaceholderText` after them, and last Word's 164 built-ins as docx4j's
|
|
16
|
+
* `KnownStyles.xml` carries them (CR-002 section 41: `Heading5`, `Quote`, `HTMLCode`, ...), the
|
|
17
|
+
* list numbering some of them name dropped, since no numbering definition comes with them.
|
|
15
18
|
*/
|
|
16
19
|
let sources;
|
|
17
20
|
function styleSources() {
|
|
@@ -20,8 +23,17 @@ function styleSources() {
|
|
|
20
23
|
const defaults = await read(DEFAULT_STYLES_XML);
|
|
21
24
|
// and Word's two comment styles, which the comment parts add when they are made, for a caller
|
|
22
25
|
// that makes the comment itself (CR-002 section 37), and Word's PlaceholderText, which a
|
|
23
|
-
// placeholder run names (CR-002 section 40)
|
|
24
|
-
|
|
26
|
+
// placeholder run names (CR-002 section 40); then the known styles (section 41), each without
|
|
27
|
+
// the w:numPr the file left on the headings and list styles: it names a w:numId of the document
|
|
28
|
+
// the file was taken from, which the document being spliced into need not have, and a heading
|
|
29
|
+
// spliced with it would join a list that is not there (the editor's copy drops it the same way)
|
|
30
|
+
const known = (await read(KNOWN_STYLES_XML)).map((style) => {
|
|
31
|
+
var _a;
|
|
32
|
+
if ((_a = style.pPr) === null || _a === void 0 ? void 0 : _a.numPr)
|
|
33
|
+
delete style.pPr.numPr;
|
|
34
|
+
return style;
|
|
35
|
+
});
|
|
36
|
+
return { defaults, all: [...defaults, ...(await read(SPLICEABLE_STYLES_XML)), ...commentStyles(), placeholderStyle(), ...known] };
|
|
25
37
|
})()));
|
|
26
38
|
}
|
|
27
39
|
/**
|
|
@@ -42,8 +54,10 @@ export class StylesFacade {
|
|
|
42
54
|
*
|
|
43
55
|
* A style already in the document is left exactly as it is - the document's own definition wins
|
|
44
56
|
* over the default, always, since a consumer must not silently restyle a document it was asked to
|
|
45
|
-
* add a footnote to. An id
|
|
46
|
-
*
|
|
57
|
+
* add a footnote to. An id none of the sources carry - the defaults, the nine spliceable styles,
|
|
58
|
+
* the comment and placeholder styles, and Word's built-ins from docx4j's `KnownStyles.xml`
|
|
59
|
+
* (section 41) - throws a `Docx4JException` naming it: the caller asked for a definition nothing
|
|
60
|
+
* here has, and inventing one would be worse than saying so.
|
|
47
61
|
* The whole closure is worked out before anything is written, so a call that throws has changed
|
|
48
62
|
* nothing (CR-002 section 31).
|
|
49
63
|
*
|
|
@@ -105,7 +119,7 @@ function closureOf(wanted, present, source) {
|
|
|
105
119
|
seen.add(id);
|
|
106
120
|
const template = source.find((s) => s.styleId === id);
|
|
107
121
|
if (template === undefined) {
|
|
108
|
-
throw new Docx4JException(`No definition to splice for the style ${id}: neither docx4j's default styles, the nine it comments out, the two comment styles nor
|
|
122
|
+
throw new Docx4JException(`No definition to splice for the style ${id}: neither docx4j's default styles, the nine it comments out, the two comment styles, PlaceholderText nor Word's built-ins in docx4j's KnownStyles.xml carry one. Define it yourself on the styles part.`);
|
|
109
123
|
}
|
|
110
124
|
// what it is based on and linked to must be there too, and before it reads better in the part
|
|
111
125
|
for (const dependency of [(_a = template.basedOn) === null || _a === void 0 ? void 0 : _a.val, (_b = template.link) === null || _b === void 0 ? void 0 : _b.val]) {
|