@apollo-design/docx-preview 0.6.2
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/LICENSE +201 -0
- package/NOTICE +6 -0
- package/README.md +155 -0
- package/dist/docx-preview.cjs +4190 -0
- package/dist/docx-preview.d.ts +57 -0
- package/dist/docx-preview.js +4190 -0
- package/dist/docx-preview.js.map +1 -0
- package/dist/docx-preview.min.js +8 -0
- package/dist/docx-preview.min.js.map +1 -0
- package/dist/docx-preview.min.mjs +8 -0
- package/dist/docx-preview.min.mjs.map +1 -0
- package/dist/docx-preview.mjs +4096 -0
- package/dist/docx-preview.mjs.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,4096 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @license
|
|
3
|
+
* @apollo-design/docx-preview <https://github.com/apollocmh/docx-preview>
|
|
4
|
+
* Released under Apache License 2.0 — Copyright apollocmh
|
|
5
|
+
* Based on docxjs by Volodymyr Baydalka <https://github.com/VolodymyrBaydalka/docxjs>
|
|
6
|
+
*/
|
|
7
|
+
import JSZip from "jszip";
|
|
8
|
+
//#region src/common/relationship.ts
|
|
9
|
+
var RelationshipTypes = /* @__PURE__ */ function(RelationshipTypes) {
|
|
10
|
+
RelationshipTypes["OfficeDocument"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
|
|
11
|
+
RelationshipTypes["FontTable"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable";
|
|
12
|
+
RelationshipTypes["Image"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
|
|
13
|
+
RelationshipTypes["Numbering"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering";
|
|
14
|
+
RelationshipTypes["Styles"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
|
|
15
|
+
RelationshipTypes["StylesWithEffects"] = "http://schemas.microsoft.com/office/2007/relationships/stylesWithEffects";
|
|
16
|
+
RelationshipTypes["Theme"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
|
|
17
|
+
RelationshipTypes["Settings"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings";
|
|
18
|
+
RelationshipTypes["WebSettings"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings";
|
|
19
|
+
RelationshipTypes["Hyperlink"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
|
|
20
|
+
RelationshipTypes["Footnotes"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes";
|
|
21
|
+
RelationshipTypes["Endnotes"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes";
|
|
22
|
+
RelationshipTypes["Footer"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer";
|
|
23
|
+
RelationshipTypes["Header"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header";
|
|
24
|
+
RelationshipTypes["ExtendedProperties"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties";
|
|
25
|
+
RelationshipTypes["CoreProperties"] = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties";
|
|
26
|
+
RelationshipTypes["CustomProperties"] = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/custom-properties";
|
|
27
|
+
RelationshipTypes["Comments"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments";
|
|
28
|
+
RelationshipTypes["CommentsExtended"] = "http://schemas.microsoft.com/office/2011/relationships/commentsExtended";
|
|
29
|
+
RelationshipTypes["AltChunk"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk";
|
|
30
|
+
return RelationshipTypes;
|
|
31
|
+
}({});
|
|
32
|
+
function parseRelationships(root, xml) {
|
|
33
|
+
return xml.elements(root).map((e) => ({
|
|
34
|
+
id: xml.attr(e, "Id"),
|
|
35
|
+
type: xml.attr(e, "Type"),
|
|
36
|
+
target: xml.attr(e, "Target"),
|
|
37
|
+
targetMode: xml.attr(e, "TargetMode")
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/utils.ts
|
|
42
|
+
function escapeClassName(className) {
|
|
43
|
+
return className?.replace(/[ .]+/g, "-").replace(/[&]+/g, "and").toLowerCase();
|
|
44
|
+
}
|
|
45
|
+
function encloseFontFamily(fontFamily) {
|
|
46
|
+
return /^[^"'].*\s.*[^"']$/.test(fontFamily) ? `'${fontFamily}'` : fontFamily;
|
|
47
|
+
}
|
|
48
|
+
function splitPath(path) {
|
|
49
|
+
let si = path.lastIndexOf("/") + 1;
|
|
50
|
+
return [si == 0 ? "" : path.substring(0, si), si == 0 ? path : path.substring(si)];
|
|
51
|
+
}
|
|
52
|
+
function resolvePath(path, base) {
|
|
53
|
+
try {
|
|
54
|
+
return new URL(path, "http://docx/" + base).toString().substring(12);
|
|
55
|
+
} catch {
|
|
56
|
+
return `${base}${path}`;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function keyBy(array, by) {
|
|
60
|
+
return array.reduce((a, x) => {
|
|
61
|
+
a[by(x)] = x;
|
|
62
|
+
return a;
|
|
63
|
+
}, {});
|
|
64
|
+
}
|
|
65
|
+
function blobToBase64(blob) {
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
const reader = new FileReader();
|
|
68
|
+
reader.onloadend = () => resolve(reader.result);
|
|
69
|
+
reader.onerror = () => reject();
|
|
70
|
+
reader.readAsDataURL(blob);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function isObject(item) {
|
|
74
|
+
return item && typeof item === "object" && !Array.isArray(item);
|
|
75
|
+
}
|
|
76
|
+
function isString(item) {
|
|
77
|
+
return typeof item === "string" || item instanceof String;
|
|
78
|
+
}
|
|
79
|
+
function mergeDeep(target, ...sources) {
|
|
80
|
+
if (!sources.length) return target;
|
|
81
|
+
const source = sources.shift();
|
|
82
|
+
if (isObject(target) && isObject(source)) for (const key in source) if (isObject(source[key])) mergeDeep(target[key] ?? (target[key] = {}), source[key]);
|
|
83
|
+
else target[key] = source[key];
|
|
84
|
+
return mergeDeep(target, ...sources);
|
|
85
|
+
}
|
|
86
|
+
function asArray(val) {
|
|
87
|
+
return Array.isArray(val) ? val : [val];
|
|
88
|
+
}
|
|
89
|
+
function clamp(val, min, max) {
|
|
90
|
+
return min > val ? min : max < val ? max : val;
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/document/common.ts
|
|
94
|
+
var ns$1 = {
|
|
95
|
+
wordml: "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
|
96
|
+
drawingml: "http://schemas.openxmlformats.org/drawingml/2006/main",
|
|
97
|
+
picture: "http://schemas.openxmlformats.org/drawingml/2006/picture",
|
|
98
|
+
compatibility: "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
|
99
|
+
math: "http://schemas.openxmlformats.org/officeDocument/2006/math"
|
|
100
|
+
};
|
|
101
|
+
var LengthUsage = {
|
|
102
|
+
Dxa: {
|
|
103
|
+
mul: .05,
|
|
104
|
+
unit: "pt"
|
|
105
|
+
},
|
|
106
|
+
Emu: {
|
|
107
|
+
mul: 1 / 12700,
|
|
108
|
+
unit: "pt"
|
|
109
|
+
},
|
|
110
|
+
FontSize: {
|
|
111
|
+
mul: .5,
|
|
112
|
+
unit: "pt"
|
|
113
|
+
},
|
|
114
|
+
Border: {
|
|
115
|
+
mul: .125,
|
|
116
|
+
unit: "pt",
|
|
117
|
+
min: .25,
|
|
118
|
+
max: 12
|
|
119
|
+
},
|
|
120
|
+
Point: {
|
|
121
|
+
mul: 1,
|
|
122
|
+
unit: "pt"
|
|
123
|
+
},
|
|
124
|
+
Percent: {
|
|
125
|
+
mul: .02,
|
|
126
|
+
unit: "%"
|
|
127
|
+
},
|
|
128
|
+
LineHeight: {
|
|
129
|
+
mul: 1 / 240,
|
|
130
|
+
unit: ""
|
|
131
|
+
},
|
|
132
|
+
VmlEmu: {
|
|
133
|
+
mul: 1 / 12700,
|
|
134
|
+
unit: ""
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
function convertLength(val, usage = LengthUsage.Dxa) {
|
|
138
|
+
if (val == null || /.+(p[xt]|[%])$/.test(val)) return val;
|
|
139
|
+
var num = parseInt(val) * usage.mul;
|
|
140
|
+
if (usage.min && usage.max) num = clamp(num, usage.min, usage.max);
|
|
141
|
+
return `${num.toFixed(2)}${usage.unit}`;
|
|
142
|
+
}
|
|
143
|
+
function convertBoolean(v, defaultValue = false) {
|
|
144
|
+
switch (v) {
|
|
145
|
+
case "1": return true;
|
|
146
|
+
case "0": return false;
|
|
147
|
+
case "on": return true;
|
|
148
|
+
case "off": return false;
|
|
149
|
+
case "true": return true;
|
|
150
|
+
case "false": return false;
|
|
151
|
+
default: return defaultValue;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function parseCommonProperty(elem, props, xml) {
|
|
155
|
+
if (elem.namespaceURI != ns$1.wordml) return false;
|
|
156
|
+
switch (elem.localName) {
|
|
157
|
+
case "color":
|
|
158
|
+
props.color = xml.attr(elem, "val");
|
|
159
|
+
break;
|
|
160
|
+
case "sz":
|
|
161
|
+
props.fontSize = xml.lengthAttr(elem, "val", LengthUsage.FontSize);
|
|
162
|
+
break;
|
|
163
|
+
default: return false;
|
|
164
|
+
}
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region src/parser/xml-parser.ts
|
|
169
|
+
function parseXmlString(xmlString, trimXmlDeclaration = false) {
|
|
170
|
+
if (trimXmlDeclaration) xmlString = xmlString.replace(/<[?].*[?]>/, "");
|
|
171
|
+
xmlString = removeUTF8BOM(xmlString);
|
|
172
|
+
const result = new DOMParser().parseFromString(xmlString, "application/xml");
|
|
173
|
+
const errorText = hasXmlParserError(result);
|
|
174
|
+
if (errorText) throw new Error(errorText);
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
function hasXmlParserError(doc) {
|
|
178
|
+
return doc.getElementsByTagName("parsererror")[0]?.textContent;
|
|
179
|
+
}
|
|
180
|
+
function removeUTF8BOM(data) {
|
|
181
|
+
return data.charCodeAt(0) === 65279 ? data.substring(1) : data;
|
|
182
|
+
}
|
|
183
|
+
function serializeXmlString(elem) {
|
|
184
|
+
return new XMLSerializer().serializeToString(elem);
|
|
185
|
+
}
|
|
186
|
+
var XmlParser = class {
|
|
187
|
+
elements(elem, localName = null) {
|
|
188
|
+
const result = [];
|
|
189
|
+
for (let i = 0, l = elem.childNodes.length; i < l; i++) {
|
|
190
|
+
let c = elem.childNodes.item(i);
|
|
191
|
+
if (c.nodeType == Node.ELEMENT_NODE && (localName == null || c.localName == localName)) result.push(c);
|
|
192
|
+
}
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
element(elem, localName) {
|
|
196
|
+
for (let i = 0, l = elem.childNodes.length; i < l; i++) {
|
|
197
|
+
let c = elem.childNodes.item(i);
|
|
198
|
+
if (c.nodeType == 1 && c.localName == localName) return c;
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
elementAttr(elem, localName, attrLocalName) {
|
|
203
|
+
var el = this.element(elem, localName);
|
|
204
|
+
return el ? this.attr(el, attrLocalName) : void 0;
|
|
205
|
+
}
|
|
206
|
+
attrs(elem) {
|
|
207
|
+
return Array.from(elem.attributes);
|
|
208
|
+
}
|
|
209
|
+
attr(elem, localName) {
|
|
210
|
+
for (let i = 0, l = elem.attributes.length; i < l; i++) {
|
|
211
|
+
let a = elem.attributes.item(i);
|
|
212
|
+
if (a.localName == localName) return a.value;
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
intAttr(node, attrName, defaultValue = null) {
|
|
217
|
+
var val = this.attr(node, attrName);
|
|
218
|
+
return val ? parseInt(val) : defaultValue;
|
|
219
|
+
}
|
|
220
|
+
hexAttr(node, attrName, defaultValue = null) {
|
|
221
|
+
var val = this.attr(node, attrName);
|
|
222
|
+
return val ? parseInt(val, 16) : defaultValue;
|
|
223
|
+
}
|
|
224
|
+
floatAttr(node, attrName, defaultValue = null) {
|
|
225
|
+
var val = this.attr(node, attrName);
|
|
226
|
+
return val ? parseFloat(val) : defaultValue;
|
|
227
|
+
}
|
|
228
|
+
boolAttr(node, attrName, defaultValue = null) {
|
|
229
|
+
return convertBoolean(this.attr(node, attrName), defaultValue);
|
|
230
|
+
}
|
|
231
|
+
lengthAttr(node, attrName, usage = LengthUsage.Dxa) {
|
|
232
|
+
return convertLength(this.attr(node, attrName), usage);
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
var globalXmlParser = new XmlParser();
|
|
236
|
+
//#endregion
|
|
237
|
+
//#region src/common/part.ts
|
|
238
|
+
var Part = class {
|
|
239
|
+
constructor(_package, path) {
|
|
240
|
+
this._package = _package;
|
|
241
|
+
this.path = path;
|
|
242
|
+
}
|
|
243
|
+
async load() {
|
|
244
|
+
this.rels = await this._package.loadRelationships(this.path);
|
|
245
|
+
const xmlText = await this._package.load(this.path);
|
|
246
|
+
const xmlDoc = this._package.parseXmlDocument(xmlText);
|
|
247
|
+
if (this._package.options.keepOrigin) this._xmlDocument = xmlDoc;
|
|
248
|
+
this.parseXml(xmlDoc.firstElementChild);
|
|
249
|
+
}
|
|
250
|
+
save() {
|
|
251
|
+
this._package.update(this.path, serializeXmlString(this._xmlDocument));
|
|
252
|
+
}
|
|
253
|
+
parseXml(root) {}
|
|
254
|
+
};
|
|
255
|
+
//#endregion
|
|
256
|
+
//#region src/font-table/fonts.ts
|
|
257
|
+
var embedFontTypeMap = {
|
|
258
|
+
embedRegular: "regular",
|
|
259
|
+
embedBold: "bold",
|
|
260
|
+
embedItalic: "italic",
|
|
261
|
+
embedBoldItalic: "boldItalic"
|
|
262
|
+
};
|
|
263
|
+
function parseFonts(root, xml) {
|
|
264
|
+
return xml.elements(root).map((el) => parseFont(el, xml));
|
|
265
|
+
}
|
|
266
|
+
function parseFont(elem, xml) {
|
|
267
|
+
let result = {
|
|
268
|
+
name: xml.attr(elem, "name"),
|
|
269
|
+
embedFontRefs: []
|
|
270
|
+
};
|
|
271
|
+
for (let el of xml.elements(elem)) switch (el.localName) {
|
|
272
|
+
case "family":
|
|
273
|
+
result.family = xml.attr(el, "val");
|
|
274
|
+
break;
|
|
275
|
+
case "altName":
|
|
276
|
+
result.altName = xml.attr(el, "val");
|
|
277
|
+
break;
|
|
278
|
+
case "embedRegular":
|
|
279
|
+
case "embedBold":
|
|
280
|
+
case "embedItalic":
|
|
281
|
+
case "embedBoldItalic": result.embedFontRefs.push(parseEmbedFontRef(el, xml));
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
function parseEmbedFontRef(elem, xml) {
|
|
286
|
+
return {
|
|
287
|
+
id: xml.attr(elem, "id"),
|
|
288
|
+
key: xml.attr(elem, "fontKey"),
|
|
289
|
+
type: embedFontTypeMap[elem.localName]
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region src/font-table/font-table.ts
|
|
294
|
+
var FontTablePart = class extends Part {
|
|
295
|
+
parseXml(root) {
|
|
296
|
+
this.fonts = parseFonts(root, this._package.xmlParser);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
//#endregion
|
|
300
|
+
//#region src/common/content-types.ts
|
|
301
|
+
function parseContentTypes(root, xml) {
|
|
302
|
+
return xml.elements(root).map((e) => ({
|
|
303
|
+
extension: xml.attr(e, "Extension"),
|
|
304
|
+
partName: xml.attr(e, "PartName"),
|
|
305
|
+
contentType: xml.attr(e, "ContentType")
|
|
306
|
+
}));
|
|
307
|
+
}
|
|
308
|
+
//#endregion
|
|
309
|
+
//#region src/common/open-xml-package.ts
|
|
310
|
+
var OpenXmlPackage = class OpenXmlPackage {
|
|
311
|
+
constructor(_zip, options) {
|
|
312
|
+
this._zip = _zip;
|
|
313
|
+
this.options = options;
|
|
314
|
+
this.xmlParser = new XmlParser();
|
|
315
|
+
}
|
|
316
|
+
get(path) {
|
|
317
|
+
const p = normalizePath(path);
|
|
318
|
+
return this._zip.files[p] ?? this._zip.files[p.replace(/\//g, "\\")];
|
|
319
|
+
}
|
|
320
|
+
update(path, content) {
|
|
321
|
+
this._zip.file(path, content);
|
|
322
|
+
}
|
|
323
|
+
static async load(input, options) {
|
|
324
|
+
const zip = await JSZip.loadAsync(input);
|
|
325
|
+
return new OpenXmlPackage(zip, options);
|
|
326
|
+
}
|
|
327
|
+
save(type = "blob") {
|
|
328
|
+
return this._zip.generateAsync({ type });
|
|
329
|
+
}
|
|
330
|
+
load(path, type = "string") {
|
|
331
|
+
return this.get(path)?.async(type) ?? Promise.resolve(null);
|
|
332
|
+
}
|
|
333
|
+
async loadRelationships(path = null) {
|
|
334
|
+
let relsPath = `_rels/.rels`;
|
|
335
|
+
if (path != null) {
|
|
336
|
+
const [f, fn] = splitPath(path);
|
|
337
|
+
relsPath = `${f}_rels/${fn}.rels`;
|
|
338
|
+
}
|
|
339
|
+
const txt = await this.load(relsPath);
|
|
340
|
+
return txt ? parseRelationships(this.parseXmlDocument(txt).firstElementChild, this.xmlParser) : null;
|
|
341
|
+
}
|
|
342
|
+
async loadContentTypes() {
|
|
343
|
+
const txt = await this.load("[Content_Types].xml");
|
|
344
|
+
return txt ? parseContentTypes(this.parseXmlDocument(txt).firstElementChild, this.xmlParser) : [];
|
|
345
|
+
}
|
|
346
|
+
/** @internal */
|
|
347
|
+
parseXmlDocument(txt) {
|
|
348
|
+
return parseXmlString(txt, this.options.trimXmlDeclaration);
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
function normalizePath(path) {
|
|
352
|
+
return path.startsWith("/") ? path.substr(1) : path;
|
|
353
|
+
}
|
|
354
|
+
//#endregion
|
|
355
|
+
//#region src/document/document-part.ts
|
|
356
|
+
var DocumentPart = class extends Part {
|
|
357
|
+
constructor(pkg, path, parser) {
|
|
358
|
+
super(pkg, path);
|
|
359
|
+
this._documentParser = parser;
|
|
360
|
+
}
|
|
361
|
+
parseXml(root) {
|
|
362
|
+
this.body = this._documentParser.parseDocumentFile(root);
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
//#endregion
|
|
366
|
+
//#region src/document/border.ts
|
|
367
|
+
function parseBorder(elem, xml) {
|
|
368
|
+
return {
|
|
369
|
+
type: xml.attr(elem, "val"),
|
|
370
|
+
color: xml.attr(elem, "color"),
|
|
371
|
+
size: xml.lengthAttr(elem, "sz", LengthUsage.Border),
|
|
372
|
+
offset: xml.lengthAttr(elem, "space", LengthUsage.Point),
|
|
373
|
+
frame: xml.boolAttr(elem, "frame"),
|
|
374
|
+
shadow: xml.boolAttr(elem, "shadow")
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function parseBorders(elem, xml) {
|
|
378
|
+
var result = {};
|
|
379
|
+
for (let e of xml.elements(elem)) switch (e.localName) {
|
|
380
|
+
case "left":
|
|
381
|
+
result.left = parseBorder(e, xml);
|
|
382
|
+
break;
|
|
383
|
+
case "top":
|
|
384
|
+
result.top = parseBorder(e, xml);
|
|
385
|
+
break;
|
|
386
|
+
case "right":
|
|
387
|
+
result.right = parseBorder(e, xml);
|
|
388
|
+
break;
|
|
389
|
+
case "bottom": result.bottom = parseBorder(e, xml);
|
|
390
|
+
}
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
//#endregion
|
|
394
|
+
//#region src/document/section.ts
|
|
395
|
+
function parseSectionProperties(elem, xml = globalXmlParser) {
|
|
396
|
+
var section = {};
|
|
397
|
+
for (let e of xml.elements(elem)) switch (e.localName) {
|
|
398
|
+
case "pgSz":
|
|
399
|
+
section.pageSize = {
|
|
400
|
+
width: xml.lengthAttr(e, "w"),
|
|
401
|
+
height: xml.lengthAttr(e, "h"),
|
|
402
|
+
orientation: xml.attr(e, "orient")
|
|
403
|
+
};
|
|
404
|
+
break;
|
|
405
|
+
case "type":
|
|
406
|
+
section.type = xml.attr(e, "val");
|
|
407
|
+
break;
|
|
408
|
+
case "pgMar":
|
|
409
|
+
section.pageMargins = {
|
|
410
|
+
left: xml.lengthAttr(e, "left"),
|
|
411
|
+
right: xml.lengthAttr(e, "right"),
|
|
412
|
+
top: xml.lengthAttr(e, "top"),
|
|
413
|
+
bottom: xml.lengthAttr(e, "bottom"),
|
|
414
|
+
header: xml.lengthAttr(e, "header"),
|
|
415
|
+
footer: xml.lengthAttr(e, "footer"),
|
|
416
|
+
gutter: xml.lengthAttr(e, "gutter")
|
|
417
|
+
};
|
|
418
|
+
break;
|
|
419
|
+
case "cols":
|
|
420
|
+
section.columns = parseColumns(e, xml);
|
|
421
|
+
break;
|
|
422
|
+
case "headerReference":
|
|
423
|
+
(section.headerRefs ?? (section.headerRefs = [])).push(parseFooterHeaderReference(e, xml));
|
|
424
|
+
break;
|
|
425
|
+
case "footerReference":
|
|
426
|
+
(section.footerRefs ?? (section.footerRefs = [])).push(parseFooterHeaderReference(e, xml));
|
|
427
|
+
break;
|
|
428
|
+
case "titlePg":
|
|
429
|
+
section.titlePage = xml.boolAttr(e, "val", true);
|
|
430
|
+
break;
|
|
431
|
+
case "pgBorders":
|
|
432
|
+
section.pageBorders = parseBorders(e, xml);
|
|
433
|
+
break;
|
|
434
|
+
case "pgNumType": section.pageNumber = parsePageNumber(e, xml);
|
|
435
|
+
}
|
|
436
|
+
return section;
|
|
437
|
+
}
|
|
438
|
+
function parseColumns(elem, xml) {
|
|
439
|
+
return {
|
|
440
|
+
numberOfColumns: xml.intAttr(elem, "num"),
|
|
441
|
+
space: xml.lengthAttr(elem, "space"),
|
|
442
|
+
separator: xml.boolAttr(elem, "sep"),
|
|
443
|
+
equalWidth: xml.boolAttr(elem, "equalWidth", true),
|
|
444
|
+
columns: xml.elements(elem, "col").map((e) => ({
|
|
445
|
+
width: xml.lengthAttr(e, "w"),
|
|
446
|
+
space: xml.lengthAttr(e, "space")
|
|
447
|
+
}))
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function parsePageNumber(elem, xml) {
|
|
451
|
+
return {
|
|
452
|
+
chapSep: xml.attr(elem, "chapSep"),
|
|
453
|
+
chapStyle: xml.attr(elem, "chapStyle"),
|
|
454
|
+
format: xml.attr(elem, "fmt"),
|
|
455
|
+
start: xml.intAttr(elem, "start")
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
function parseFooterHeaderReference(elem, xml) {
|
|
459
|
+
return {
|
|
460
|
+
id: xml.attr(elem, "id"),
|
|
461
|
+
type: xml.attr(elem, "type")
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
//#endregion
|
|
465
|
+
//#region src/document/line-spacing.ts
|
|
466
|
+
function parseLineSpacing(elem, xml) {
|
|
467
|
+
return {
|
|
468
|
+
before: xml.lengthAttr(elem, "before"),
|
|
469
|
+
after: xml.lengthAttr(elem, "after"),
|
|
470
|
+
line: xml.intAttr(elem, "line"),
|
|
471
|
+
lineRule: xml.attr(elem, "lineRule")
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/document/run.ts
|
|
476
|
+
function parseRunProperties(elem, xml) {
|
|
477
|
+
let result = {};
|
|
478
|
+
for (let el of xml.elements(elem)) parseRunProperty(el, result, xml);
|
|
479
|
+
return result;
|
|
480
|
+
}
|
|
481
|
+
function parseRunProperty(elem, props, xml) {
|
|
482
|
+
if (parseCommonProperty(elem, props, xml)) return true;
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
//#endregion
|
|
486
|
+
//#region src/document/paragraph.ts
|
|
487
|
+
function parseParagraphProperties(elem, xml) {
|
|
488
|
+
let result = {};
|
|
489
|
+
for (let el of xml.elements(elem)) parseParagraphProperty(el, result, xml);
|
|
490
|
+
return result;
|
|
491
|
+
}
|
|
492
|
+
function parseParagraphProperty(elem, props, xml) {
|
|
493
|
+
if (elem.namespaceURI != ns$1.wordml) return false;
|
|
494
|
+
if (parseCommonProperty(elem, props, xml)) return true;
|
|
495
|
+
switch (elem.localName) {
|
|
496
|
+
case "tabs":
|
|
497
|
+
props.tabs = parseTabs(elem, xml);
|
|
498
|
+
break;
|
|
499
|
+
case "sectPr":
|
|
500
|
+
props.sectionProps = parseSectionProperties(elem, xml);
|
|
501
|
+
break;
|
|
502
|
+
case "numPr":
|
|
503
|
+
props.numbering = parseNumbering$1(elem, xml);
|
|
504
|
+
break;
|
|
505
|
+
case "spacing":
|
|
506
|
+
props.lineSpacing = parseLineSpacing(elem, xml);
|
|
507
|
+
return false;
|
|
508
|
+
case "textAlignment":
|
|
509
|
+
props.textAlignment = xml.attr(elem, "val");
|
|
510
|
+
return false;
|
|
511
|
+
case "keepLines":
|
|
512
|
+
props.keepLines = xml.boolAttr(elem, "val", true);
|
|
513
|
+
break;
|
|
514
|
+
case "keepNext":
|
|
515
|
+
props.keepNext = xml.boolAttr(elem, "val", true);
|
|
516
|
+
break;
|
|
517
|
+
case "pageBreakBefore":
|
|
518
|
+
props.pageBreakBefore = xml.boolAttr(elem, "val", true);
|
|
519
|
+
break;
|
|
520
|
+
case "outlineLvl":
|
|
521
|
+
props.outlineLevel = xml.intAttr(elem, "val");
|
|
522
|
+
break;
|
|
523
|
+
case "pStyle":
|
|
524
|
+
props.styleName = xml.attr(elem, "val");
|
|
525
|
+
break;
|
|
526
|
+
case "rPr":
|
|
527
|
+
props.runProps = parseRunProperties(elem, xml);
|
|
528
|
+
break;
|
|
529
|
+
default: return false;
|
|
530
|
+
}
|
|
531
|
+
return true;
|
|
532
|
+
}
|
|
533
|
+
function parseTabs(elem, xml) {
|
|
534
|
+
return xml.elements(elem, "tab").map((e) => ({
|
|
535
|
+
position: xml.lengthAttr(e, "pos"),
|
|
536
|
+
leader: xml.attr(e, "leader"),
|
|
537
|
+
style: xml.attr(e, "val")
|
|
538
|
+
}));
|
|
539
|
+
}
|
|
540
|
+
function parseNumbering$1(elem, xml) {
|
|
541
|
+
var result = {};
|
|
542
|
+
for (let e of xml.elements(elem)) switch (e.localName) {
|
|
543
|
+
case "numId":
|
|
544
|
+
result.id = xml.attr(e, "val");
|
|
545
|
+
break;
|
|
546
|
+
case "ilvl": result.level = xml.intAttr(e, "val");
|
|
547
|
+
}
|
|
548
|
+
return result;
|
|
549
|
+
}
|
|
550
|
+
//#endregion
|
|
551
|
+
//#region src/numbering/numbering.ts
|
|
552
|
+
function parseNumberingPart(elem, xml) {
|
|
553
|
+
let result = {
|
|
554
|
+
numberings: [],
|
|
555
|
+
abstractNumberings: [],
|
|
556
|
+
bulletPictures: []
|
|
557
|
+
};
|
|
558
|
+
for (let e of xml.elements(elem)) switch (e.localName) {
|
|
559
|
+
case "num":
|
|
560
|
+
result.numberings.push(parseNumbering(e, xml));
|
|
561
|
+
break;
|
|
562
|
+
case "abstractNum":
|
|
563
|
+
result.abstractNumberings.push(parseAbstractNumbering(e, xml));
|
|
564
|
+
break;
|
|
565
|
+
case "numPicBullet": result.bulletPictures.push(parseNumberingBulletPicture(e, xml));
|
|
566
|
+
}
|
|
567
|
+
return result;
|
|
568
|
+
}
|
|
569
|
+
function parseNumbering(elem, xml) {
|
|
570
|
+
let result = {
|
|
571
|
+
id: xml.attr(elem, "numId"),
|
|
572
|
+
overrides: []
|
|
573
|
+
};
|
|
574
|
+
for (let e of xml.elements(elem)) switch (e.localName) {
|
|
575
|
+
case "abstractNumId":
|
|
576
|
+
result.abstractId = xml.attr(e, "val");
|
|
577
|
+
break;
|
|
578
|
+
case "lvlOverride": result.overrides.push(parseNumberingLevelOverrride(e, xml));
|
|
579
|
+
}
|
|
580
|
+
return result;
|
|
581
|
+
}
|
|
582
|
+
function parseAbstractNumbering(elem, xml) {
|
|
583
|
+
let result = {
|
|
584
|
+
id: xml.attr(elem, "abstractNumId"),
|
|
585
|
+
levels: []
|
|
586
|
+
};
|
|
587
|
+
for (let e of xml.elements(elem)) switch (e.localName) {
|
|
588
|
+
case "name":
|
|
589
|
+
result.name = xml.attr(e, "val");
|
|
590
|
+
break;
|
|
591
|
+
case "multiLevelType":
|
|
592
|
+
result.multiLevelType = xml.attr(e, "val");
|
|
593
|
+
break;
|
|
594
|
+
case "numStyleLink":
|
|
595
|
+
result.numberingStyleLink = xml.attr(e, "val");
|
|
596
|
+
break;
|
|
597
|
+
case "styleLink":
|
|
598
|
+
result.styleLink = xml.attr(e, "val");
|
|
599
|
+
break;
|
|
600
|
+
case "lvl": result.levels.push(parseNumberingLevel(e, xml));
|
|
601
|
+
}
|
|
602
|
+
return result;
|
|
603
|
+
}
|
|
604
|
+
function parseNumberingLevel(elem, xml) {
|
|
605
|
+
let result = { level: xml.intAttr(elem, "ilvl") };
|
|
606
|
+
for (let e of xml.elements(elem)) switch (e.localName) {
|
|
607
|
+
case "start":
|
|
608
|
+
result.start = xml.attr(e, "val");
|
|
609
|
+
break;
|
|
610
|
+
case "lvlRestart":
|
|
611
|
+
result.restart = xml.intAttr(e, "val");
|
|
612
|
+
break;
|
|
613
|
+
case "numFmt":
|
|
614
|
+
result.format = xml.attr(e, "val");
|
|
615
|
+
break;
|
|
616
|
+
case "lvlText":
|
|
617
|
+
result.text = xml.attr(e, "val");
|
|
618
|
+
break;
|
|
619
|
+
case "lvlJc":
|
|
620
|
+
result.justification = xml.attr(e, "val");
|
|
621
|
+
break;
|
|
622
|
+
case "lvlPicBulletId":
|
|
623
|
+
result.bulletPictureId = xml.attr(e, "val");
|
|
624
|
+
break;
|
|
625
|
+
case "pStyle":
|
|
626
|
+
result.paragraphStyle = xml.attr(e, "val");
|
|
627
|
+
break;
|
|
628
|
+
case "pPr":
|
|
629
|
+
result.paragraphProps = parseParagraphProperties(e, xml);
|
|
630
|
+
break;
|
|
631
|
+
case "rPr": result.runProps = parseRunProperties(e, xml);
|
|
632
|
+
}
|
|
633
|
+
return result;
|
|
634
|
+
}
|
|
635
|
+
function parseNumberingLevelOverrride(elem, xml) {
|
|
636
|
+
let result = { level: xml.intAttr(elem, "ilvl") };
|
|
637
|
+
for (let e of xml.elements(elem)) switch (e.localName) {
|
|
638
|
+
case "startOverride":
|
|
639
|
+
result.start = xml.intAttr(e, "val");
|
|
640
|
+
break;
|
|
641
|
+
case "lvl": result.numberingLevel = parseNumberingLevel(e, xml);
|
|
642
|
+
}
|
|
643
|
+
return result;
|
|
644
|
+
}
|
|
645
|
+
function parseNumberingBulletPicture(elem, xml) {
|
|
646
|
+
var pict = xml.element(elem, "pict");
|
|
647
|
+
var shape = pict && xml.element(pict, "shape");
|
|
648
|
+
var imagedata = shape && xml.element(shape, "imagedata");
|
|
649
|
+
return imagedata ? {
|
|
650
|
+
id: xml.attr(elem, "numPicBulletId"),
|
|
651
|
+
referenceId: xml.attr(imagedata, "id"),
|
|
652
|
+
style: xml.attr(shape, "style")
|
|
653
|
+
} : null;
|
|
654
|
+
}
|
|
655
|
+
//#endregion
|
|
656
|
+
//#region src/numbering/numbering-part.ts
|
|
657
|
+
var NumberingPart = class extends Part {
|
|
658
|
+
constructor(pkg, path, parser) {
|
|
659
|
+
super(pkg, path);
|
|
660
|
+
this._documentParser = parser;
|
|
661
|
+
}
|
|
662
|
+
parseXml(root) {
|
|
663
|
+
Object.assign(this, parseNumberingPart(root, this._package.xmlParser));
|
|
664
|
+
this.domNumberings = this._documentParser.parseNumberingFile(root);
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
//#endregion
|
|
668
|
+
//#region src/styles/styles-part.ts
|
|
669
|
+
var StylesPart = class extends Part {
|
|
670
|
+
constructor(pkg, path, parser) {
|
|
671
|
+
super(pkg, path);
|
|
672
|
+
this._documentParser = parser;
|
|
673
|
+
}
|
|
674
|
+
parseXml(root) {
|
|
675
|
+
this.styles = this._documentParser.parseStylesFile(root);
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
//#endregion
|
|
679
|
+
//#region src/document/dom.ts
|
|
680
|
+
var DomType = /* @__PURE__ */ function(DomType) {
|
|
681
|
+
DomType["Document"] = "document";
|
|
682
|
+
DomType["Paragraph"] = "paragraph";
|
|
683
|
+
DomType["Run"] = "run";
|
|
684
|
+
DomType["Break"] = "break";
|
|
685
|
+
DomType["NoBreakHyphen"] = "noBreakHyphen";
|
|
686
|
+
DomType["Table"] = "table";
|
|
687
|
+
DomType["Row"] = "row";
|
|
688
|
+
DomType["Cell"] = "cell";
|
|
689
|
+
DomType["Hyperlink"] = "hyperlink";
|
|
690
|
+
DomType["SmartTag"] = "smartTag";
|
|
691
|
+
DomType["Drawing"] = "drawing";
|
|
692
|
+
DomType["Image"] = "image";
|
|
693
|
+
DomType["Text"] = "text";
|
|
694
|
+
DomType["Tab"] = "tab";
|
|
695
|
+
DomType["Symbol"] = "symbol";
|
|
696
|
+
DomType["BookmarkStart"] = "bookmarkStart";
|
|
697
|
+
DomType["BookmarkEnd"] = "bookmarkEnd";
|
|
698
|
+
DomType["Footer"] = "footer";
|
|
699
|
+
DomType["Header"] = "header";
|
|
700
|
+
DomType["FootnoteReference"] = "footnoteReference";
|
|
701
|
+
DomType["EndnoteReference"] = "endnoteReference";
|
|
702
|
+
DomType["Footnote"] = "footnote";
|
|
703
|
+
DomType["Endnote"] = "endnote";
|
|
704
|
+
DomType["SimpleField"] = "simpleField";
|
|
705
|
+
DomType["ComplexField"] = "complexField";
|
|
706
|
+
DomType["Instruction"] = "instruction";
|
|
707
|
+
DomType["VmlPicture"] = "vmlPicture";
|
|
708
|
+
DomType["MmlMath"] = "mmlMath";
|
|
709
|
+
DomType["MmlMathParagraph"] = "mmlMathParagraph";
|
|
710
|
+
DomType["MmlFraction"] = "mmlFraction";
|
|
711
|
+
DomType["MmlFunction"] = "mmlFunction";
|
|
712
|
+
DomType["MmlFunctionName"] = "mmlFunctionName";
|
|
713
|
+
DomType["MmlNumerator"] = "mmlNumerator";
|
|
714
|
+
DomType["MmlDenominator"] = "mmlDenominator";
|
|
715
|
+
DomType["MmlRadical"] = "mmlRadical";
|
|
716
|
+
DomType["MmlBase"] = "mmlBase";
|
|
717
|
+
DomType["MmlDegree"] = "mmlDegree";
|
|
718
|
+
DomType["MmlSuperscript"] = "mmlSuperscript";
|
|
719
|
+
DomType["MmlSubscript"] = "mmlSubscript";
|
|
720
|
+
DomType["MmlPreSubSuper"] = "mmlPreSubSuper";
|
|
721
|
+
DomType["MmlSubArgument"] = "mmlSubArgument";
|
|
722
|
+
DomType["MmlSuperArgument"] = "mmlSuperArgument";
|
|
723
|
+
DomType["MmlNary"] = "mmlNary";
|
|
724
|
+
DomType["MmlDelimiter"] = "mmlDelimiter";
|
|
725
|
+
DomType["MmlRun"] = "mmlRun";
|
|
726
|
+
DomType["MmlEquationArray"] = "mmlEquationArray";
|
|
727
|
+
DomType["MmlLimit"] = "mmlLimit";
|
|
728
|
+
DomType["MmlLimitLower"] = "mmlLimitLower";
|
|
729
|
+
DomType["MmlMatrix"] = "mmlMatrix";
|
|
730
|
+
DomType["MmlMatrixRow"] = "mmlMatrixRow";
|
|
731
|
+
DomType["MmlBox"] = "mmlBox";
|
|
732
|
+
DomType["MmlBar"] = "mmlBar";
|
|
733
|
+
DomType["MmlGroupChar"] = "mmlGroupChar";
|
|
734
|
+
DomType["VmlElement"] = "vmlElement";
|
|
735
|
+
DomType["Inserted"] = "inserted";
|
|
736
|
+
DomType["Deleted"] = "deleted";
|
|
737
|
+
DomType["DeletedText"] = "deletedText";
|
|
738
|
+
DomType["Comment"] = "comment";
|
|
739
|
+
DomType["CommentReference"] = "commentReference";
|
|
740
|
+
DomType["CommentRangeStart"] = "commentRangeStart";
|
|
741
|
+
DomType["CommentRangeEnd"] = "commentRangeEnd";
|
|
742
|
+
DomType["AltChunk"] = "altChunk";
|
|
743
|
+
return DomType;
|
|
744
|
+
}({});
|
|
745
|
+
var OpenXmlElementBase = class {
|
|
746
|
+
constructor() {
|
|
747
|
+
this.children = [];
|
|
748
|
+
this.cssStyle = {};
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
//#endregion
|
|
752
|
+
//#region src/header-footer/elements.ts
|
|
753
|
+
var WmlHeader = class extends OpenXmlElementBase {
|
|
754
|
+
constructor(..._args) {
|
|
755
|
+
super(..._args);
|
|
756
|
+
this.type = DomType.Header;
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
var WmlFooter = class extends OpenXmlElementBase {
|
|
760
|
+
constructor(..._args2) {
|
|
761
|
+
super(..._args2);
|
|
762
|
+
this.type = DomType.Footer;
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
//#endregion
|
|
766
|
+
//#region src/header-footer/parts.ts
|
|
767
|
+
var BaseHeaderFooterPart = class extends Part {
|
|
768
|
+
constructor(pkg, path, parser) {
|
|
769
|
+
super(pkg, path);
|
|
770
|
+
this._documentParser = parser;
|
|
771
|
+
}
|
|
772
|
+
parseXml(root) {
|
|
773
|
+
this.rootElement = this.createRootElement();
|
|
774
|
+
this.rootElement.children = this._documentParser.parseBodyElements(root);
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
var HeaderPart = class extends BaseHeaderFooterPart {
|
|
778
|
+
createRootElement() {
|
|
779
|
+
return new WmlHeader();
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
var FooterPart = class extends BaseHeaderFooterPart {
|
|
783
|
+
createRootElement() {
|
|
784
|
+
return new WmlFooter();
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
//#endregion
|
|
788
|
+
//#region src/document-props/extended-props.ts
|
|
789
|
+
function parseExtendedProps(root, xmlParser) {
|
|
790
|
+
const result = {};
|
|
791
|
+
for (let el of xmlParser.elements(root)) switch (el.localName) {
|
|
792
|
+
case "Template":
|
|
793
|
+
result.template = el.textContent;
|
|
794
|
+
break;
|
|
795
|
+
case "Pages":
|
|
796
|
+
result.pages = safeParseToInt(el.textContent);
|
|
797
|
+
break;
|
|
798
|
+
case "Words":
|
|
799
|
+
result.words = safeParseToInt(el.textContent);
|
|
800
|
+
break;
|
|
801
|
+
case "Characters":
|
|
802
|
+
result.characters = safeParseToInt(el.textContent);
|
|
803
|
+
break;
|
|
804
|
+
case "Application":
|
|
805
|
+
result.application = el.textContent;
|
|
806
|
+
break;
|
|
807
|
+
case "Lines":
|
|
808
|
+
result.lines = safeParseToInt(el.textContent);
|
|
809
|
+
break;
|
|
810
|
+
case "Paragraphs":
|
|
811
|
+
result.paragraphs = safeParseToInt(el.textContent);
|
|
812
|
+
break;
|
|
813
|
+
case "Company":
|
|
814
|
+
result.company = el.textContent;
|
|
815
|
+
break;
|
|
816
|
+
case "AppVersion": result.appVersion = el.textContent;
|
|
817
|
+
}
|
|
818
|
+
return result;
|
|
819
|
+
}
|
|
820
|
+
function safeParseToInt(value) {
|
|
821
|
+
if (typeof value === "undefined") return;
|
|
822
|
+
return parseInt(value);
|
|
823
|
+
}
|
|
824
|
+
//#endregion
|
|
825
|
+
//#region src/document-props/extended-props-part.ts
|
|
826
|
+
var ExtendedPropsPart = class extends Part {
|
|
827
|
+
parseXml(root) {
|
|
828
|
+
this.props = parseExtendedProps(root, this._package.xmlParser);
|
|
829
|
+
}
|
|
830
|
+
};
|
|
831
|
+
//#endregion
|
|
832
|
+
//#region src/document-props/core-props.ts
|
|
833
|
+
function parseCoreProps(root, xmlParser) {
|
|
834
|
+
const result = {};
|
|
835
|
+
for (let el of xmlParser.elements(root)) switch (el.localName) {
|
|
836
|
+
case "title":
|
|
837
|
+
result.title = el.textContent;
|
|
838
|
+
break;
|
|
839
|
+
case "description":
|
|
840
|
+
result.description = el.textContent;
|
|
841
|
+
break;
|
|
842
|
+
case "subject":
|
|
843
|
+
result.subject = el.textContent;
|
|
844
|
+
break;
|
|
845
|
+
case "creator":
|
|
846
|
+
result.creator = el.textContent;
|
|
847
|
+
break;
|
|
848
|
+
case "keywords":
|
|
849
|
+
result.keywords = el.textContent;
|
|
850
|
+
break;
|
|
851
|
+
case "language":
|
|
852
|
+
result.language = el.textContent;
|
|
853
|
+
break;
|
|
854
|
+
case "lastModifiedBy":
|
|
855
|
+
result.lastModifiedBy = el.textContent;
|
|
856
|
+
break;
|
|
857
|
+
case "revision": el.textContent && (result.revision = parseInt(el.textContent));
|
|
858
|
+
}
|
|
859
|
+
return result;
|
|
860
|
+
}
|
|
861
|
+
//#endregion
|
|
862
|
+
//#region src/document-props/core-props-part.ts
|
|
863
|
+
var CorePropsPart = class extends Part {
|
|
864
|
+
parseXml(root) {
|
|
865
|
+
this.props = parseCoreProps(root, this._package.xmlParser);
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
//#endregion
|
|
869
|
+
//#region src/theme/theme.ts
|
|
870
|
+
var DmlTheme = class {};
|
|
871
|
+
function parseTheme(elem, xml) {
|
|
872
|
+
var result = new DmlTheme();
|
|
873
|
+
var themeElements = xml.element(elem, "themeElements");
|
|
874
|
+
for (let el of xml.elements(themeElements)) switch (el.localName) {
|
|
875
|
+
case "clrScheme":
|
|
876
|
+
result.colorScheme = parseColorScheme(el, xml);
|
|
877
|
+
break;
|
|
878
|
+
case "fontScheme": result.fontScheme = parseFontScheme(el, xml);
|
|
879
|
+
}
|
|
880
|
+
return result;
|
|
881
|
+
}
|
|
882
|
+
function parseColorScheme(elem, xml) {
|
|
883
|
+
var result = {
|
|
884
|
+
name: xml.attr(elem, "name"),
|
|
885
|
+
colors: {}
|
|
886
|
+
};
|
|
887
|
+
for (let el of xml.elements(elem)) {
|
|
888
|
+
var srgbClr = xml.element(el, "srgbClr");
|
|
889
|
+
var sysClr = xml.element(el, "sysClr");
|
|
890
|
+
if (srgbClr) result.colors[el.localName] = xml.attr(srgbClr, "val");
|
|
891
|
+
else if (sysClr) result.colors[el.localName] = xml.attr(sysClr, "lastClr");
|
|
892
|
+
}
|
|
893
|
+
return result;
|
|
894
|
+
}
|
|
895
|
+
function parseFontScheme(elem, xml) {
|
|
896
|
+
var result = { name: xml.attr(elem, "name") };
|
|
897
|
+
for (let el of xml.elements(elem)) switch (el.localName) {
|
|
898
|
+
case "majorFont":
|
|
899
|
+
result.majorFont = parseFontInfo(el, xml);
|
|
900
|
+
break;
|
|
901
|
+
case "minorFont": result.minorFont = parseFontInfo(el, xml);
|
|
902
|
+
}
|
|
903
|
+
return result;
|
|
904
|
+
}
|
|
905
|
+
function parseFontInfo(elem, xml) {
|
|
906
|
+
return {
|
|
907
|
+
latinTypeface: xml.elementAttr(elem, "latin", "typeface"),
|
|
908
|
+
eaTypeface: xml.elementAttr(elem, "ea", "typeface"),
|
|
909
|
+
csTypeface: xml.elementAttr(elem, "cs", "typeface")
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
//#endregion
|
|
913
|
+
//#region src/theme/theme-part.ts
|
|
914
|
+
var ThemePart = class extends Part {
|
|
915
|
+
constructor(pkg, path) {
|
|
916
|
+
super(pkg, path);
|
|
917
|
+
}
|
|
918
|
+
parseXml(root) {
|
|
919
|
+
this.theme = parseTheme(root, this._package.xmlParser);
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
//#endregion
|
|
923
|
+
//#region src/notes/elements.ts
|
|
924
|
+
var WmlBaseNote = class {};
|
|
925
|
+
var WmlFootnote = class extends WmlBaseNote {
|
|
926
|
+
constructor(..._args) {
|
|
927
|
+
super(..._args);
|
|
928
|
+
this.type = DomType.Footnote;
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
var WmlEndnote = class extends WmlBaseNote {
|
|
932
|
+
constructor(..._args2) {
|
|
933
|
+
super(..._args2);
|
|
934
|
+
this.type = DomType.Endnote;
|
|
935
|
+
}
|
|
936
|
+
};
|
|
937
|
+
//#endregion
|
|
938
|
+
//#region src/notes/parts.ts
|
|
939
|
+
var BaseNotePart = class extends Part {
|
|
940
|
+
constructor(pkg, path, parser) {
|
|
941
|
+
super(pkg, path);
|
|
942
|
+
this._documentParser = parser;
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
var FootnotesPart = class extends BaseNotePart {
|
|
946
|
+
constructor(pkg, path, parser) {
|
|
947
|
+
super(pkg, path, parser);
|
|
948
|
+
}
|
|
949
|
+
parseXml(root) {
|
|
950
|
+
this.notes = this._documentParser.parseNotes(root, "footnote", WmlFootnote);
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
var EndnotesPart = class extends BaseNotePart {
|
|
954
|
+
constructor(pkg, path, parser) {
|
|
955
|
+
super(pkg, path, parser);
|
|
956
|
+
}
|
|
957
|
+
parseXml(root) {
|
|
958
|
+
this.notes = this._documentParser.parseNotes(root, "endnote", WmlEndnote);
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
//#endregion
|
|
962
|
+
//#region src/settings/settings.ts
|
|
963
|
+
function parseSettings(elem, xml) {
|
|
964
|
+
var result = {};
|
|
965
|
+
for (let el of xml.elements(elem)) switch (el.localName) {
|
|
966
|
+
case "defaultTabStop":
|
|
967
|
+
result.defaultTabStop = xml.lengthAttr(el, "val");
|
|
968
|
+
break;
|
|
969
|
+
case "footnotePr":
|
|
970
|
+
result.footnoteProps = parseNoteProperties(el, xml);
|
|
971
|
+
break;
|
|
972
|
+
case "endnotePr":
|
|
973
|
+
result.endnoteProps = parseNoteProperties(el, xml);
|
|
974
|
+
break;
|
|
975
|
+
case "autoHyphenation":
|
|
976
|
+
result.autoHyphenation = xml.boolAttr(el, "val");
|
|
977
|
+
break;
|
|
978
|
+
case "compat": for (let c of xml.elements(el)) if (c.localName == "compatSetting" && xml.attr(c, "name") == "compatibilityMode" && (xml.attr(c, "uri") ?? "").includes("schemas.microsoft.com/office/word")) result.compatMode = xml.intAttr(c, "val", null);
|
|
979
|
+
}
|
|
980
|
+
return result;
|
|
981
|
+
}
|
|
982
|
+
function parseNoteProperties(elem, xml) {
|
|
983
|
+
var result = { defaultNoteIds: [] };
|
|
984
|
+
for (let el of xml.elements(elem)) switch (el.localName) {
|
|
985
|
+
case "numFmt":
|
|
986
|
+
result.nummeringFormat = xml.attr(el, "val");
|
|
987
|
+
break;
|
|
988
|
+
case "footnote":
|
|
989
|
+
case "endnote": result.defaultNoteIds.push(xml.attr(el, "id"));
|
|
990
|
+
}
|
|
991
|
+
return result;
|
|
992
|
+
}
|
|
993
|
+
//#endregion
|
|
994
|
+
//#region src/settings/settings-part.ts
|
|
995
|
+
var SettingsPart = class extends Part {
|
|
996
|
+
constructor(pkg, path) {
|
|
997
|
+
super(pkg, path);
|
|
998
|
+
}
|
|
999
|
+
parseXml(root) {
|
|
1000
|
+
this.settings = parseSettings(root, this._package.xmlParser);
|
|
1001
|
+
}
|
|
1002
|
+
};
|
|
1003
|
+
//#endregion
|
|
1004
|
+
//#region src/document-props/custom-props.ts
|
|
1005
|
+
function parseCustomProps(root, xml) {
|
|
1006
|
+
return xml.elements(root, "property").map((e) => {
|
|
1007
|
+
const firstChild = e.firstChild;
|
|
1008
|
+
return {
|
|
1009
|
+
formatId: xml.attr(e, "fmtid"),
|
|
1010
|
+
name: xml.attr(e, "name"),
|
|
1011
|
+
type: firstChild.nodeName,
|
|
1012
|
+
value: firstChild.textContent
|
|
1013
|
+
};
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
//#endregion
|
|
1017
|
+
//#region src/document-props/custom-props-part.ts
|
|
1018
|
+
var CustomPropsPart = class extends Part {
|
|
1019
|
+
parseXml(root) {
|
|
1020
|
+
this.props = parseCustomProps(root, this._package.xmlParser);
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
//#endregion
|
|
1024
|
+
//#region src/comments/comments-part.ts
|
|
1025
|
+
var CommentsPart = class extends Part {
|
|
1026
|
+
constructor(pkg, path, parser) {
|
|
1027
|
+
super(pkg, path);
|
|
1028
|
+
this._documentParser = parser;
|
|
1029
|
+
}
|
|
1030
|
+
parseXml(root) {
|
|
1031
|
+
this.comments = this._documentParser.parseComments(root);
|
|
1032
|
+
this.commentMap = keyBy(this.comments, (x) => x.id);
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1035
|
+
//#endregion
|
|
1036
|
+
//#region src/comments/comments-extended-part.ts
|
|
1037
|
+
var CommentsExtendedPart = class extends Part {
|
|
1038
|
+
constructor(pkg, path) {
|
|
1039
|
+
super(pkg, path);
|
|
1040
|
+
this.comments = [];
|
|
1041
|
+
}
|
|
1042
|
+
parseXml(root) {
|
|
1043
|
+
const xml = this._package.xmlParser;
|
|
1044
|
+
for (let el of xml.elements(root, "commentEx")) this.comments.push({
|
|
1045
|
+
paraId: xml.attr(el, "paraId"),
|
|
1046
|
+
paraIdParent: xml.attr(el, "paraIdParent"),
|
|
1047
|
+
done: xml.boolAttr(el, "done")
|
|
1048
|
+
});
|
|
1049
|
+
this.commentMap = keyBy(this.comments, (x) => x.paraId);
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
//#endregion
|
|
1053
|
+
//#region src/word-document.ts
|
|
1054
|
+
var topLevelRels = [
|
|
1055
|
+
{
|
|
1056
|
+
type: RelationshipTypes.OfficeDocument,
|
|
1057
|
+
target: "word/document.xml"
|
|
1058
|
+
},
|
|
1059
|
+
{
|
|
1060
|
+
type: RelationshipTypes.ExtendedProperties,
|
|
1061
|
+
target: "docProps/app.xml"
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
type: RelationshipTypes.CoreProperties,
|
|
1065
|
+
target: "docProps/core.xml"
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
type: RelationshipTypes.CustomProperties,
|
|
1069
|
+
target: "docProps/custom.xml"
|
|
1070
|
+
}
|
|
1071
|
+
];
|
|
1072
|
+
var WordDocument = class WordDocument {
|
|
1073
|
+
constructor() {
|
|
1074
|
+
this.parts = [];
|
|
1075
|
+
this.partsMap = {};
|
|
1076
|
+
this.contentTypes = [];
|
|
1077
|
+
}
|
|
1078
|
+
static async load(blob, parser, options) {
|
|
1079
|
+
var d = new WordDocument();
|
|
1080
|
+
d._options = options;
|
|
1081
|
+
d._parser = parser;
|
|
1082
|
+
d._package = await OpenXmlPackage.load(blob, options);
|
|
1083
|
+
d.rels = await d._package.loadRelationships();
|
|
1084
|
+
d.contentTypes = await d._package.loadContentTypes();
|
|
1085
|
+
await Promise.all(topLevelRels.map((rel) => {
|
|
1086
|
+
const r = d.rels.find((x) => x.type === rel.type) ?? rel;
|
|
1087
|
+
return d.loadRelationshipPart(r.target, r.type);
|
|
1088
|
+
}));
|
|
1089
|
+
return d;
|
|
1090
|
+
}
|
|
1091
|
+
save(type = "blob") {
|
|
1092
|
+
return this._package.save(type);
|
|
1093
|
+
}
|
|
1094
|
+
async loadRelationshipPart(path, type) {
|
|
1095
|
+
if (this.partsMap[path]) return this.partsMap[path];
|
|
1096
|
+
if (!this._package.get(path)) return null;
|
|
1097
|
+
let part = null;
|
|
1098
|
+
switch (type) {
|
|
1099
|
+
case RelationshipTypes.OfficeDocument:
|
|
1100
|
+
this.documentPart = part = new DocumentPart(this._package, path, this._parser);
|
|
1101
|
+
break;
|
|
1102
|
+
case RelationshipTypes.FontTable:
|
|
1103
|
+
this.fontTablePart = part = new FontTablePart(this._package, path);
|
|
1104
|
+
break;
|
|
1105
|
+
case RelationshipTypes.Numbering:
|
|
1106
|
+
this.numberingPart = part = new NumberingPart(this._package, path, this._parser);
|
|
1107
|
+
break;
|
|
1108
|
+
case RelationshipTypes.Styles:
|
|
1109
|
+
this.stylesPart = part = new StylesPart(this._package, path, this._parser);
|
|
1110
|
+
break;
|
|
1111
|
+
case RelationshipTypes.Theme:
|
|
1112
|
+
this.themePart = part = new ThemePart(this._package, path);
|
|
1113
|
+
break;
|
|
1114
|
+
case RelationshipTypes.Footnotes:
|
|
1115
|
+
this.footnotesPart = part = new FootnotesPart(this._package, path, this._parser);
|
|
1116
|
+
break;
|
|
1117
|
+
case RelationshipTypes.Endnotes:
|
|
1118
|
+
this.endnotesPart = part = new EndnotesPart(this._package, path, this._parser);
|
|
1119
|
+
break;
|
|
1120
|
+
case RelationshipTypes.Footer:
|
|
1121
|
+
part = new FooterPart(this._package, path, this._parser);
|
|
1122
|
+
break;
|
|
1123
|
+
case RelationshipTypes.Header:
|
|
1124
|
+
part = new HeaderPart(this._package, path, this._parser);
|
|
1125
|
+
break;
|
|
1126
|
+
case RelationshipTypes.CoreProperties:
|
|
1127
|
+
this.corePropsPart = part = new CorePropsPart(this._package, path);
|
|
1128
|
+
break;
|
|
1129
|
+
case RelationshipTypes.ExtendedProperties:
|
|
1130
|
+
this.extendedPropsPart = part = new ExtendedPropsPart(this._package, path);
|
|
1131
|
+
break;
|
|
1132
|
+
case RelationshipTypes.CustomProperties:
|
|
1133
|
+
part = new CustomPropsPart(this._package, path);
|
|
1134
|
+
break;
|
|
1135
|
+
case RelationshipTypes.Settings:
|
|
1136
|
+
this.settingsPart = part = new SettingsPart(this._package, path);
|
|
1137
|
+
break;
|
|
1138
|
+
case RelationshipTypes.Comments:
|
|
1139
|
+
this.commentsPart = part = new CommentsPart(this._package, path, this._parser);
|
|
1140
|
+
break;
|
|
1141
|
+
case RelationshipTypes.CommentsExtended: this.commentsExtendedPart = part = new CommentsExtendedPart(this._package, path);
|
|
1142
|
+
}
|
|
1143
|
+
if (part == null) return Promise.resolve(null);
|
|
1144
|
+
this.partsMap[path] = part;
|
|
1145
|
+
this.parts.push(part);
|
|
1146
|
+
await part.load();
|
|
1147
|
+
if (part.rels?.length > 0) {
|
|
1148
|
+
const [folder] = splitPath(part.path);
|
|
1149
|
+
await Promise.all(part.rels.map((rel) => this.loadRelationshipPart(resolvePath(rel.target, folder), rel.type)));
|
|
1150
|
+
}
|
|
1151
|
+
return part;
|
|
1152
|
+
}
|
|
1153
|
+
async loadDocumentImage(id, part) {
|
|
1154
|
+
const path = this.getPathById(part ?? this.documentPart, id);
|
|
1155
|
+
return path ? this.blobToURL(await this._package.load(path, "blob"), path) : null;
|
|
1156
|
+
}
|
|
1157
|
+
async loadNumberingImage(id) {
|
|
1158
|
+
const path = this.getPathById(this.numberingPart, id);
|
|
1159
|
+
return path ? this.blobToURL(await this._package.load(path, "blob"), path) : null;
|
|
1160
|
+
}
|
|
1161
|
+
async loadFont(id, key) {
|
|
1162
|
+
const path = this.getPathById(this.fontTablePart, id);
|
|
1163
|
+
if (!path) return null;
|
|
1164
|
+
const x = await this._package.load(path, "uint8array");
|
|
1165
|
+
return x ? this.blobToURL(new Blob([deobfuscate(x, key)]), path) : x;
|
|
1166
|
+
}
|
|
1167
|
+
async loadAltChunk(id, part) {
|
|
1168
|
+
const path = this.getPathById(part ?? this.documentPart, id);
|
|
1169
|
+
return path ? this._package.load(path, "string") : Promise.resolve(null);
|
|
1170
|
+
}
|
|
1171
|
+
blobToURL(blob, path) {
|
|
1172
|
+
if (!blob) return null;
|
|
1173
|
+
if (path) {
|
|
1174
|
+
const ct = this.contentTypes.find((x) => x.partName === path || x.extension && path.endsWith(`.${x.extension}`));
|
|
1175
|
+
blob = ct ? new Blob([blob], { type: ct.contentType }) : blob;
|
|
1176
|
+
}
|
|
1177
|
+
if (this._options.useBase64URL) return blobToBase64(blob);
|
|
1178
|
+
return URL.createObjectURL(blob);
|
|
1179
|
+
}
|
|
1180
|
+
findPartByRelId(id, basePart = null) {
|
|
1181
|
+
var rel = (basePart.rels ?? this.rels).find((r) => r.id == id);
|
|
1182
|
+
const folder = basePart ? splitPath(basePart.path)[0] : "";
|
|
1183
|
+
return rel ? this.partsMap[resolvePath(rel.target, folder)] : null;
|
|
1184
|
+
}
|
|
1185
|
+
getPathById(part, id) {
|
|
1186
|
+
const rel = part.rels.find((x) => x.id == id);
|
|
1187
|
+
const [folder] = splitPath(part.path);
|
|
1188
|
+
return rel ? resolvePath(rel.target, folder) : null;
|
|
1189
|
+
}
|
|
1190
|
+
};
|
|
1191
|
+
function deobfuscate(data, guidKey) {
|
|
1192
|
+
const len = 16;
|
|
1193
|
+
const trimmed = guidKey.replace(/{|}|-/g, "");
|
|
1194
|
+
const numbers = new Array(len);
|
|
1195
|
+
for (let i = 0; i < len; i++) numbers[len - i - 1] = parseInt(trimmed.substring(i * 2, i * 2 + 2), 16);
|
|
1196
|
+
for (let i = 0; i < 32; i++) data[i] = data[i] ^ numbers[i % len];
|
|
1197
|
+
return data;
|
|
1198
|
+
}
|
|
1199
|
+
//#endregion
|
|
1200
|
+
//#region src/document/bookmarks.ts
|
|
1201
|
+
function parseBookmarkStart(elem, xml) {
|
|
1202
|
+
return {
|
|
1203
|
+
type: DomType.BookmarkStart,
|
|
1204
|
+
id: xml.attr(elem, "id"),
|
|
1205
|
+
name: xml.attr(elem, "name"),
|
|
1206
|
+
colFirst: xml.intAttr(elem, "colFirst"),
|
|
1207
|
+
colLast: xml.intAttr(elem, "colLast")
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
function parseBookmarkEnd(elem, xml) {
|
|
1211
|
+
return {
|
|
1212
|
+
type: DomType.BookmarkEnd,
|
|
1213
|
+
id: xml.attr(elem, "id")
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
//#endregion
|
|
1217
|
+
//#region src/vml/vml.ts
|
|
1218
|
+
var VmlElement = class extends OpenXmlElementBase {
|
|
1219
|
+
constructor(..._args) {
|
|
1220
|
+
super(..._args);
|
|
1221
|
+
this.type = DomType.VmlElement;
|
|
1222
|
+
this.attrs = {};
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1225
|
+
function parseVmlElement(elem, parser) {
|
|
1226
|
+
var result = new VmlElement();
|
|
1227
|
+
switch (elem.localName) {
|
|
1228
|
+
case "rect":
|
|
1229
|
+
result.tagName = "rect";
|
|
1230
|
+
Object.assign(result.attrs, {
|
|
1231
|
+
width: "100%",
|
|
1232
|
+
height: "100%"
|
|
1233
|
+
});
|
|
1234
|
+
break;
|
|
1235
|
+
case "oval":
|
|
1236
|
+
result.tagName = "ellipse";
|
|
1237
|
+
Object.assign(result.attrs, {
|
|
1238
|
+
cx: "50%",
|
|
1239
|
+
cy: "50%",
|
|
1240
|
+
rx: "50%",
|
|
1241
|
+
ry: "50%"
|
|
1242
|
+
});
|
|
1243
|
+
break;
|
|
1244
|
+
case "line":
|
|
1245
|
+
result.tagName = "line";
|
|
1246
|
+
break;
|
|
1247
|
+
case "shape":
|
|
1248
|
+
result.tagName = "g";
|
|
1249
|
+
break;
|
|
1250
|
+
case "textbox":
|
|
1251
|
+
result.tagName = "foreignObject";
|
|
1252
|
+
Object.assign(result.attrs, {
|
|
1253
|
+
width: "100%",
|
|
1254
|
+
height: "100%"
|
|
1255
|
+
});
|
|
1256
|
+
break;
|
|
1257
|
+
default: return null;
|
|
1258
|
+
}
|
|
1259
|
+
for (const at of globalXmlParser.attrs(elem)) switch (at.localName) {
|
|
1260
|
+
case "style":
|
|
1261
|
+
result.cssStyleText = at.value;
|
|
1262
|
+
break;
|
|
1263
|
+
case "fillcolor":
|
|
1264
|
+
result.attrs.fill = at.value;
|
|
1265
|
+
break;
|
|
1266
|
+
case "from":
|
|
1267
|
+
const [x1, y1] = parsePoint(at.value);
|
|
1268
|
+
Object.assign(result.attrs, {
|
|
1269
|
+
x1,
|
|
1270
|
+
y1
|
|
1271
|
+
});
|
|
1272
|
+
break;
|
|
1273
|
+
case "to":
|
|
1274
|
+
const [x2, y2] = parsePoint(at.value);
|
|
1275
|
+
Object.assign(result.attrs, {
|
|
1276
|
+
x2,
|
|
1277
|
+
y2
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
for (const el of globalXmlParser.elements(elem)) switch (el.localName) {
|
|
1281
|
+
case "stroke":
|
|
1282
|
+
Object.assign(result.attrs, parseStroke(el));
|
|
1283
|
+
break;
|
|
1284
|
+
case "fill":
|
|
1285
|
+
Object.assign(result.attrs, parseFill(el));
|
|
1286
|
+
break;
|
|
1287
|
+
case "imagedata":
|
|
1288
|
+
result.tagName = "image";
|
|
1289
|
+
Object.assign(result.attrs, {
|
|
1290
|
+
width: "100%",
|
|
1291
|
+
height: "100%"
|
|
1292
|
+
});
|
|
1293
|
+
result.imageHref = {
|
|
1294
|
+
id: globalXmlParser.attr(el, "id"),
|
|
1295
|
+
title: globalXmlParser.attr(el, "title")
|
|
1296
|
+
};
|
|
1297
|
+
break;
|
|
1298
|
+
case "txbxContent":
|
|
1299
|
+
result.children.push(...parser.parseBodyElements(el));
|
|
1300
|
+
break;
|
|
1301
|
+
default:
|
|
1302
|
+
const child = parseVmlElement(el, parser);
|
|
1303
|
+
child && result.children.push(child);
|
|
1304
|
+
}
|
|
1305
|
+
return result;
|
|
1306
|
+
}
|
|
1307
|
+
function parseStroke(el) {
|
|
1308
|
+
return {
|
|
1309
|
+
"stroke": globalXmlParser.attr(el, "color"),
|
|
1310
|
+
"stroke-width": globalXmlParser.lengthAttr(el, "weight", LengthUsage.Emu) ?? "1px"
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
function parseFill(el) {
|
|
1314
|
+
return {};
|
|
1315
|
+
}
|
|
1316
|
+
function parsePoint(val) {
|
|
1317
|
+
return val.split(",");
|
|
1318
|
+
}
|
|
1319
|
+
//#endregion
|
|
1320
|
+
//#region src/comments/elements.ts
|
|
1321
|
+
var WmlComment = class extends OpenXmlElementBase {
|
|
1322
|
+
constructor(..._args) {
|
|
1323
|
+
super(..._args);
|
|
1324
|
+
this.type = DomType.Comment;
|
|
1325
|
+
}
|
|
1326
|
+
};
|
|
1327
|
+
var WmlCommentReference = class extends OpenXmlElementBase {
|
|
1328
|
+
constructor(id) {
|
|
1329
|
+
super();
|
|
1330
|
+
this.id = id;
|
|
1331
|
+
this.type = DomType.CommentReference;
|
|
1332
|
+
}
|
|
1333
|
+
};
|
|
1334
|
+
var WmlCommentRangeStart = class extends OpenXmlElementBase {
|
|
1335
|
+
constructor(id) {
|
|
1336
|
+
super();
|
|
1337
|
+
this.id = id;
|
|
1338
|
+
this.type = DomType.CommentRangeStart;
|
|
1339
|
+
}
|
|
1340
|
+
};
|
|
1341
|
+
var WmlCommentRangeEnd = class extends OpenXmlElementBase {
|
|
1342
|
+
constructor(id) {
|
|
1343
|
+
super();
|
|
1344
|
+
this.id = id;
|
|
1345
|
+
this.type = DomType.CommentRangeEnd;
|
|
1346
|
+
}
|
|
1347
|
+
};
|
|
1348
|
+
//#endregion
|
|
1349
|
+
//#region src/document-parser.ts
|
|
1350
|
+
var autos = {
|
|
1351
|
+
shd: "inherit",
|
|
1352
|
+
color: "black",
|
|
1353
|
+
borderColor: "black",
|
|
1354
|
+
highlight: "transparent"
|
|
1355
|
+
};
|
|
1356
|
+
var supportedNamespaceURIs = [];
|
|
1357
|
+
var mmlTagMap = {
|
|
1358
|
+
"oMath": DomType.MmlMath,
|
|
1359
|
+
"oMathPara": DomType.MmlMathParagraph,
|
|
1360
|
+
"f": DomType.MmlFraction,
|
|
1361
|
+
"func": DomType.MmlFunction,
|
|
1362
|
+
"fName": DomType.MmlFunctionName,
|
|
1363
|
+
"num": DomType.MmlNumerator,
|
|
1364
|
+
"den": DomType.MmlDenominator,
|
|
1365
|
+
"rad": DomType.MmlRadical,
|
|
1366
|
+
"deg": DomType.MmlDegree,
|
|
1367
|
+
"e": DomType.MmlBase,
|
|
1368
|
+
"sSup": DomType.MmlSuperscript,
|
|
1369
|
+
"sSub": DomType.MmlSubscript,
|
|
1370
|
+
"sPre": DomType.MmlPreSubSuper,
|
|
1371
|
+
"sup": DomType.MmlSuperArgument,
|
|
1372
|
+
"sub": DomType.MmlSubArgument,
|
|
1373
|
+
"d": DomType.MmlDelimiter,
|
|
1374
|
+
"nary": DomType.MmlNary,
|
|
1375
|
+
"eqArr": DomType.MmlEquationArray,
|
|
1376
|
+
"lim": DomType.MmlLimit,
|
|
1377
|
+
"limLow": DomType.MmlLimitLower,
|
|
1378
|
+
"m": DomType.MmlMatrix,
|
|
1379
|
+
"mr": DomType.MmlMatrixRow,
|
|
1380
|
+
"box": DomType.MmlBox,
|
|
1381
|
+
"bar": DomType.MmlBar,
|
|
1382
|
+
"groupChr": DomType.MmlGroupChar
|
|
1383
|
+
};
|
|
1384
|
+
var DocumentParser = class {
|
|
1385
|
+
constructor(options) {
|
|
1386
|
+
this.options = {
|
|
1387
|
+
ignoreWidth: false,
|
|
1388
|
+
debug: false,
|
|
1389
|
+
...options
|
|
1390
|
+
};
|
|
1391
|
+
}
|
|
1392
|
+
parseNotes(xmlDoc, elemName, elemClass) {
|
|
1393
|
+
var result = [];
|
|
1394
|
+
for (let el of globalXmlParser.elements(xmlDoc, elemName)) {
|
|
1395
|
+
const node = new elemClass();
|
|
1396
|
+
node.id = globalXmlParser.attr(el, "id");
|
|
1397
|
+
node.noteType = globalXmlParser.attr(el, "type");
|
|
1398
|
+
node.children = this.parseBodyElements(el);
|
|
1399
|
+
result.push(node);
|
|
1400
|
+
}
|
|
1401
|
+
return result;
|
|
1402
|
+
}
|
|
1403
|
+
parseComments(xmlDoc) {
|
|
1404
|
+
var result = [];
|
|
1405
|
+
for (let el of globalXmlParser.elements(xmlDoc, "comment")) {
|
|
1406
|
+
const item = new WmlComment();
|
|
1407
|
+
item.id = globalXmlParser.attr(el, "id");
|
|
1408
|
+
item.author = globalXmlParser.attr(el, "author");
|
|
1409
|
+
item.initials = globalXmlParser.attr(el, "initials");
|
|
1410
|
+
item.date = globalXmlParser.attr(el, "date");
|
|
1411
|
+
item.children = this.parseBodyElements(el);
|
|
1412
|
+
result.push(item);
|
|
1413
|
+
}
|
|
1414
|
+
return result;
|
|
1415
|
+
}
|
|
1416
|
+
parseDocumentFile(xmlDoc) {
|
|
1417
|
+
var xbody = globalXmlParser.element(xmlDoc, "body");
|
|
1418
|
+
var background = globalXmlParser.element(xmlDoc, "background");
|
|
1419
|
+
var sectPr = globalXmlParser.element(xbody, "sectPr");
|
|
1420
|
+
return {
|
|
1421
|
+
type: DomType.Document,
|
|
1422
|
+
children: this.parseBodyElements(xbody),
|
|
1423
|
+
props: sectPr ? parseSectionProperties(sectPr, globalXmlParser) : {},
|
|
1424
|
+
cssStyle: background ? this.parseBackground(background) : {}
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
parseBackground(elem) {
|
|
1428
|
+
var result = {};
|
|
1429
|
+
var color = xmlUtil.colorAttr(elem, "color");
|
|
1430
|
+
if (color) result["background-color"] = color;
|
|
1431
|
+
return result;
|
|
1432
|
+
}
|
|
1433
|
+
parseBodyElements(element) {
|
|
1434
|
+
var children = [];
|
|
1435
|
+
for (const elem of globalXmlParser.elements(element)) switch (elem.localName) {
|
|
1436
|
+
case "p":
|
|
1437
|
+
children.push(this.parseParagraph(elem));
|
|
1438
|
+
break;
|
|
1439
|
+
case "altChunk":
|
|
1440
|
+
children.push(this.parseAltChunk(elem));
|
|
1441
|
+
break;
|
|
1442
|
+
case "tbl":
|
|
1443
|
+
children.push(this.parseTable(elem));
|
|
1444
|
+
break;
|
|
1445
|
+
case "sdt": children.push(...this.parseSdt(elem, (e) => this.parseBodyElements(e)));
|
|
1446
|
+
}
|
|
1447
|
+
return children;
|
|
1448
|
+
}
|
|
1449
|
+
parseStylesFile(xstyles) {
|
|
1450
|
+
var result = [];
|
|
1451
|
+
for (const n of globalXmlParser.elements(xstyles)) switch (n.localName) {
|
|
1452
|
+
case "style":
|
|
1453
|
+
result.push(this.parseStyle(n));
|
|
1454
|
+
break;
|
|
1455
|
+
case "docDefaults": result.push(this.parseDefaultStyles(n));
|
|
1456
|
+
}
|
|
1457
|
+
return result;
|
|
1458
|
+
}
|
|
1459
|
+
parseDefaultStyles(node) {
|
|
1460
|
+
var result = {
|
|
1461
|
+
id: null,
|
|
1462
|
+
name: null,
|
|
1463
|
+
target: null,
|
|
1464
|
+
basedOn: null,
|
|
1465
|
+
styles: []
|
|
1466
|
+
};
|
|
1467
|
+
for (const c of globalXmlParser.elements(node)) switch (c.localName) {
|
|
1468
|
+
case "rPrDefault":
|
|
1469
|
+
var rPr = globalXmlParser.element(c, "rPr");
|
|
1470
|
+
if (rPr) result.styles.push({
|
|
1471
|
+
target: "span",
|
|
1472
|
+
values: this.parseDefaultProperties(rPr, {})
|
|
1473
|
+
});
|
|
1474
|
+
break;
|
|
1475
|
+
case "pPrDefault":
|
|
1476
|
+
var pPr = globalXmlParser.element(c, "pPr");
|
|
1477
|
+
if (pPr) result.styles.push({
|
|
1478
|
+
target: "p",
|
|
1479
|
+
values: this.parseDefaultProperties(pPr, {})
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
return result;
|
|
1483
|
+
}
|
|
1484
|
+
parseStyle(node) {
|
|
1485
|
+
var result = {
|
|
1486
|
+
id: globalXmlParser.attr(node, "styleId"),
|
|
1487
|
+
isDefault: globalXmlParser.boolAttr(node, "default"),
|
|
1488
|
+
name: null,
|
|
1489
|
+
target: null,
|
|
1490
|
+
basedOn: null,
|
|
1491
|
+
styles: [],
|
|
1492
|
+
linked: null
|
|
1493
|
+
};
|
|
1494
|
+
switch (globalXmlParser.attr(node, "type")) {
|
|
1495
|
+
case "paragraph":
|
|
1496
|
+
result.target = "p";
|
|
1497
|
+
break;
|
|
1498
|
+
case "table":
|
|
1499
|
+
result.target = "table";
|
|
1500
|
+
break;
|
|
1501
|
+
case "character": result.target = "span";
|
|
1502
|
+
}
|
|
1503
|
+
for (const n of globalXmlParser.elements(node)) switch (n.localName) {
|
|
1504
|
+
case "basedOn":
|
|
1505
|
+
result.basedOn = globalXmlParser.attr(n, "val");
|
|
1506
|
+
break;
|
|
1507
|
+
case "name":
|
|
1508
|
+
result.name = globalXmlParser.attr(n, "val");
|
|
1509
|
+
break;
|
|
1510
|
+
case "link":
|
|
1511
|
+
result.linked = globalXmlParser.attr(n, "val");
|
|
1512
|
+
break;
|
|
1513
|
+
case "next":
|
|
1514
|
+
result.next = globalXmlParser.attr(n, "val");
|
|
1515
|
+
break;
|
|
1516
|
+
case "aliases":
|
|
1517
|
+
result.aliases = globalXmlParser.attr(n, "val").split(",");
|
|
1518
|
+
break;
|
|
1519
|
+
case "pPr":
|
|
1520
|
+
result.styles.push({
|
|
1521
|
+
target: "p",
|
|
1522
|
+
values: this.parseDefaultProperties(n, {})
|
|
1523
|
+
});
|
|
1524
|
+
result.paragraphProps = parseParagraphProperties(n, globalXmlParser);
|
|
1525
|
+
break;
|
|
1526
|
+
case "rPr":
|
|
1527
|
+
result.styles.push({
|
|
1528
|
+
target: "span",
|
|
1529
|
+
values: this.parseDefaultProperties(n, {})
|
|
1530
|
+
});
|
|
1531
|
+
result.runProps = parseRunProperties(n, globalXmlParser);
|
|
1532
|
+
break;
|
|
1533
|
+
case "tblPr":
|
|
1534
|
+
case "tcPr":
|
|
1535
|
+
result.styles.push({
|
|
1536
|
+
target: "td",
|
|
1537
|
+
values: this.parseDefaultProperties(n, {})
|
|
1538
|
+
});
|
|
1539
|
+
break;
|
|
1540
|
+
case "tblStylePr":
|
|
1541
|
+
for (let s of this.parseTableStyle(n)) result.styles.push(s);
|
|
1542
|
+
break;
|
|
1543
|
+
case "rsid":
|
|
1544
|
+
case "qFormat":
|
|
1545
|
+
case "hidden":
|
|
1546
|
+
case "semiHidden":
|
|
1547
|
+
case "unhideWhenUsed":
|
|
1548
|
+
case "autoRedefine":
|
|
1549
|
+
case "uiPriority": break;
|
|
1550
|
+
default: this.options.debug && console.warn(`DOCX: Unknown style element: ${n.localName}`);
|
|
1551
|
+
}
|
|
1552
|
+
return result;
|
|
1553
|
+
}
|
|
1554
|
+
parseTableStyle(node) {
|
|
1555
|
+
var result = [];
|
|
1556
|
+
var type = globalXmlParser.attr(node, "type");
|
|
1557
|
+
var selector = "";
|
|
1558
|
+
var modificator = "";
|
|
1559
|
+
switch (type) {
|
|
1560
|
+
case "firstRow":
|
|
1561
|
+
modificator = ".first-row";
|
|
1562
|
+
selector = "tr.first-row td";
|
|
1563
|
+
break;
|
|
1564
|
+
case "lastRow":
|
|
1565
|
+
modificator = ".last-row";
|
|
1566
|
+
selector = "tr.last-row td";
|
|
1567
|
+
break;
|
|
1568
|
+
case "firstCol":
|
|
1569
|
+
modificator = ".first-col";
|
|
1570
|
+
selector = "td.first-col";
|
|
1571
|
+
break;
|
|
1572
|
+
case "lastCol":
|
|
1573
|
+
modificator = ".last-col";
|
|
1574
|
+
selector = "td.last-col";
|
|
1575
|
+
break;
|
|
1576
|
+
case "band1Vert":
|
|
1577
|
+
modificator = ":not(.no-vband)";
|
|
1578
|
+
selector = "td.odd-col";
|
|
1579
|
+
break;
|
|
1580
|
+
case "band2Vert":
|
|
1581
|
+
modificator = ":not(.no-vband)";
|
|
1582
|
+
selector = "td.even-col";
|
|
1583
|
+
break;
|
|
1584
|
+
case "band1Horz":
|
|
1585
|
+
modificator = ":not(.no-hband)";
|
|
1586
|
+
selector = "tr.odd-row";
|
|
1587
|
+
break;
|
|
1588
|
+
case "band2Horz":
|
|
1589
|
+
modificator = ":not(.no-hband)";
|
|
1590
|
+
selector = "tr.even-row";
|
|
1591
|
+
break;
|
|
1592
|
+
default: return [];
|
|
1593
|
+
}
|
|
1594
|
+
for (const n of globalXmlParser.elements(node)) switch (n.localName) {
|
|
1595
|
+
case "pPr":
|
|
1596
|
+
result.push({
|
|
1597
|
+
target: `${selector} p`,
|
|
1598
|
+
mod: modificator,
|
|
1599
|
+
values: this.parseDefaultProperties(n, {})
|
|
1600
|
+
});
|
|
1601
|
+
break;
|
|
1602
|
+
case "rPr":
|
|
1603
|
+
result.push({
|
|
1604
|
+
target: `${selector} span`,
|
|
1605
|
+
mod: modificator,
|
|
1606
|
+
values: this.parseDefaultProperties(n, {})
|
|
1607
|
+
});
|
|
1608
|
+
break;
|
|
1609
|
+
case "tblPr":
|
|
1610
|
+
case "tcPr": result.push({
|
|
1611
|
+
target: selector,
|
|
1612
|
+
mod: modificator,
|
|
1613
|
+
values: this.parseDefaultProperties(n, {})
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
return result;
|
|
1617
|
+
}
|
|
1618
|
+
parseNumberingFile(node) {
|
|
1619
|
+
const levels = [];
|
|
1620
|
+
const nums = [];
|
|
1621
|
+
const bullets = [];
|
|
1622
|
+
for (const n of globalXmlParser.elements(node)) switch (n.localName) {
|
|
1623
|
+
case "abstractNum":
|
|
1624
|
+
levels.push(...this.parseAbstractNumbering(n, bullets));
|
|
1625
|
+
break;
|
|
1626
|
+
case "numPicBullet":
|
|
1627
|
+
bullets.push(this.parseNumberingPicBullet(n));
|
|
1628
|
+
break;
|
|
1629
|
+
case "num": nums.push({
|
|
1630
|
+
numId: globalXmlParser.attr(n, "numId"),
|
|
1631
|
+
abstractNumId: globalXmlParser.elementAttr(n, "abstractNumId", "val")
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
return nums.flatMap((x) => levels.filter((lvl) => x.abstractNumId == lvl.id).map((lvl) => ({
|
|
1635
|
+
...lvl,
|
|
1636
|
+
id: x.numId
|
|
1637
|
+
})));
|
|
1638
|
+
}
|
|
1639
|
+
parseNumberingPicBullet(elem) {
|
|
1640
|
+
var pict = globalXmlParser.element(elem, "pict");
|
|
1641
|
+
var shape = pict && globalXmlParser.element(pict, "shape");
|
|
1642
|
+
var imagedata = shape && globalXmlParser.element(shape, "imagedata");
|
|
1643
|
+
return imagedata ? {
|
|
1644
|
+
id: globalXmlParser.intAttr(elem, "numPicBulletId"),
|
|
1645
|
+
src: globalXmlParser.attr(imagedata, "id"),
|
|
1646
|
+
style: globalXmlParser.attr(shape, "style")
|
|
1647
|
+
} : null;
|
|
1648
|
+
}
|
|
1649
|
+
parseAbstractNumbering(node, bullets) {
|
|
1650
|
+
var result = [];
|
|
1651
|
+
var id = globalXmlParser.attr(node, "abstractNumId");
|
|
1652
|
+
for (const n of globalXmlParser.elements(node)) switch (n.localName) {
|
|
1653
|
+
case "lvl": result.push(this.parseNumberingLevel(id, n, bullets));
|
|
1654
|
+
}
|
|
1655
|
+
return result;
|
|
1656
|
+
}
|
|
1657
|
+
parseNumberingLevel(id, node, bullets) {
|
|
1658
|
+
var result = {
|
|
1659
|
+
id,
|
|
1660
|
+
level: globalXmlParser.intAttr(node, "ilvl"),
|
|
1661
|
+
start: 1,
|
|
1662
|
+
pStyleName: void 0,
|
|
1663
|
+
pStyle: {},
|
|
1664
|
+
rStyle: {},
|
|
1665
|
+
suff: "tab"
|
|
1666
|
+
};
|
|
1667
|
+
for (const n of globalXmlParser.elements(node)) switch (n.localName) {
|
|
1668
|
+
case "start":
|
|
1669
|
+
result.start = globalXmlParser.intAttr(n, "val");
|
|
1670
|
+
break;
|
|
1671
|
+
case "pPr":
|
|
1672
|
+
this.parseDefaultProperties(n, result.pStyle);
|
|
1673
|
+
break;
|
|
1674
|
+
case "rPr":
|
|
1675
|
+
this.parseDefaultProperties(n, result.rStyle);
|
|
1676
|
+
break;
|
|
1677
|
+
case "lvlPicBulletId":
|
|
1678
|
+
var bulletId = globalXmlParser.intAttr(n, "val");
|
|
1679
|
+
result.bullet = bullets.find((x) => x?.id == bulletId);
|
|
1680
|
+
break;
|
|
1681
|
+
case "lvlText":
|
|
1682
|
+
result.levelText = globalXmlParser.attr(n, "val");
|
|
1683
|
+
break;
|
|
1684
|
+
case "pStyle":
|
|
1685
|
+
result.pStyleName = globalXmlParser.attr(n, "val");
|
|
1686
|
+
break;
|
|
1687
|
+
case "numFmt":
|
|
1688
|
+
result.format = globalXmlParser.attr(n, "val");
|
|
1689
|
+
break;
|
|
1690
|
+
case "suff": result.suff = globalXmlParser.attr(n, "val");
|
|
1691
|
+
}
|
|
1692
|
+
return result;
|
|
1693
|
+
}
|
|
1694
|
+
parseSdt(node, parser) {
|
|
1695
|
+
const sdtContent = globalXmlParser.element(node, "sdtContent");
|
|
1696
|
+
return sdtContent ? parser(sdtContent) : [];
|
|
1697
|
+
}
|
|
1698
|
+
parseChange(type, node, parentParser) {
|
|
1699
|
+
return {
|
|
1700
|
+
type,
|
|
1701
|
+
children: parentParser(node)?.children ?? [],
|
|
1702
|
+
id: globalXmlParser.attr(node, "id"),
|
|
1703
|
+
author: globalXmlParser.attr(node, "author"),
|
|
1704
|
+
date: globalXmlParser.attr(node, "date")
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
parseAltChunk(node) {
|
|
1708
|
+
return {
|
|
1709
|
+
type: DomType.AltChunk,
|
|
1710
|
+
children: [],
|
|
1711
|
+
id: globalXmlParser.attr(node, "id")
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
parseParagraph(node) {
|
|
1715
|
+
var result = {
|
|
1716
|
+
type: DomType.Paragraph,
|
|
1717
|
+
children: []
|
|
1718
|
+
};
|
|
1719
|
+
for (let el of globalXmlParser.elements(node)) switch (el.localName) {
|
|
1720
|
+
case "pPr":
|
|
1721
|
+
this.parseParagraphProperties(el, result);
|
|
1722
|
+
break;
|
|
1723
|
+
case "r":
|
|
1724
|
+
result.children.push(this.parseRun(el, result));
|
|
1725
|
+
break;
|
|
1726
|
+
case "hyperlink":
|
|
1727
|
+
result.children.push(this.parseHyperlink(el, result));
|
|
1728
|
+
break;
|
|
1729
|
+
case "smartTag":
|
|
1730
|
+
result.children.push(this.parseSmartTag(el, result));
|
|
1731
|
+
break;
|
|
1732
|
+
case "bookmarkStart":
|
|
1733
|
+
result.children.push(parseBookmarkStart(el, globalXmlParser));
|
|
1734
|
+
break;
|
|
1735
|
+
case "bookmarkEnd":
|
|
1736
|
+
result.children.push(parseBookmarkEnd(el, globalXmlParser));
|
|
1737
|
+
break;
|
|
1738
|
+
case "commentRangeStart":
|
|
1739
|
+
result.children.push(new WmlCommentRangeStart(globalXmlParser.attr(el, "id")));
|
|
1740
|
+
break;
|
|
1741
|
+
case "commentRangeEnd":
|
|
1742
|
+
result.children.push(new WmlCommentRangeEnd(globalXmlParser.attr(el, "id")));
|
|
1743
|
+
break;
|
|
1744
|
+
case "oMath":
|
|
1745
|
+
case "oMathPara":
|
|
1746
|
+
result.children.push(this.parseMathElement(el));
|
|
1747
|
+
break;
|
|
1748
|
+
case "sdt":
|
|
1749
|
+
result.children.push(...this.parseSdt(el, (e) => this.parseParagraph(e).children));
|
|
1750
|
+
break;
|
|
1751
|
+
case "ins":
|
|
1752
|
+
result.children.push(this.parseChange(DomType.Inserted, el, (e) => this.parseParagraph(e)));
|
|
1753
|
+
break;
|
|
1754
|
+
case "del": result.children.push(this.parseChange(DomType.Deleted, el, (e) => this.parseParagraph(e)));
|
|
1755
|
+
}
|
|
1756
|
+
return result;
|
|
1757
|
+
}
|
|
1758
|
+
parseParagraphProperties(elem, paragraph) {
|
|
1759
|
+
this.parseDefaultProperties(elem, paragraph.cssStyle = {}, null, (c) => {
|
|
1760
|
+
if (parseParagraphProperty(c, paragraph, globalXmlParser)) return true;
|
|
1761
|
+
switch (c.localName) {
|
|
1762
|
+
case "pStyle":
|
|
1763
|
+
paragraph.styleName = globalXmlParser.attr(c, "val");
|
|
1764
|
+
break;
|
|
1765
|
+
case "cnfStyle":
|
|
1766
|
+
paragraph.className = values.classNameOfCnfStyle(c);
|
|
1767
|
+
break;
|
|
1768
|
+
case "framePr":
|
|
1769
|
+
this.parseFrame(c, paragraph);
|
|
1770
|
+
break;
|
|
1771
|
+
case "rPr": break;
|
|
1772
|
+
default: return false;
|
|
1773
|
+
}
|
|
1774
|
+
return true;
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
parseFrame(node, paragraph) {
|
|
1778
|
+
if (globalXmlParser.attr(node, "dropCap") == "drop") paragraph.cssStyle["float"] = "left";
|
|
1779
|
+
}
|
|
1780
|
+
parseHyperlink(node, parent) {
|
|
1781
|
+
var result = {
|
|
1782
|
+
type: DomType.Hyperlink,
|
|
1783
|
+
parent,
|
|
1784
|
+
children: []
|
|
1785
|
+
};
|
|
1786
|
+
result.anchor = globalXmlParser.attr(node, "anchor");
|
|
1787
|
+
result.id = globalXmlParser.attr(node, "id");
|
|
1788
|
+
for (const c of globalXmlParser.elements(node)) switch (c.localName) {
|
|
1789
|
+
case "r": result.children.push(this.parseRun(c, result));
|
|
1790
|
+
}
|
|
1791
|
+
return result;
|
|
1792
|
+
}
|
|
1793
|
+
parseSmartTag(node, parent) {
|
|
1794
|
+
var result = {
|
|
1795
|
+
type: DomType.SmartTag,
|
|
1796
|
+
parent,
|
|
1797
|
+
children: []
|
|
1798
|
+
};
|
|
1799
|
+
var uri = globalXmlParser.attr(node, "uri");
|
|
1800
|
+
var element = globalXmlParser.attr(node, "element");
|
|
1801
|
+
if (uri) result.uri = uri;
|
|
1802
|
+
if (element) result.element = element;
|
|
1803
|
+
for (const c of globalXmlParser.elements(node)) switch (c.localName) {
|
|
1804
|
+
case "r":
|
|
1805
|
+
result.children.push(this.parseRun(c, result));
|
|
1806
|
+
break;
|
|
1807
|
+
case "smartTag": result.children.push(this.parseSmartTag(c, result));
|
|
1808
|
+
}
|
|
1809
|
+
return result;
|
|
1810
|
+
}
|
|
1811
|
+
parseRun(node, parent) {
|
|
1812
|
+
var result = {
|
|
1813
|
+
type: DomType.Run,
|
|
1814
|
+
parent,
|
|
1815
|
+
children: []
|
|
1816
|
+
};
|
|
1817
|
+
for (let c of globalXmlParser.elements(node)) {
|
|
1818
|
+
c = this.checkAlternateContent(c);
|
|
1819
|
+
switch (c.localName) {
|
|
1820
|
+
case "t":
|
|
1821
|
+
result.children.push({
|
|
1822
|
+
type: DomType.Text,
|
|
1823
|
+
text: c.textContent
|
|
1824
|
+
});
|
|
1825
|
+
break;
|
|
1826
|
+
case "delText":
|
|
1827
|
+
result.children.push({
|
|
1828
|
+
type: DomType.DeletedText,
|
|
1829
|
+
text: c.textContent
|
|
1830
|
+
});
|
|
1831
|
+
break;
|
|
1832
|
+
case "commentReference":
|
|
1833
|
+
result.children.push(new WmlCommentReference(globalXmlParser.attr(c, "id")));
|
|
1834
|
+
break;
|
|
1835
|
+
case "fldSimple":
|
|
1836
|
+
result.children.push({
|
|
1837
|
+
type: DomType.SimpleField,
|
|
1838
|
+
instruction: globalXmlParser.attr(c, "instr"),
|
|
1839
|
+
lock: globalXmlParser.boolAttr(c, "lock", false),
|
|
1840
|
+
dirty: globalXmlParser.boolAttr(c, "dirty", false)
|
|
1841
|
+
});
|
|
1842
|
+
break;
|
|
1843
|
+
case "instrText":
|
|
1844
|
+
result.fieldRun = true;
|
|
1845
|
+
result.children.push({
|
|
1846
|
+
type: DomType.Instruction,
|
|
1847
|
+
text: c.textContent
|
|
1848
|
+
});
|
|
1849
|
+
break;
|
|
1850
|
+
case "fldChar":
|
|
1851
|
+
result.fieldRun = true;
|
|
1852
|
+
result.children.push({
|
|
1853
|
+
type: DomType.ComplexField,
|
|
1854
|
+
charType: globalXmlParser.attr(c, "fldCharType"),
|
|
1855
|
+
lock: globalXmlParser.boolAttr(c, "lock", false),
|
|
1856
|
+
dirty: globalXmlParser.boolAttr(c, "dirty", false)
|
|
1857
|
+
});
|
|
1858
|
+
break;
|
|
1859
|
+
case "noBreakHyphen":
|
|
1860
|
+
result.children.push({ type: DomType.NoBreakHyphen });
|
|
1861
|
+
break;
|
|
1862
|
+
case "br":
|
|
1863
|
+
result.children.push({
|
|
1864
|
+
type: DomType.Break,
|
|
1865
|
+
break: globalXmlParser.attr(c, "type") || "textWrapping"
|
|
1866
|
+
});
|
|
1867
|
+
break;
|
|
1868
|
+
case "lastRenderedPageBreak":
|
|
1869
|
+
result.children.push({
|
|
1870
|
+
type: DomType.Break,
|
|
1871
|
+
break: "lastRenderedPageBreak"
|
|
1872
|
+
});
|
|
1873
|
+
break;
|
|
1874
|
+
case "sym":
|
|
1875
|
+
result.children.push({
|
|
1876
|
+
type: DomType.Symbol,
|
|
1877
|
+
font: encloseFontFamily(globalXmlParser.attr(c, "font")),
|
|
1878
|
+
char: globalXmlParser.hexAttr(c, "char")
|
|
1879
|
+
});
|
|
1880
|
+
break;
|
|
1881
|
+
case "tab":
|
|
1882
|
+
result.children.push({ type: DomType.Tab });
|
|
1883
|
+
break;
|
|
1884
|
+
case "footnoteReference":
|
|
1885
|
+
result.children.push({
|
|
1886
|
+
type: DomType.FootnoteReference,
|
|
1887
|
+
id: globalXmlParser.attr(c, "id")
|
|
1888
|
+
});
|
|
1889
|
+
break;
|
|
1890
|
+
case "endnoteReference":
|
|
1891
|
+
result.children.push({
|
|
1892
|
+
type: DomType.EndnoteReference,
|
|
1893
|
+
id: globalXmlParser.attr(c, "id")
|
|
1894
|
+
});
|
|
1895
|
+
break;
|
|
1896
|
+
case "drawing":
|
|
1897
|
+
let d = this.parseDrawing(c);
|
|
1898
|
+
if (d) result.children.push(d);
|
|
1899
|
+
break;
|
|
1900
|
+
case "pict":
|
|
1901
|
+
result.children.push(this.parseVmlPicture(c));
|
|
1902
|
+
break;
|
|
1903
|
+
case "rPr": this.parseRunProperties(c, result);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
return result;
|
|
1907
|
+
}
|
|
1908
|
+
parseMathElement(elem) {
|
|
1909
|
+
const propsTag = `${elem.localName}Pr`;
|
|
1910
|
+
const result = {
|
|
1911
|
+
type: mmlTagMap[elem.localName],
|
|
1912
|
+
children: []
|
|
1913
|
+
};
|
|
1914
|
+
for (const el of globalXmlParser.elements(elem)) if (mmlTagMap[el.localName]) result.children.push(this.parseMathElement(el));
|
|
1915
|
+
else if (el.localName == "r") {
|
|
1916
|
+
var run = this.parseRun(el);
|
|
1917
|
+
run.type = DomType.MmlRun;
|
|
1918
|
+
result.children.push(run);
|
|
1919
|
+
} else if (el.localName == propsTag) result.props = this.parseMathProperies(el);
|
|
1920
|
+
return result;
|
|
1921
|
+
}
|
|
1922
|
+
parseMathProperies(elem) {
|
|
1923
|
+
const result = {};
|
|
1924
|
+
for (const el of globalXmlParser.elements(elem)) switch (el.localName) {
|
|
1925
|
+
case "chr":
|
|
1926
|
+
result.char = globalXmlParser.attr(el, "val");
|
|
1927
|
+
break;
|
|
1928
|
+
case "vertJc":
|
|
1929
|
+
result.verticalJustification = globalXmlParser.attr(el, "val");
|
|
1930
|
+
break;
|
|
1931
|
+
case "pos":
|
|
1932
|
+
result.position = globalXmlParser.attr(el, "val");
|
|
1933
|
+
break;
|
|
1934
|
+
case "degHide":
|
|
1935
|
+
result.hideDegree = globalXmlParser.boolAttr(el, "val");
|
|
1936
|
+
break;
|
|
1937
|
+
case "begChr":
|
|
1938
|
+
result.beginChar = globalXmlParser.attr(el, "val");
|
|
1939
|
+
break;
|
|
1940
|
+
case "endChr": result.endChar = globalXmlParser.attr(el, "val");
|
|
1941
|
+
}
|
|
1942
|
+
return result;
|
|
1943
|
+
}
|
|
1944
|
+
parseRunProperties(elem, run) {
|
|
1945
|
+
this.parseDefaultProperties(elem, run.cssStyle = {}, null, (c) => {
|
|
1946
|
+
switch (c.localName) {
|
|
1947
|
+
case "rStyle":
|
|
1948
|
+
run.styleName = globalXmlParser.attr(c, "val");
|
|
1949
|
+
break;
|
|
1950
|
+
case "vertAlign":
|
|
1951
|
+
run.verticalAlign = values.valueOfVertAlign(c, true);
|
|
1952
|
+
break;
|
|
1953
|
+
default: return false;
|
|
1954
|
+
}
|
|
1955
|
+
return true;
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
parseVmlPicture(elem) {
|
|
1959
|
+
const result = {
|
|
1960
|
+
type: DomType.VmlPicture,
|
|
1961
|
+
children: []
|
|
1962
|
+
};
|
|
1963
|
+
for (const el of globalXmlParser.elements(elem)) {
|
|
1964
|
+
const child = parseVmlElement(el, this);
|
|
1965
|
+
child && result.children.push(child);
|
|
1966
|
+
}
|
|
1967
|
+
return result;
|
|
1968
|
+
}
|
|
1969
|
+
checkAlternateContent(elem) {
|
|
1970
|
+
if (elem.localName != "AlternateContent") return elem;
|
|
1971
|
+
var choice = globalXmlParser.element(elem, "Choice");
|
|
1972
|
+
if (choice) {
|
|
1973
|
+
var requires = globalXmlParser.attr(choice, "Requires");
|
|
1974
|
+
var namespaceURI = elem.lookupNamespaceURI(requires);
|
|
1975
|
+
if (supportedNamespaceURIs.includes(namespaceURI)) return choice.firstElementChild;
|
|
1976
|
+
}
|
|
1977
|
+
return globalXmlParser.element(elem, "Fallback")?.firstElementChild;
|
|
1978
|
+
}
|
|
1979
|
+
parseDrawing(node) {
|
|
1980
|
+
for (var n of globalXmlParser.elements(node)) switch (n.localName) {
|
|
1981
|
+
case "inline":
|
|
1982
|
+
case "anchor": return this.parseDrawingWrapper(n);
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
parseDrawingWrapper(node) {
|
|
1986
|
+
var result = {
|
|
1987
|
+
type: DomType.Drawing,
|
|
1988
|
+
children: [],
|
|
1989
|
+
cssStyle: {}
|
|
1990
|
+
};
|
|
1991
|
+
var isAnchor = node.localName == "anchor";
|
|
1992
|
+
let wrapType = null;
|
|
1993
|
+
let simplePos = globalXmlParser.boolAttr(node, "simplePos");
|
|
1994
|
+
globalXmlParser.boolAttr(node, "behindDoc");
|
|
1995
|
+
let posX = {
|
|
1996
|
+
relative: "page",
|
|
1997
|
+
align: "left",
|
|
1998
|
+
offset: "0"
|
|
1999
|
+
};
|
|
2000
|
+
let posY = {
|
|
2001
|
+
relative: "page",
|
|
2002
|
+
align: "top",
|
|
2003
|
+
offset: "0"
|
|
2004
|
+
};
|
|
2005
|
+
for (var n of globalXmlParser.elements(node)) switch (n.localName) {
|
|
2006
|
+
case "simplePos":
|
|
2007
|
+
if (simplePos) {
|
|
2008
|
+
posX.offset = globalXmlParser.lengthAttr(n, "x", LengthUsage.Emu);
|
|
2009
|
+
posY.offset = globalXmlParser.lengthAttr(n, "y", LengthUsage.Emu);
|
|
2010
|
+
}
|
|
2011
|
+
break;
|
|
2012
|
+
case "extent":
|
|
2013
|
+
result.cssStyle["width"] = globalXmlParser.lengthAttr(n, "cx", LengthUsage.Emu);
|
|
2014
|
+
result.cssStyle["height"] = globalXmlParser.lengthAttr(n, "cy", LengthUsage.Emu);
|
|
2015
|
+
break;
|
|
2016
|
+
case "positionH":
|
|
2017
|
+
case "positionV":
|
|
2018
|
+
if (!simplePos) {
|
|
2019
|
+
let pos = n.localName == "positionH" ? posX : posY;
|
|
2020
|
+
var alignNode = globalXmlParser.element(n, "align");
|
|
2021
|
+
var offsetNode = globalXmlParser.element(n, "posOffset");
|
|
2022
|
+
pos.relative = globalXmlParser.attr(n, "relativeFrom") ?? pos.relative;
|
|
2023
|
+
if (alignNode) pos.align = alignNode.textContent;
|
|
2024
|
+
if (offsetNode) pos.offset = convertLength(offsetNode.textContent, LengthUsage.Emu);
|
|
2025
|
+
}
|
|
2026
|
+
break;
|
|
2027
|
+
case "wrapTopAndBottom":
|
|
2028
|
+
wrapType = "wrapTopAndBottom";
|
|
2029
|
+
break;
|
|
2030
|
+
case "wrapNone":
|
|
2031
|
+
wrapType = "wrapNone";
|
|
2032
|
+
break;
|
|
2033
|
+
case "graphic":
|
|
2034
|
+
var g = this.parseGraphic(n);
|
|
2035
|
+
if (g) result.children.push(g);
|
|
2036
|
+
}
|
|
2037
|
+
if (wrapType == "wrapTopAndBottom") {
|
|
2038
|
+
result.cssStyle["display"] = "block";
|
|
2039
|
+
if (posX.align) {
|
|
2040
|
+
result.cssStyle["text-align"] = posX.align;
|
|
2041
|
+
result.cssStyle["width"] = "100%";
|
|
2042
|
+
}
|
|
2043
|
+
} else if (wrapType == "wrapNone") {
|
|
2044
|
+
result.cssStyle["display"] = "block";
|
|
2045
|
+
result.cssStyle["position"] = "relative";
|
|
2046
|
+
result.cssStyle["width"] = "0px";
|
|
2047
|
+
result.cssStyle["height"] = "0px";
|
|
2048
|
+
if (posX.offset) result.cssStyle["left"] = posX.offset;
|
|
2049
|
+
if (posY.offset) result.cssStyle["top"] = posY.offset;
|
|
2050
|
+
} else if (isAnchor && (posX.align == "left" || posX.align == "right")) result.cssStyle["float"] = posX.align;
|
|
2051
|
+
return result;
|
|
2052
|
+
}
|
|
2053
|
+
parseGraphic(elem) {
|
|
2054
|
+
var graphicData = globalXmlParser.element(elem, "graphicData");
|
|
2055
|
+
for (let n of globalXmlParser.elements(graphicData)) switch (n.localName) {
|
|
2056
|
+
case "pic": return this.parsePicture(n);
|
|
2057
|
+
}
|
|
2058
|
+
return null;
|
|
2059
|
+
}
|
|
2060
|
+
parsePicture(elem) {
|
|
2061
|
+
var result = {
|
|
2062
|
+
type: DomType.Image,
|
|
2063
|
+
src: "",
|
|
2064
|
+
cssStyle: {}
|
|
2065
|
+
};
|
|
2066
|
+
var blipFill = globalXmlParser.element(elem, "blipFill");
|
|
2067
|
+
var blip = globalXmlParser.element(blipFill, "blip");
|
|
2068
|
+
var srcRect = globalXmlParser.element(blipFill, "srcRect");
|
|
2069
|
+
result.src = globalXmlParser.attr(blip, "embed");
|
|
2070
|
+
if (srcRect) result.srcRect = [
|
|
2071
|
+
globalXmlParser.intAttr(srcRect, "l", 0) / 1e5,
|
|
2072
|
+
globalXmlParser.intAttr(srcRect, "t", 0) / 1e5,
|
|
2073
|
+
globalXmlParser.intAttr(srcRect, "r", 0) / 1e5,
|
|
2074
|
+
globalXmlParser.intAttr(srcRect, "b", 0) / 1e5
|
|
2075
|
+
];
|
|
2076
|
+
var spPr = globalXmlParser.element(elem, "spPr");
|
|
2077
|
+
var xfrm = globalXmlParser.element(spPr, "xfrm");
|
|
2078
|
+
result.cssStyle["position"] = "relative";
|
|
2079
|
+
if (xfrm) {
|
|
2080
|
+
result.rotation = globalXmlParser.intAttr(xfrm, "rot", 0) / 6e4;
|
|
2081
|
+
for (var n of globalXmlParser.elements(xfrm)) switch (n.localName) {
|
|
2082
|
+
case "ext":
|
|
2083
|
+
result.cssStyle["width"] = globalXmlParser.lengthAttr(n, "cx", LengthUsage.Emu);
|
|
2084
|
+
result.cssStyle["height"] = globalXmlParser.lengthAttr(n, "cy", LengthUsage.Emu);
|
|
2085
|
+
break;
|
|
2086
|
+
case "off":
|
|
2087
|
+
result.cssStyle["left"] = globalXmlParser.lengthAttr(n, "x", LengthUsage.Emu);
|
|
2088
|
+
result.cssStyle["top"] = globalXmlParser.lengthAttr(n, "y", LengthUsage.Emu);
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
return result;
|
|
2092
|
+
}
|
|
2093
|
+
parseTable(node) {
|
|
2094
|
+
var result = {
|
|
2095
|
+
type: DomType.Table,
|
|
2096
|
+
children: []
|
|
2097
|
+
};
|
|
2098
|
+
for (const c of globalXmlParser.elements(node)) switch (c.localName) {
|
|
2099
|
+
case "tr":
|
|
2100
|
+
result.children.push(this.parseTableRow(c));
|
|
2101
|
+
break;
|
|
2102
|
+
case "tblGrid":
|
|
2103
|
+
result.columns = this.parseTableColumns(c);
|
|
2104
|
+
break;
|
|
2105
|
+
case "tblPr": this.parseTableProperties(c, result);
|
|
2106
|
+
}
|
|
2107
|
+
return result;
|
|
2108
|
+
}
|
|
2109
|
+
parseTableColumns(node) {
|
|
2110
|
+
var result = [];
|
|
2111
|
+
for (const n of globalXmlParser.elements(node)) switch (n.localName) {
|
|
2112
|
+
case "gridCol": result.push({ width: globalXmlParser.lengthAttr(n, "w") });
|
|
2113
|
+
}
|
|
2114
|
+
return result;
|
|
2115
|
+
}
|
|
2116
|
+
parseTableProperties(elem, table) {
|
|
2117
|
+
table.cssStyle = {};
|
|
2118
|
+
table.cellStyle = {};
|
|
2119
|
+
this.parseDefaultProperties(elem, table.cssStyle, table.cellStyle, (c) => {
|
|
2120
|
+
switch (c.localName) {
|
|
2121
|
+
case "tblStyle":
|
|
2122
|
+
table.styleName = globalXmlParser.attr(c, "val");
|
|
2123
|
+
break;
|
|
2124
|
+
case "tblLook":
|
|
2125
|
+
table.className = values.classNameOftblLook(c);
|
|
2126
|
+
break;
|
|
2127
|
+
case "tblpPr":
|
|
2128
|
+
this.parseTablePosition(c, table);
|
|
2129
|
+
break;
|
|
2130
|
+
case "tblStyleColBandSize":
|
|
2131
|
+
table.colBandSize = globalXmlParser.intAttr(c, "val");
|
|
2132
|
+
break;
|
|
2133
|
+
case "tblStyleRowBandSize":
|
|
2134
|
+
table.rowBandSize = globalXmlParser.intAttr(c, "val");
|
|
2135
|
+
break;
|
|
2136
|
+
case "hidden":
|
|
2137
|
+
table.cssStyle["display"] = "none";
|
|
2138
|
+
break;
|
|
2139
|
+
default: return false;
|
|
2140
|
+
}
|
|
2141
|
+
return true;
|
|
2142
|
+
});
|
|
2143
|
+
switch (table.cssStyle["text-align"]) {
|
|
2144
|
+
case "center":
|
|
2145
|
+
delete table.cssStyle["text-align"];
|
|
2146
|
+
table.cssStyle["margin-left"] = "auto";
|
|
2147
|
+
table.cssStyle["margin-right"] = "auto";
|
|
2148
|
+
break;
|
|
2149
|
+
case "right":
|
|
2150
|
+
delete table.cssStyle["text-align"];
|
|
2151
|
+
table.cssStyle["margin-left"] = "auto";
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
parseTablePosition(node, table) {
|
|
2155
|
+
var topFromText = globalXmlParser.lengthAttr(node, "topFromText");
|
|
2156
|
+
var bottomFromText = globalXmlParser.lengthAttr(node, "bottomFromText");
|
|
2157
|
+
var rightFromText = globalXmlParser.lengthAttr(node, "rightFromText");
|
|
2158
|
+
var leftFromText = globalXmlParser.lengthAttr(node, "leftFromText");
|
|
2159
|
+
var horzAnchor = globalXmlParser.attr(node, "horzAnchor");
|
|
2160
|
+
var vertAnchor = globalXmlParser.attr(node, "vertAnchor");
|
|
2161
|
+
var tblpXSpec = globalXmlParser.attr(node, "tblpXSpec");
|
|
2162
|
+
if (globalXmlParser.attr(node, "tblpYSpec") == "bottom" && vertAnchor != "text") {
|
|
2163
|
+
table.cssStyle["position"] = "absolute";
|
|
2164
|
+
table.cssStyle["bottom"] = vertAnchor == "page" ? "0px" : "var(--docx-pad-bottom, 0px)";
|
|
2165
|
+
if (tblpXSpec == "right") table.cssStyle["right"] = horzAnchor == "page" ? "0px" : "var(--docx-pad-right, 0px)";
|
|
2166
|
+
else if (tblpXSpec == "center") {
|
|
2167
|
+
table.cssStyle["left"] = "50%";
|
|
2168
|
+
table.cssStyle["transform"] = "translateX(-50%)";
|
|
2169
|
+
} else table.cssStyle["left"] = horzAnchor == "page" ? "0px" : "var(--docx-pad-left, 0px)";
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
table.cssStyle["float"] = "left";
|
|
2173
|
+
table.cssStyle["margin-bottom"] = values.addSize(table.cssStyle["margin-bottom"], bottomFromText);
|
|
2174
|
+
table.cssStyle["margin-left"] = values.addSize(table.cssStyle["margin-left"], leftFromText);
|
|
2175
|
+
table.cssStyle["margin-right"] = values.addSize(table.cssStyle["margin-right"], rightFromText);
|
|
2176
|
+
table.cssStyle["margin-top"] = values.addSize(table.cssStyle["margin-top"], topFromText);
|
|
2177
|
+
}
|
|
2178
|
+
parseTableRow(node) {
|
|
2179
|
+
var result = {
|
|
2180
|
+
type: DomType.Row,
|
|
2181
|
+
children: []
|
|
2182
|
+
};
|
|
2183
|
+
for (const c of globalXmlParser.elements(node)) switch (c.localName) {
|
|
2184
|
+
case "tc":
|
|
2185
|
+
result.children.push(this.parseTableCell(c));
|
|
2186
|
+
break;
|
|
2187
|
+
case "trPr":
|
|
2188
|
+
case "tblPrEx": this.parseTableRowProperties(c, result);
|
|
2189
|
+
}
|
|
2190
|
+
return result;
|
|
2191
|
+
}
|
|
2192
|
+
parseTableRowProperties(elem, row) {
|
|
2193
|
+
row.cssStyle = this.parseDefaultProperties(elem, {}, null, (c) => {
|
|
2194
|
+
switch (c.localName) {
|
|
2195
|
+
case "cnfStyle":
|
|
2196
|
+
row.className = values.classNameOfCnfStyle(c);
|
|
2197
|
+
break;
|
|
2198
|
+
case "tblHeader":
|
|
2199
|
+
row.isHeader = globalXmlParser.boolAttr(c, "val");
|
|
2200
|
+
break;
|
|
2201
|
+
case "gridBefore":
|
|
2202
|
+
row.gridBefore = globalXmlParser.intAttr(c, "val");
|
|
2203
|
+
break;
|
|
2204
|
+
case "gridAfter":
|
|
2205
|
+
row.gridAfter = globalXmlParser.intAttr(c, "val");
|
|
2206
|
+
break;
|
|
2207
|
+
default: return false;
|
|
2208
|
+
}
|
|
2209
|
+
return true;
|
|
2210
|
+
});
|
|
2211
|
+
}
|
|
2212
|
+
parseTableCell(node) {
|
|
2213
|
+
var result = {
|
|
2214
|
+
type: DomType.Cell,
|
|
2215
|
+
children: []
|
|
2216
|
+
};
|
|
2217
|
+
for (const c of globalXmlParser.elements(node)) switch (c.localName) {
|
|
2218
|
+
case "tbl":
|
|
2219
|
+
result.children.push(this.parseTable(c));
|
|
2220
|
+
break;
|
|
2221
|
+
case "p":
|
|
2222
|
+
result.children.push(this.parseParagraph(c));
|
|
2223
|
+
break;
|
|
2224
|
+
case "tcPr": this.parseTableCellProperties(c, result);
|
|
2225
|
+
}
|
|
2226
|
+
return result;
|
|
2227
|
+
}
|
|
2228
|
+
parseTableCellProperties(elem, cell) {
|
|
2229
|
+
cell.cssStyle = this.parseDefaultProperties(elem, {}, null, (c) => {
|
|
2230
|
+
switch (c.localName) {
|
|
2231
|
+
case "gridSpan":
|
|
2232
|
+
cell.span = globalXmlParser.intAttr(c, "val", null);
|
|
2233
|
+
break;
|
|
2234
|
+
case "vMerge":
|
|
2235
|
+
cell.verticalMerge = globalXmlParser.attr(c, "val") ?? "continue";
|
|
2236
|
+
break;
|
|
2237
|
+
case "cnfStyle":
|
|
2238
|
+
cell.className = values.classNameOfCnfStyle(c);
|
|
2239
|
+
break;
|
|
2240
|
+
default: return false;
|
|
2241
|
+
}
|
|
2242
|
+
return true;
|
|
2243
|
+
});
|
|
2244
|
+
this.parseTableCellVerticalText(elem, cell);
|
|
2245
|
+
}
|
|
2246
|
+
parseTableCellVerticalText(elem, cell) {
|
|
2247
|
+
const directionMap = {
|
|
2248
|
+
"btLr": {
|
|
2249
|
+
writingMode: "vertical-rl",
|
|
2250
|
+
transform: "rotate(180deg)"
|
|
2251
|
+
},
|
|
2252
|
+
"lrTb": {
|
|
2253
|
+
writingMode: "vertical-lr",
|
|
2254
|
+
transform: "none"
|
|
2255
|
+
},
|
|
2256
|
+
"tbRl": {
|
|
2257
|
+
writingMode: "vertical-rl",
|
|
2258
|
+
transform: "none"
|
|
2259
|
+
}
|
|
2260
|
+
};
|
|
2261
|
+
for (const c of globalXmlParser.elements(elem)) if (c.localName === "textDirection") {
|
|
2262
|
+
const style = directionMap[globalXmlParser.attr(c, "val")] || { writingMode: "horizontal-tb" };
|
|
2263
|
+
cell.cssStyle["writing-mode"] = style.writingMode;
|
|
2264
|
+
cell.cssStyle["transform"] = style.transform;
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
parseDefaultProperties(elem, style = null, childStyle = null, handler = null) {
|
|
2268
|
+
style = style || {};
|
|
2269
|
+
for (const c of globalXmlParser.elements(elem)) {
|
|
2270
|
+
if (handler?.(c)) continue;
|
|
2271
|
+
switch (c.localName) {
|
|
2272
|
+
case "jc":
|
|
2273
|
+
style["text-align"] = values.valueOfJc(c);
|
|
2274
|
+
break;
|
|
2275
|
+
case "textAlignment":
|
|
2276
|
+
style["vertical-align"] = values.valueOfTextAlignment(c);
|
|
2277
|
+
break;
|
|
2278
|
+
case "color":
|
|
2279
|
+
style["color"] = xmlUtil.colorAttr(c, "val", null, autos.color);
|
|
2280
|
+
break;
|
|
2281
|
+
case "sz":
|
|
2282
|
+
style["font-size"] = style["min-height"] = globalXmlParser.lengthAttr(c, "val", LengthUsage.FontSize);
|
|
2283
|
+
break;
|
|
2284
|
+
case "shd":
|
|
2285
|
+
style["background-color"] = xmlUtil.colorAttr(c, "fill", null, autos.shd);
|
|
2286
|
+
break;
|
|
2287
|
+
case "highlight":
|
|
2288
|
+
style["background-color"] = xmlUtil.colorAttr(c, "val", null, autos.highlight);
|
|
2289
|
+
break;
|
|
2290
|
+
case "vertAlign": break;
|
|
2291
|
+
case "position":
|
|
2292
|
+
style.verticalAlign = globalXmlParser.lengthAttr(c, "val", LengthUsage.FontSize);
|
|
2293
|
+
break;
|
|
2294
|
+
case "tcW": if (this.options.ignoreWidth) break;
|
|
2295
|
+
case "tblW":
|
|
2296
|
+
style["width"] = values.valueOfSize(c, "w");
|
|
2297
|
+
break;
|
|
2298
|
+
case "trHeight":
|
|
2299
|
+
this.parseTrHeight(c, style);
|
|
2300
|
+
break;
|
|
2301
|
+
case "strike":
|
|
2302
|
+
style["text-decoration"] = globalXmlParser.boolAttr(c, "val", true) ? "line-through" : "none";
|
|
2303
|
+
break;
|
|
2304
|
+
case "b":
|
|
2305
|
+
style["font-weight"] = globalXmlParser.boolAttr(c, "val", true) ? "bold" : "normal";
|
|
2306
|
+
break;
|
|
2307
|
+
case "i":
|
|
2308
|
+
style["font-style"] = globalXmlParser.boolAttr(c, "val", true) ? "italic" : "normal";
|
|
2309
|
+
break;
|
|
2310
|
+
case "caps":
|
|
2311
|
+
style["text-transform"] = globalXmlParser.boolAttr(c, "val", true) ? "uppercase" : "none";
|
|
2312
|
+
break;
|
|
2313
|
+
case "smallCaps":
|
|
2314
|
+
style["font-variant"] = globalXmlParser.boolAttr(c, "val", true) ? "small-caps" : "none";
|
|
2315
|
+
break;
|
|
2316
|
+
case "u":
|
|
2317
|
+
this.parseUnderline(c, style);
|
|
2318
|
+
break;
|
|
2319
|
+
case "ind":
|
|
2320
|
+
case "tblInd":
|
|
2321
|
+
this.parseIndentation(c, style);
|
|
2322
|
+
break;
|
|
2323
|
+
case "rFonts":
|
|
2324
|
+
this.parseFont(c, style);
|
|
2325
|
+
break;
|
|
2326
|
+
case "tblBorders":
|
|
2327
|
+
this.parseBorderProperties(c, childStyle || style);
|
|
2328
|
+
break;
|
|
2329
|
+
case "tblCellSpacing":
|
|
2330
|
+
style["border-spacing"] = values.valueOfMargin(c);
|
|
2331
|
+
style["border-collapse"] = "separate";
|
|
2332
|
+
break;
|
|
2333
|
+
case "pBdr":
|
|
2334
|
+
this.parseBorderProperties(c, style);
|
|
2335
|
+
break;
|
|
2336
|
+
case "bdr":
|
|
2337
|
+
style["border"] = values.valueOfBorder(c);
|
|
2338
|
+
break;
|
|
2339
|
+
case "tcBorders":
|
|
2340
|
+
this.parseBorderProperties(c, style);
|
|
2341
|
+
break;
|
|
2342
|
+
case "vanish":
|
|
2343
|
+
if (globalXmlParser.boolAttr(c, "val", true)) style["display"] = "none";
|
|
2344
|
+
break;
|
|
2345
|
+
case "kern": break;
|
|
2346
|
+
case "noWrap": break;
|
|
2347
|
+
case "tblCellMar":
|
|
2348
|
+
case "tcMar":
|
|
2349
|
+
this.parseMarginProperties(c, childStyle || style);
|
|
2350
|
+
break;
|
|
2351
|
+
case "tblLayout":
|
|
2352
|
+
style["table-layout"] = values.valueOfTblLayout(c);
|
|
2353
|
+
break;
|
|
2354
|
+
case "vAlign":
|
|
2355
|
+
style["vertical-align"] = values.valueOfTextAlignment(c);
|
|
2356
|
+
break;
|
|
2357
|
+
case "spacing":
|
|
2358
|
+
if (elem.localName == "pPr") this.parseSpacing(c, style);
|
|
2359
|
+
break;
|
|
2360
|
+
case "wordWrap":
|
|
2361
|
+
if (globalXmlParser.boolAttr(c, "val")) style["overflow-wrap"] = "break-word";
|
|
2362
|
+
break;
|
|
2363
|
+
case "suppressAutoHyphens":
|
|
2364
|
+
style["hyphens"] = globalXmlParser.boolAttr(c, "val", true) ? "none" : "auto";
|
|
2365
|
+
break;
|
|
2366
|
+
case "lang":
|
|
2367
|
+
style["$lang"] = globalXmlParser.attr(c, "val");
|
|
2368
|
+
break;
|
|
2369
|
+
case "rtl":
|
|
2370
|
+
case "bidi":
|
|
2371
|
+
if (globalXmlParser.boolAttr(c, "val", true)) style["direction"] = "rtl";
|
|
2372
|
+
break;
|
|
2373
|
+
case "bCs":
|
|
2374
|
+
case "iCs":
|
|
2375
|
+
case "szCs":
|
|
2376
|
+
case "tabs":
|
|
2377
|
+
case "outlineLvl":
|
|
2378
|
+
case "contextualSpacing":
|
|
2379
|
+
case "tblStyleColBandSize":
|
|
2380
|
+
case "tblStyleRowBandSize":
|
|
2381
|
+
case "webHidden":
|
|
2382
|
+
case "pageBreakBefore":
|
|
2383
|
+
case "suppressLineNumbers":
|
|
2384
|
+
case "keepLines":
|
|
2385
|
+
case "keepNext":
|
|
2386
|
+
case "widowControl":
|
|
2387
|
+
case "noProof": break;
|
|
2388
|
+
default: if (this.options.debug) console.warn(`DOCX: Unknown document element: ${elem.localName}.${c.localName}`);
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
return style;
|
|
2392
|
+
}
|
|
2393
|
+
parseUnderline(node, style) {
|
|
2394
|
+
var val = globalXmlParser.attr(node, "val");
|
|
2395
|
+
if (val == null) return;
|
|
2396
|
+
switch (val) {
|
|
2397
|
+
case "dash":
|
|
2398
|
+
case "dashDotDotHeavy":
|
|
2399
|
+
case "dashDotHeavy":
|
|
2400
|
+
case "dashedHeavy":
|
|
2401
|
+
case "dashLong":
|
|
2402
|
+
case "dashLongHeavy":
|
|
2403
|
+
case "dotDash":
|
|
2404
|
+
case "dotDotDash":
|
|
2405
|
+
style["text-decoration"] = "underline dashed";
|
|
2406
|
+
break;
|
|
2407
|
+
case "dotted":
|
|
2408
|
+
case "dottedHeavy":
|
|
2409
|
+
style["text-decoration"] = "underline dotted";
|
|
2410
|
+
break;
|
|
2411
|
+
case "double":
|
|
2412
|
+
style["text-decoration"] = "underline double";
|
|
2413
|
+
break;
|
|
2414
|
+
case "single":
|
|
2415
|
+
case "thick":
|
|
2416
|
+
style["text-decoration"] = "underline";
|
|
2417
|
+
break;
|
|
2418
|
+
case "wave":
|
|
2419
|
+
case "wavyDouble":
|
|
2420
|
+
case "wavyHeavy":
|
|
2421
|
+
style["text-decoration"] = "underline wavy";
|
|
2422
|
+
break;
|
|
2423
|
+
case "words":
|
|
2424
|
+
style["text-decoration"] = "underline";
|
|
2425
|
+
break;
|
|
2426
|
+
case "none": style["text-decoration"] = "none";
|
|
2427
|
+
}
|
|
2428
|
+
var col = xmlUtil.colorAttr(node, "color");
|
|
2429
|
+
if (col) style["text-decoration-color"] = col;
|
|
2430
|
+
}
|
|
2431
|
+
parseFont(node, style) {
|
|
2432
|
+
var ascii = globalXmlParser.attr(node, "ascii") ?? globalXmlParser.attr(node, "hAnsi") ?? values.themeValue(node, "asciiTheme") ?? values.themeValue(node, "hAnsiTheme");
|
|
2433
|
+
var eastAsia = globalXmlParser.attr(node, "eastAsia") ?? values.themeValue(node, "eastAsiaTheme");
|
|
2434
|
+
if (ascii) style["--docx-font-ascii"] = encloseFontFamily(ascii);
|
|
2435
|
+
if (eastAsia) style["--docx-font-ea"] = encloseFontFamily(eastAsia);
|
|
2436
|
+
}
|
|
2437
|
+
parseIndentation(node, style) {
|
|
2438
|
+
var firstLine = globalXmlParser.lengthAttr(node, "firstLine");
|
|
2439
|
+
var hanging = globalXmlParser.lengthAttr(node, "hanging");
|
|
2440
|
+
var left = globalXmlParser.lengthAttr(node, "left");
|
|
2441
|
+
var start = globalXmlParser.lengthAttr(node, "start");
|
|
2442
|
+
var right = globalXmlParser.lengthAttr(node, "right");
|
|
2443
|
+
var end = globalXmlParser.lengthAttr(node, "end");
|
|
2444
|
+
if (firstLine) style["text-indent"] = firstLine;
|
|
2445
|
+
if (hanging) style["text-indent"] = `-${hanging}`;
|
|
2446
|
+
if (left || start) style["margin-inline-start"] = left || start;
|
|
2447
|
+
if (right || end) style["margin-inline-end"] = right || end;
|
|
2448
|
+
}
|
|
2449
|
+
parseSpacing(node, style) {
|
|
2450
|
+
var before = globalXmlParser.lengthAttr(node, "before");
|
|
2451
|
+
var after = globalXmlParser.lengthAttr(node, "after");
|
|
2452
|
+
var beforeLines = globalXmlParser.intAttr(node, "beforeLines", null);
|
|
2453
|
+
var afterLines = globalXmlParser.intAttr(node, "afterLines", null);
|
|
2454
|
+
var line = globalXmlParser.intAttr(node, "line", null);
|
|
2455
|
+
var lineRule = globalXmlParser.attr(node, "lineRule");
|
|
2456
|
+
if (before) style["margin-top"] = before;
|
|
2457
|
+
else if (beforeLines != null) style["margin-top"] = `${beforeLines / 100}em`;
|
|
2458
|
+
if (after) style["margin-bottom"] = after;
|
|
2459
|
+
else if (afterLines != null) style["margin-bottom"] = `${afterLines / 100}em`;
|
|
2460
|
+
if (line !== null) switch (lineRule) {
|
|
2461
|
+
case "auto":
|
|
2462
|
+
style["line-height"] = `${(line / 240).toFixed(2)}`;
|
|
2463
|
+
break;
|
|
2464
|
+
case "atLeast":
|
|
2465
|
+
style["line-height"] = `${line / 20}pt`;
|
|
2466
|
+
break;
|
|
2467
|
+
default: style["line-height"] = style["min-height"] = `${line / 20}pt`;
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
parseMarginProperties(node, output) {
|
|
2471
|
+
for (const c of globalXmlParser.elements(node)) switch (c.localName) {
|
|
2472
|
+
case "left":
|
|
2473
|
+
output["padding-left"] = values.valueOfMargin(c);
|
|
2474
|
+
break;
|
|
2475
|
+
case "right":
|
|
2476
|
+
output["padding-right"] = values.valueOfMargin(c);
|
|
2477
|
+
break;
|
|
2478
|
+
case "top":
|
|
2479
|
+
output["padding-top"] = values.valueOfMargin(c);
|
|
2480
|
+
break;
|
|
2481
|
+
case "bottom": output["padding-bottom"] = values.valueOfMargin(c);
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
parseTrHeight(node, output) {
|
|
2485
|
+
switch (globalXmlParser.attr(node, "hRule")) {
|
|
2486
|
+
case "exact":
|
|
2487
|
+
output["height"] = globalXmlParser.lengthAttr(node, "val");
|
|
2488
|
+
break;
|
|
2489
|
+
default: output["height"] = globalXmlParser.lengthAttr(node, "val");
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
parseBorderProperties(node, output) {
|
|
2493
|
+
for (const c of globalXmlParser.elements(node)) switch (c.localName) {
|
|
2494
|
+
case "start":
|
|
2495
|
+
case "left":
|
|
2496
|
+
output["border-left"] = values.valueOfBorder(c);
|
|
2497
|
+
break;
|
|
2498
|
+
case "end":
|
|
2499
|
+
case "right":
|
|
2500
|
+
output["border-right"] = values.valueOfBorder(c);
|
|
2501
|
+
break;
|
|
2502
|
+
case "top":
|
|
2503
|
+
output["border-top"] = values.valueOfBorder(c);
|
|
2504
|
+
break;
|
|
2505
|
+
case "bottom": output["border-bottom"] = values.valueOfBorder(c);
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
};
|
|
2509
|
+
var knownColors = [
|
|
2510
|
+
"black",
|
|
2511
|
+
"blue",
|
|
2512
|
+
"cyan",
|
|
2513
|
+
"darkBlue",
|
|
2514
|
+
"darkCyan",
|
|
2515
|
+
"darkGray",
|
|
2516
|
+
"darkGreen",
|
|
2517
|
+
"darkMagenta",
|
|
2518
|
+
"darkRed",
|
|
2519
|
+
"darkYellow",
|
|
2520
|
+
"green",
|
|
2521
|
+
"lightGray",
|
|
2522
|
+
"magenta",
|
|
2523
|
+
"none",
|
|
2524
|
+
"red",
|
|
2525
|
+
"white",
|
|
2526
|
+
"yellow"
|
|
2527
|
+
];
|
|
2528
|
+
var xmlUtil = class {
|
|
2529
|
+
static colorAttr(node, attrName, defValue = null, autoColor = "black") {
|
|
2530
|
+
var v = globalXmlParser.attr(node, attrName);
|
|
2531
|
+
if (v) {
|
|
2532
|
+
if (v == "auto") return autoColor;
|
|
2533
|
+
else if (knownColors.includes(v)) return v;
|
|
2534
|
+
return `#${v}`;
|
|
2535
|
+
}
|
|
2536
|
+
var themeColor = globalXmlParser.attr(node, "themeColor");
|
|
2537
|
+
return themeColor ? `var(--docx-${themeColor}-color)` : defValue;
|
|
2538
|
+
}
|
|
2539
|
+
};
|
|
2540
|
+
var values = class values {
|
|
2541
|
+
static themeValue(c, attr) {
|
|
2542
|
+
var val = globalXmlParser.attr(c, attr);
|
|
2543
|
+
return val ? `var(--docx-${val}-font)` : null;
|
|
2544
|
+
}
|
|
2545
|
+
static valueOfSize(c, attr) {
|
|
2546
|
+
var type = LengthUsage.Dxa;
|
|
2547
|
+
switch (globalXmlParser.attr(c, "type")) {
|
|
2548
|
+
case "dxa": break;
|
|
2549
|
+
case "pct":
|
|
2550
|
+
type = LengthUsage.Percent;
|
|
2551
|
+
break;
|
|
2552
|
+
case "auto": return "auto";
|
|
2553
|
+
}
|
|
2554
|
+
return globalXmlParser.lengthAttr(c, attr, type);
|
|
2555
|
+
}
|
|
2556
|
+
static valueOfMargin(c) {
|
|
2557
|
+
return globalXmlParser.lengthAttr(c, "w");
|
|
2558
|
+
}
|
|
2559
|
+
static valueOfBorder(c) {
|
|
2560
|
+
var type = values.parseBorderType(globalXmlParser.attr(c, "val"));
|
|
2561
|
+
if (type == "none") return "none";
|
|
2562
|
+
var color = xmlUtil.colorAttr(c, "color");
|
|
2563
|
+
return `${globalXmlParser.lengthAttr(c, "sz", LengthUsage.Border)} ${type} ${color == "auto" ? autos.borderColor : color}`;
|
|
2564
|
+
}
|
|
2565
|
+
static parseBorderType(type) {
|
|
2566
|
+
switch (type) {
|
|
2567
|
+
case "single": return "solid";
|
|
2568
|
+
case "dashDotStroked": return "solid";
|
|
2569
|
+
case "dashed": return "dashed";
|
|
2570
|
+
case "dashSmallGap": return "dashed";
|
|
2571
|
+
case "dotDash": return "dotted";
|
|
2572
|
+
case "dotDotDash": return "dotted";
|
|
2573
|
+
case "dotted": return "dotted";
|
|
2574
|
+
case "double": return "double";
|
|
2575
|
+
case "doubleWave": return "double";
|
|
2576
|
+
case "inset": return "inset";
|
|
2577
|
+
case "nil": return "none";
|
|
2578
|
+
case "none": return "none";
|
|
2579
|
+
case "outset": return "outset";
|
|
2580
|
+
case "thick": return "solid";
|
|
2581
|
+
case "thickThinLargeGap": return "solid";
|
|
2582
|
+
case "thickThinMediumGap": return "solid";
|
|
2583
|
+
case "thickThinSmallGap": return "solid";
|
|
2584
|
+
case "thinThickLargeGap": return "solid";
|
|
2585
|
+
case "thinThickMediumGap": return "solid";
|
|
2586
|
+
case "thinThickSmallGap": return "solid";
|
|
2587
|
+
case "thinThickThinLargeGap": return "solid";
|
|
2588
|
+
case "thinThickThinMediumGap": return "solid";
|
|
2589
|
+
case "thinThickThinSmallGap": return "solid";
|
|
2590
|
+
case "threeDEmboss": return "solid";
|
|
2591
|
+
case "threeDEngrave": return "solid";
|
|
2592
|
+
case "triple": return "double";
|
|
2593
|
+
case "wave": return "solid";
|
|
2594
|
+
}
|
|
2595
|
+
return "solid";
|
|
2596
|
+
}
|
|
2597
|
+
static valueOfTblLayout(c) {
|
|
2598
|
+
return globalXmlParser.attr(c, "val") == "fixed" ? "fixed" : "auto";
|
|
2599
|
+
}
|
|
2600
|
+
static classNameOfCnfStyle(c) {
|
|
2601
|
+
const val = globalXmlParser.attr(c, "val");
|
|
2602
|
+
const classes = [
|
|
2603
|
+
"first-row",
|
|
2604
|
+
"last-row",
|
|
2605
|
+
"first-col",
|
|
2606
|
+
"last-col",
|
|
2607
|
+
"odd-col",
|
|
2608
|
+
"even-col",
|
|
2609
|
+
"odd-row",
|
|
2610
|
+
"even-row",
|
|
2611
|
+
"ne-cell",
|
|
2612
|
+
"nw-cell",
|
|
2613
|
+
"se-cell",
|
|
2614
|
+
"sw-cell"
|
|
2615
|
+
];
|
|
2616
|
+
if (val) return classes.filter((_, i) => val[i] == "1").join(" ");
|
|
2617
|
+
const attrs = [
|
|
2618
|
+
"firstRow",
|
|
2619
|
+
"lastRow",
|
|
2620
|
+
"firstColumn",
|
|
2621
|
+
"lastColumn",
|
|
2622
|
+
"oddVBand",
|
|
2623
|
+
"evenVBand",
|
|
2624
|
+
"oddHBand",
|
|
2625
|
+
"evenHBand",
|
|
2626
|
+
"firstRowLastColumn",
|
|
2627
|
+
"firstRowFirstColumn",
|
|
2628
|
+
"lastRowLastColumn",
|
|
2629
|
+
"lastRowFirstColumn"
|
|
2630
|
+
];
|
|
2631
|
+
return classes.filter((_, i) => globalXmlParser.boolAttr(c, attrs[i])).join(" ");
|
|
2632
|
+
}
|
|
2633
|
+
static valueOfJc(c) {
|
|
2634
|
+
var type = globalXmlParser.attr(c, "val");
|
|
2635
|
+
switch (type) {
|
|
2636
|
+
case "start":
|
|
2637
|
+
case "left": return "left";
|
|
2638
|
+
case "center": return "center";
|
|
2639
|
+
case "end":
|
|
2640
|
+
case "right": return "right";
|
|
2641
|
+
case "both": return "justify";
|
|
2642
|
+
}
|
|
2643
|
+
return type;
|
|
2644
|
+
}
|
|
2645
|
+
static valueOfVertAlign(c, asTagName = false) {
|
|
2646
|
+
var type = globalXmlParser.attr(c, "val");
|
|
2647
|
+
switch (type) {
|
|
2648
|
+
case "subscript": return "sub";
|
|
2649
|
+
case "superscript": return asTagName ? "sup" : "super";
|
|
2650
|
+
}
|
|
2651
|
+
return asTagName ? null : type;
|
|
2652
|
+
}
|
|
2653
|
+
static valueOfTextAlignment(c) {
|
|
2654
|
+
var type = globalXmlParser.attr(c, "val");
|
|
2655
|
+
switch (type) {
|
|
2656
|
+
case "auto":
|
|
2657
|
+
case "baseline": return "baseline";
|
|
2658
|
+
case "top": return "top";
|
|
2659
|
+
case "center": return "middle";
|
|
2660
|
+
case "bottom": return "bottom";
|
|
2661
|
+
}
|
|
2662
|
+
return type;
|
|
2663
|
+
}
|
|
2664
|
+
static addSize(a, b) {
|
|
2665
|
+
if (a == null) return b;
|
|
2666
|
+
if (b == null) return a;
|
|
2667
|
+
return `calc(${a} + ${b})`;
|
|
2668
|
+
}
|
|
2669
|
+
static classNameOftblLook(c) {
|
|
2670
|
+
const val = globalXmlParser.hexAttr(c, "val", 0);
|
|
2671
|
+
let className = "";
|
|
2672
|
+
if (globalXmlParser.boolAttr(c, "firstRow") || val & 32) className += " first-row";
|
|
2673
|
+
if (globalXmlParser.boolAttr(c, "lastRow") || val & 64) className += " last-row";
|
|
2674
|
+
if (globalXmlParser.boolAttr(c, "firstColumn") || val & 128) className += " first-col";
|
|
2675
|
+
if (globalXmlParser.boolAttr(c, "lastColumn") || val & 256) className += " last-col";
|
|
2676
|
+
if (globalXmlParser.boolAttr(c, "noHBand") || val & 512) className += " no-hband";
|
|
2677
|
+
if (globalXmlParser.boolAttr(c, "noVBand") || val & 1024) className += " no-vband";
|
|
2678
|
+
return className.trim();
|
|
2679
|
+
}
|
|
2680
|
+
};
|
|
2681
|
+
//#endregion
|
|
2682
|
+
//#region src/javascript.ts
|
|
2683
|
+
var defaultTab = {
|
|
2684
|
+
pos: 0,
|
|
2685
|
+
leader: "none",
|
|
2686
|
+
style: "left"
|
|
2687
|
+
};
|
|
2688
|
+
var maxTabs = 50;
|
|
2689
|
+
function computePixelToPoint(container = document.body) {
|
|
2690
|
+
const temp = document.createElement("div");
|
|
2691
|
+
temp.style.width = "100pt";
|
|
2692
|
+
container.appendChild(temp);
|
|
2693
|
+
const result = 100 / temp.offsetWidth;
|
|
2694
|
+
container.removeChild(temp);
|
|
2695
|
+
return result;
|
|
2696
|
+
}
|
|
2697
|
+
function updateTabStop(elem, tabs, defaultTabSize, pixelToPoint = 72 / 96) {
|
|
2698
|
+
const p = elem.closest("p");
|
|
2699
|
+
const ebb = elem.getBoundingClientRect();
|
|
2700
|
+
const pbb = p.getBoundingClientRect();
|
|
2701
|
+
const pcs = getComputedStyle(p);
|
|
2702
|
+
const tabStops = tabs?.length > 0 ? tabs.map((t) => ({
|
|
2703
|
+
pos: lengthToPoint(t.position),
|
|
2704
|
+
leader: t.leader,
|
|
2705
|
+
style: t.style
|
|
2706
|
+
})).sort((a, b) => a.pos - b.pos) : [defaultTab];
|
|
2707
|
+
const lastTab = tabStops[tabStops.length - 1];
|
|
2708
|
+
const pWidthPt = pbb.width * pixelToPoint;
|
|
2709
|
+
const size = lengthToPoint(defaultTabSize);
|
|
2710
|
+
let pos = lastTab.pos + size;
|
|
2711
|
+
if (pos < pWidthPt) for (; pos < pWidthPt && tabStops.length < maxTabs; pos += size) tabStops.push({
|
|
2712
|
+
...defaultTab,
|
|
2713
|
+
pos
|
|
2714
|
+
});
|
|
2715
|
+
const marginLeft = parseFloat(pcs.marginLeft);
|
|
2716
|
+
const pOffset = pbb.left + marginLeft;
|
|
2717
|
+
const left = (ebb.left - pOffset) * pixelToPoint;
|
|
2718
|
+
const tab = tabStops.find((t) => t.style != "clear" && t.pos > left);
|
|
2719
|
+
if (tab == null) return;
|
|
2720
|
+
let width = 1;
|
|
2721
|
+
if (tab.style == "right" || tab.style == "center") {
|
|
2722
|
+
const tabStops = Array.from(p.querySelectorAll(`.${elem.className}`));
|
|
2723
|
+
const nextIdx = tabStops.indexOf(elem) + 1;
|
|
2724
|
+
const range = document.createRange();
|
|
2725
|
+
range.setStart(elem, 1);
|
|
2726
|
+
if (nextIdx < tabStops.length) range.setEndBefore(tabStops[nextIdx]);
|
|
2727
|
+
else range.setEndAfter(p);
|
|
2728
|
+
const mul = tab.style == "center" ? .5 : 1;
|
|
2729
|
+
const nextBB = range.getBoundingClientRect();
|
|
2730
|
+
const offset = nextBB.left + mul * nextBB.width - (pbb.left - marginLeft);
|
|
2731
|
+
width = tab.pos - offset * pixelToPoint;
|
|
2732
|
+
} else width = tab.pos - left;
|
|
2733
|
+
elem.innerHTML = " ";
|
|
2734
|
+
elem.style.textDecoration = "inherit";
|
|
2735
|
+
elem.style.wordSpacing = `${width.toFixed(0)}pt`;
|
|
2736
|
+
switch (tab.leader) {
|
|
2737
|
+
case "dot":
|
|
2738
|
+
case "middleDot":
|
|
2739
|
+
elem.style.textDecoration = "underline";
|
|
2740
|
+
elem.style.textDecorationStyle = "dotted";
|
|
2741
|
+
break;
|
|
2742
|
+
case "hyphen":
|
|
2743
|
+
case "heavy":
|
|
2744
|
+
case "underscore": elem.style.textDecoration = "underline";
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
function lengthToPoint(length) {
|
|
2748
|
+
return parseFloat(length);
|
|
2749
|
+
}
|
|
2750
|
+
//#endregion
|
|
2751
|
+
//#region src/html.ts
|
|
2752
|
+
var ns = /* @__PURE__ */ function(ns) {
|
|
2753
|
+
ns["html"] = "http://www.w3.org/1999/xhtml";
|
|
2754
|
+
ns["svg"] = "http://www.w3.org/2000/svg";
|
|
2755
|
+
ns["mathML"] = "http://www.w3.org/1998/Math/MathML";
|
|
2756
|
+
return ns;
|
|
2757
|
+
}({});
|
|
2758
|
+
function h(elem) {
|
|
2759
|
+
if (isString(elem)) return document.createTextNode(elem);
|
|
2760
|
+
if (elem instanceof Node) return elem;
|
|
2761
|
+
const { ns, tagName, className, style, children, ...props } = elem;
|
|
2762
|
+
if (tagName === "#fragment") return document.createDocumentFragment();
|
|
2763
|
+
if (tagName === "#comment") return document.createComment(children[0]);
|
|
2764
|
+
const result = ns ? document.createElementNS(ns, tagName) : document.createElement(tagName);
|
|
2765
|
+
if (className) result.setAttribute("class", className);
|
|
2766
|
+
if (style) {
|
|
2767
|
+
if (isString(style)) result.setAttribute("style", style);
|
|
2768
|
+
else for (const [key, value] of Object.entries(style)) if (key.startsWith("--")) result.style.setProperty(key, value);
|
|
2769
|
+
else result.style[key] = value;
|
|
2770
|
+
}
|
|
2771
|
+
if (props) {
|
|
2772
|
+
for (const [key, value] of Object.entries(props)) if (value !== void 0) result[key] = value;
|
|
2773
|
+
}
|
|
2774
|
+
if (children) children.forEach((c) => result.appendChild(h(c)));
|
|
2775
|
+
return result;
|
|
2776
|
+
}
|
|
2777
|
+
function cx(...classNames) {
|
|
2778
|
+
return classNames.filter(Boolean).join(" ");
|
|
2779
|
+
}
|
|
2780
|
+
//#endregion
|
|
2781
|
+
//#region src/html-renderer.ts
|
|
2782
|
+
var HtmlRenderer = class {
|
|
2783
|
+
constructor() {
|
|
2784
|
+
this.className = "docx";
|
|
2785
|
+
this.styleMap = {};
|
|
2786
|
+
this.currentPart = null;
|
|
2787
|
+
this.tableVerticalMerges = [];
|
|
2788
|
+
this.currentVerticalMerge = null;
|
|
2789
|
+
this.tableCellPositions = [];
|
|
2790
|
+
this.currentCellPosition = null;
|
|
2791
|
+
this.footnoteMap = {};
|
|
2792
|
+
this.endnoteMap = {};
|
|
2793
|
+
this.currentEndnoteIds = [];
|
|
2794
|
+
this.usedHederFooterParts = [];
|
|
2795
|
+
this.currentTabs = [];
|
|
2796
|
+
this.legacyCompat = false;
|
|
2797
|
+
this.commentMap = {};
|
|
2798
|
+
this.tasks = [];
|
|
2799
|
+
this.postRenderTasks = [];
|
|
2800
|
+
this.h = h;
|
|
2801
|
+
}
|
|
2802
|
+
async render(document, options) {
|
|
2803
|
+
this.document = document;
|
|
2804
|
+
this.options = options;
|
|
2805
|
+
this.className = options.className;
|
|
2806
|
+
this.rootSelector = options.inWrapper ? `.${this.className}-wrapper` : ":root";
|
|
2807
|
+
this.h = options.h ?? h;
|
|
2808
|
+
this.styleMap = null;
|
|
2809
|
+
this.tasks = [];
|
|
2810
|
+
if (this.options.renderComments && globalThis.Highlight) this.commentHighlight = new Highlight();
|
|
2811
|
+
const result = [...this.renderDefaultStyle()];
|
|
2812
|
+
if (document.themePart) result.push(...this.renderTheme(document.themePart));
|
|
2813
|
+
if (document.stylesPart != null) {
|
|
2814
|
+
this.styleMap = this.processStyles(document.stylesPart.styles);
|
|
2815
|
+
result.push(...this.renderStyles(document.stylesPart.styles));
|
|
2816
|
+
}
|
|
2817
|
+
if (document.numberingPart) {
|
|
2818
|
+
this.prodessNumberings(document.numberingPart.domNumberings);
|
|
2819
|
+
result.push(...await this.renderNumbering(document.numberingPart.domNumberings));
|
|
2820
|
+
}
|
|
2821
|
+
if (document.footnotesPart) this.footnoteMap = keyBy(document.footnotesPart.notes, (x) => x.id);
|
|
2822
|
+
if (document.endnotesPart) this.endnoteMap = keyBy(document.endnotesPart.notes, (x) => x.id);
|
|
2823
|
+
if (document.settingsPart) {
|
|
2824
|
+
this.defaultTabSize = document.settingsPart.settings?.defaultTabStop;
|
|
2825
|
+
const compatMode = document.settingsPart.settings?.compatMode;
|
|
2826
|
+
this.legacyCompat = compatMode != null && compatMode <= 11;
|
|
2827
|
+
}
|
|
2828
|
+
if (!options.ignoreFonts && document.fontTablePart) result.push(...await this.renderFontTable(document.fontTablePart));
|
|
2829
|
+
var sectionElements = this.renderSections(document.documentPart.body);
|
|
2830
|
+
if (this.options.inWrapper) result.push(this.renderWrapper(sectionElements));
|
|
2831
|
+
else result.push(...sectionElements);
|
|
2832
|
+
if (this.commentHighlight && options.renderComments) CSS.highlights.set(`${this.className}-comments`, this.commentHighlight);
|
|
2833
|
+
this.postRenderTasks.forEach((t) => t());
|
|
2834
|
+
await Promise.allSettled(this.tasks);
|
|
2835
|
+
this.refreshTabStops();
|
|
2836
|
+
return result;
|
|
2837
|
+
}
|
|
2838
|
+
renderTheme(themePart) {
|
|
2839
|
+
const variables = {};
|
|
2840
|
+
const fontScheme = themePart.theme?.fontScheme;
|
|
2841
|
+
if (fontScheme) {
|
|
2842
|
+
if (fontScheme.majorFont) {
|
|
2843
|
+
variables["--docx-majorHAnsi-font"] = fontScheme.majorFont.latinTypeface;
|
|
2844
|
+
variables["--docx-majorEastAsia-font"] = fontScheme.majorFont.eaTypeface || fontScheme.majorFont.latinTypeface;
|
|
2845
|
+
}
|
|
2846
|
+
if (fontScheme.minorFont) {
|
|
2847
|
+
variables["--docx-minorHAnsi-font"] = fontScheme.minorFont.latinTypeface;
|
|
2848
|
+
variables["--docx-minorEastAsia-font"] = fontScheme.minorFont.eaTypeface || fontScheme.minorFont.latinTypeface;
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
const colorScheme = themePart.theme?.colorScheme;
|
|
2852
|
+
if (colorScheme) for (let [k, v] of Object.entries(colorScheme.colors)) variables[`--docx-${k}-color`] = `#${v}`;
|
|
2853
|
+
const cssText = this.styleToString(`.${this.className}`, variables);
|
|
2854
|
+
return [this.h({
|
|
2855
|
+
tagName: "#comment",
|
|
2856
|
+
children: ["docxjs document theme values"]
|
|
2857
|
+
}), this.h({
|
|
2858
|
+
tagName: "style",
|
|
2859
|
+
children: [cssText]
|
|
2860
|
+
})];
|
|
2861
|
+
}
|
|
2862
|
+
async renderFontTable(fontsPart) {
|
|
2863
|
+
const result = [];
|
|
2864
|
+
for (let f of fontsPart.fonts) for (let ref of f.embedFontRefs) try {
|
|
2865
|
+
const fontData = await this.document.loadFont(ref.id, ref.key);
|
|
2866
|
+
const cssValues = {
|
|
2867
|
+
"font-family": encloseFontFamily(f.name),
|
|
2868
|
+
"src": `url(${fontData})`
|
|
2869
|
+
};
|
|
2870
|
+
if (ref.type == "bold" || ref.type == "boldItalic") cssValues["font-weight"] = "bold";
|
|
2871
|
+
if (ref.type == "italic" || ref.type == "boldItalic") cssValues["font-style"] = "italic";
|
|
2872
|
+
result.push(this.h({
|
|
2873
|
+
tagName: "#comment",
|
|
2874
|
+
children: [`docxjs ${f.name} font`]
|
|
2875
|
+
}));
|
|
2876
|
+
result.push(this.h({
|
|
2877
|
+
tagName: "style",
|
|
2878
|
+
children: [this.styleToString(`@font-face`, cssValues)]
|
|
2879
|
+
}));
|
|
2880
|
+
} catch (e) {
|
|
2881
|
+
if (this.options.debug) console.warn(`Can't load font with id ${ref.id} and key ${ref.key}`);
|
|
2882
|
+
}
|
|
2883
|
+
return result;
|
|
2884
|
+
}
|
|
2885
|
+
processStyleName(className) {
|
|
2886
|
+
return className ? `${this.className}_${escapeClassName(className)}` : this.className;
|
|
2887
|
+
}
|
|
2888
|
+
processStyles(styles) {
|
|
2889
|
+
const stylesMap = keyBy(styles.filter((x) => x.id != null), (x) => x.id);
|
|
2890
|
+
this.defaultParagraphStyle = styles.find((s) => s.isDefault && s.target == "p");
|
|
2891
|
+
this.docDefaultsStyle = styles.find((s) => s.id == null);
|
|
2892
|
+
for (const style of styles.filter((x) => x.basedOn)) {
|
|
2893
|
+
var baseStyle = stylesMap[style.basedOn];
|
|
2894
|
+
if (baseStyle) {
|
|
2895
|
+
style.paragraphProps = mergeDeep(style.paragraphProps, baseStyle.paragraphProps);
|
|
2896
|
+
style.runProps = mergeDeep(style.runProps, baseStyle.runProps);
|
|
2897
|
+
for (const baseValues of baseStyle.styles) {
|
|
2898
|
+
const styleValues = style.styles.find((x) => x.target == baseValues.target);
|
|
2899
|
+
if (styleValues) this.copyStyleProperties(baseValues.values, styleValues.values);
|
|
2900
|
+
else style.styles.push({
|
|
2901
|
+
...baseValues,
|
|
2902
|
+
values: { ...baseValues.values }
|
|
2903
|
+
});
|
|
2904
|
+
}
|
|
2905
|
+
} else if (this.options.debug) console.warn(`Can't find base style ${style.basedOn}`);
|
|
2906
|
+
}
|
|
2907
|
+
for (let style of styles) style.cssName = this.processStyleName(style.id);
|
|
2908
|
+
return stylesMap;
|
|
2909
|
+
}
|
|
2910
|
+
prodessNumberings(numberings) {
|
|
2911
|
+
for (let num of numberings.filter((n) => n.pStyleName)) {
|
|
2912
|
+
const style = this.findStyle(num.pStyleName);
|
|
2913
|
+
if (style?.paragraphProps?.numbering) style.paragraphProps.numbering.level = num.level;
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
processElement(element) {
|
|
2917
|
+
if (element.children) for (var e of element.children) {
|
|
2918
|
+
e.parent = element;
|
|
2919
|
+
if (e.type == DomType.Table) this.processTable(e);
|
|
2920
|
+
else this.processElement(e);
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
processTable(table) {
|
|
2924
|
+
for (var r of table.children) for (var c of r.children) {
|
|
2925
|
+
c.cssStyle = this.copyStyleProperties(table.cellStyle, c.cssStyle, [
|
|
2926
|
+
"border-left",
|
|
2927
|
+
"border-right",
|
|
2928
|
+
"border-top",
|
|
2929
|
+
"border-bottom",
|
|
2930
|
+
"padding-left",
|
|
2931
|
+
"padding-right",
|
|
2932
|
+
"padding-top",
|
|
2933
|
+
"padding-bottom"
|
|
2934
|
+
]);
|
|
2935
|
+
this.processElement(c);
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
copyStyleProperties(input, output, attrs = null) {
|
|
2939
|
+
if (!input) return output;
|
|
2940
|
+
if (output == null) output = {};
|
|
2941
|
+
if (attrs == null) attrs = Object.getOwnPropertyNames(input);
|
|
2942
|
+
for (var key of attrs) if (input.hasOwnProperty(key) && !output.hasOwnProperty(key)) output[key] = input[key];
|
|
2943
|
+
return output;
|
|
2944
|
+
}
|
|
2945
|
+
createPageElement(className, props, docStyle) {
|
|
2946
|
+
const style = { ...docStyle };
|
|
2947
|
+
if (props) {
|
|
2948
|
+
if (props.pageMargins) {
|
|
2949
|
+
style.paddingLeft = props.pageMargins.left;
|
|
2950
|
+
style.paddingRight = props.pageMargins.right;
|
|
2951
|
+
style.paddingTop = props.pageMargins.top;
|
|
2952
|
+
style.paddingBottom = props.pageMargins.bottom;
|
|
2953
|
+
style["--docx-pad-left"] = props.pageMargins.left;
|
|
2954
|
+
style["--docx-pad-right"] = props.pageMargins.right;
|
|
2955
|
+
style["--docx-pad-top"] = props.pageMargins.top;
|
|
2956
|
+
style["--docx-pad-bottom"] = props.pageMargins.bottom;
|
|
2957
|
+
}
|
|
2958
|
+
if (props.pageSize) {
|
|
2959
|
+
if (!this.options.ignoreWidth) style.width = props.pageSize.width;
|
|
2960
|
+
if (!this.options.ignoreHeight) style.minHeight = props.pageSize.height;
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
return this.h({
|
|
2964
|
+
tagName: "section",
|
|
2965
|
+
className,
|
|
2966
|
+
style
|
|
2967
|
+
});
|
|
2968
|
+
}
|
|
2969
|
+
createSectionContent(props) {
|
|
2970
|
+
const style = {};
|
|
2971
|
+
if (props.columns && props.columns.numberOfColumns) {
|
|
2972
|
+
style.columnCount = `${props.columns.numberOfColumns}`;
|
|
2973
|
+
style.columnGap = props.columns.space;
|
|
2974
|
+
if (props.columns.separator) style.columnRule = "1px solid black";
|
|
2975
|
+
}
|
|
2976
|
+
return this.h({
|
|
2977
|
+
tagName: "article",
|
|
2978
|
+
style
|
|
2979
|
+
});
|
|
2980
|
+
}
|
|
2981
|
+
renderSections(document) {
|
|
2982
|
+
const result = [];
|
|
2983
|
+
this.processElement(document);
|
|
2984
|
+
const sections = this.splitBySection(document.children, document.props);
|
|
2985
|
+
const pages = this.groupByPageBreaks(sections);
|
|
2986
|
+
let prevProps = null;
|
|
2987
|
+
for (let i = 0, l = pages.length; i < l; i++) {
|
|
2988
|
+
this.currentFootnoteIds = [];
|
|
2989
|
+
let props = pages[i][0].sectProps;
|
|
2990
|
+
const pageElement = this.createPageElement(this.className, props, document.cssStyle);
|
|
2991
|
+
this.options.renderHeaders && this.renderHeaderFooter(props.headerRefs, props, result.length, prevProps != props, pageElement);
|
|
2992
|
+
for (const sect of pages[i]) {
|
|
2993
|
+
var contentElement = this.createSectionContent(sect.sectProps);
|
|
2994
|
+
this.renderElements(sect.elements, contentElement);
|
|
2995
|
+
pageElement.appendChild(contentElement);
|
|
2996
|
+
props = sect.sectProps;
|
|
2997
|
+
}
|
|
2998
|
+
if (this.options.renderFootnotes) {
|
|
2999
|
+
const notes = this.renderNotes(this.currentFootnoteIds, this.footnoteMap);
|
|
3000
|
+
notes && pageElement.appendChild(notes);
|
|
3001
|
+
}
|
|
3002
|
+
if (this.options.renderEndnotes && i == l - 1) {
|
|
3003
|
+
const notes = this.renderNotes(this.currentEndnoteIds, this.endnoteMap);
|
|
3004
|
+
notes && pageElement.appendChild(notes);
|
|
3005
|
+
}
|
|
3006
|
+
this.options.renderFooters && this.renderHeaderFooter(props.footerRefs, props, result.length, prevProps != props, pageElement);
|
|
3007
|
+
result.push(pageElement);
|
|
3008
|
+
prevProps = props;
|
|
3009
|
+
}
|
|
3010
|
+
this.resolvePageFields(result);
|
|
3011
|
+
return result;
|
|
3012
|
+
}
|
|
3013
|
+
renderHeaderFooter(refs, props, page, firstOfSection, into) {
|
|
3014
|
+
if (!refs) return;
|
|
3015
|
+
var ref = (props.titlePage && firstOfSection ? refs.find((x) => x.type == "first") : null) ?? (page % 2 == 1 ? refs.find((x) => x.type == "even") : null) ?? refs.find((x) => x.type == "default");
|
|
3016
|
+
var part = ref && this.document.findPartByRelId(ref.id, this.document.documentPart);
|
|
3017
|
+
if (part) {
|
|
3018
|
+
this.currentPart = part;
|
|
3019
|
+
if (!this.usedHederFooterParts.includes(part.path)) {
|
|
3020
|
+
this.processElement(part.rootElement);
|
|
3021
|
+
this.usedHederFooterParts.push(part.path);
|
|
3022
|
+
}
|
|
3023
|
+
const [el] = this.renderElements([part.rootElement], into);
|
|
3024
|
+
if (props?.pageMargins) {
|
|
3025
|
+
if (part.rootElement.type === DomType.Header) {
|
|
3026
|
+
el.style.marginTop = `calc(${props.pageMargins.header} - ${props.pageMargins.top})`;
|
|
3027
|
+
el.style.minHeight = `calc(${props.pageMargins.top} - ${props.pageMargins.header})`;
|
|
3028
|
+
} else if (part.rootElement.type === DomType.Footer) {
|
|
3029
|
+
el.style.marginBottom = `calc(${props.pageMargins.footer} - ${props.pageMargins.bottom})`;
|
|
3030
|
+
el.style.minHeight = `calc(${props.pageMargins.bottom} - ${props.pageMargins.footer})`;
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
this.currentPart = null;
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
isPageBreakElement(elem) {
|
|
3037
|
+
if (elem.type != DomType.Break) return false;
|
|
3038
|
+
if (elem.break == "lastRenderedPageBreak") return !this.options.ignoreLastRenderedPageBreak;
|
|
3039
|
+
return elem.break == "page";
|
|
3040
|
+
}
|
|
3041
|
+
isPageBreakSection(prev, next) {
|
|
3042
|
+
if (!prev) return false;
|
|
3043
|
+
if (!next) return false;
|
|
3044
|
+
return prev.pageSize?.orientation != next.pageSize?.orientation || prev.pageSize?.width != next.pageSize?.width || prev.pageSize?.height != next.pageSize?.height;
|
|
3045
|
+
}
|
|
3046
|
+
splitBySection(elements, defaultProps) {
|
|
3047
|
+
var current = {
|
|
3048
|
+
sectProps: null,
|
|
3049
|
+
elements: [],
|
|
3050
|
+
pageBreak: false
|
|
3051
|
+
};
|
|
3052
|
+
var result = [current];
|
|
3053
|
+
for (let elem of elements) {
|
|
3054
|
+
if (elem.type == DomType.Paragraph) {
|
|
3055
|
+
if (this.findStyle(elem.styleName)?.paragraphProps?.pageBreakBefore) {
|
|
3056
|
+
current.sectProps = sectProps;
|
|
3057
|
+
current.pageBreak = true;
|
|
3058
|
+
current = {
|
|
3059
|
+
sectProps: null,
|
|
3060
|
+
elements: [],
|
|
3061
|
+
pageBreak: false
|
|
3062
|
+
};
|
|
3063
|
+
result.push(current);
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
current.elements.push(elem);
|
|
3067
|
+
if (elem.type == DomType.Paragraph) {
|
|
3068
|
+
const p = elem;
|
|
3069
|
+
var sectProps = p.sectionProps;
|
|
3070
|
+
var pBreakIndex = -1;
|
|
3071
|
+
var rBreakIndex = -1;
|
|
3072
|
+
if (this.options.breakPages && p.children) pBreakIndex = p.children.findIndex((r) => {
|
|
3073
|
+
rBreakIndex = r.children?.findIndex(this.isPageBreakElement.bind(this)) ?? -1;
|
|
3074
|
+
return rBreakIndex != -1;
|
|
3075
|
+
});
|
|
3076
|
+
if (sectProps || pBreakIndex != -1) {
|
|
3077
|
+
current.sectProps = sectProps;
|
|
3078
|
+
current.pageBreak = pBreakIndex != -1;
|
|
3079
|
+
current = {
|
|
3080
|
+
sectProps: null,
|
|
3081
|
+
elements: [],
|
|
3082
|
+
pageBreak: false
|
|
3083
|
+
};
|
|
3084
|
+
result.push(current);
|
|
3085
|
+
}
|
|
3086
|
+
if (pBreakIndex != -1) {
|
|
3087
|
+
let breakRun = p.children[pBreakIndex];
|
|
3088
|
+
let splitRun = rBreakIndex < breakRun.children.length - 1;
|
|
3089
|
+
if (pBreakIndex < p.children.length - 1 || splitRun) {
|
|
3090
|
+
var children = elem.children;
|
|
3091
|
+
var newParagraph = {
|
|
3092
|
+
...elem,
|
|
3093
|
+
children: children.slice(pBreakIndex)
|
|
3094
|
+
};
|
|
3095
|
+
elem.children = children.slice(0, pBreakIndex);
|
|
3096
|
+
current.elements.push(newParagraph);
|
|
3097
|
+
if (splitRun) {
|
|
3098
|
+
let runChildren = breakRun.children;
|
|
3099
|
+
let newRun = {
|
|
3100
|
+
...breakRun,
|
|
3101
|
+
children: runChildren.slice(0, rBreakIndex)
|
|
3102
|
+
};
|
|
3103
|
+
elem.children.push(newRun);
|
|
3104
|
+
breakRun.children = runChildren.slice(rBreakIndex);
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
let currentSectProps = null;
|
|
3111
|
+
for (let i = result.length - 1; i >= 0; i--) if (result[i].sectProps == null) result[i].sectProps = currentSectProps ?? defaultProps;
|
|
3112
|
+
else currentSectProps = result[i].sectProps;
|
|
3113
|
+
return result;
|
|
3114
|
+
}
|
|
3115
|
+
groupByPageBreaks(sections) {
|
|
3116
|
+
let current = [];
|
|
3117
|
+
let prev;
|
|
3118
|
+
const result = [current];
|
|
3119
|
+
for (let s of sections) {
|
|
3120
|
+
current.push(s);
|
|
3121
|
+
if (this.options.ignoreLastRenderedPageBreak || s.pageBreak || this.isPageBreakSection(prev, s.sectProps)) result.push(current = []);
|
|
3122
|
+
prev = s.sectProps;
|
|
3123
|
+
}
|
|
3124
|
+
return result.filter((x) => x.length > 0);
|
|
3125
|
+
}
|
|
3126
|
+
renderWrapper(children) {
|
|
3127
|
+
return this.h({
|
|
3128
|
+
tagName: "div",
|
|
3129
|
+
className: `${this.className}-wrapper`,
|
|
3130
|
+
children
|
|
3131
|
+
});
|
|
3132
|
+
}
|
|
3133
|
+
renderDefaultStyle() {
|
|
3134
|
+
var c = this.className;
|
|
3135
|
+
var wrapperStyle = `
|
|
3136
|
+
.${c}-wrapper { background: gray; padding: 30px; padding-bottom: 0px; display: flex; flex-flow: column; align-items: center; }
|
|
3137
|
+
.${c}-wrapper>section.${c} { background: white; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); margin-bottom: 30px; }`;
|
|
3138
|
+
if (this.options.hideWrapperOnPrint) wrapperStyle = `@media not print { ${wrapperStyle} }`;
|
|
3139
|
+
var styleText = `${wrapperStyle}
|
|
3140
|
+
.${c} { color: black; hyphens: auto; text-underline-position: from-font; }
|
|
3141
|
+
.${c} { --docx-font-ascii: serif; --docx-font-ea: serif; }
|
|
3142
|
+
.${c}, .${c} * { font-family: var(--docx-font-ascii), var(--docx-font-ea); }
|
|
3143
|
+
section.${c} { box-sizing: border-box; display: flex; flex-flow: column nowrap; position: relative; overflow: hidden; }
|
|
3144
|
+
section.${c}>article { margin-bottom: auto; z-index: 1; }
|
|
3145
|
+
section.${c}>footer { z-index: 1; }
|
|
3146
|
+
.${c} table { border-collapse: collapse; }
|
|
3147
|
+
.${c} table td, .${c} table th { vertical-align: top; }
|
|
3148
|
+
.${c} p { margin: 0pt; min-height: 1em; }
|
|
3149
|
+
.${c} span { white-space: pre-wrap; overflow-wrap: break-word; }
|
|
3150
|
+
.${c} a { color: inherit; text-decoration: inherit; }
|
|
3151
|
+
.${c} svg { fill: transparent; }
|
|
3152
|
+
`;
|
|
3153
|
+
if (this.options.renderComments) styleText += `
|
|
3154
|
+
.${c}-comment-ref { cursor: default; }
|
|
3155
|
+
.${c}-comment-popover { display: none; z-index: 1000; padding: 0.5rem; background: white; position: absolute; box-shadow: 0 0 0.25rem rgba(0, 0, 0, 0.25); width: 30ch; }
|
|
3156
|
+
.${c}-comment-ref:hover~.${c}-comment-popover { display: block; }
|
|
3157
|
+
.${c}-comment-author,.${c}-comment-date { font-size: 0.875rem; color: #888; }
|
|
3158
|
+
`;
|
|
3159
|
+
return [this.h({
|
|
3160
|
+
tagName: "#comment",
|
|
3161
|
+
children: ["docxjs library predefined styles"]
|
|
3162
|
+
}), this.h({
|
|
3163
|
+
tagName: "style",
|
|
3164
|
+
children: [styleText]
|
|
3165
|
+
})];
|
|
3166
|
+
}
|
|
3167
|
+
async renderNumbering(numberings) {
|
|
3168
|
+
var styleText = "";
|
|
3169
|
+
var resetCounters = [];
|
|
3170
|
+
for (var num of numberings) {
|
|
3171
|
+
var selector = `p.${this.numberingClass(num.id, num.level)}`;
|
|
3172
|
+
var listStyleType = "none";
|
|
3173
|
+
if (num.bullet) {
|
|
3174
|
+
let valiable = `--${this.className}-${num.bullet.src}`.toLowerCase();
|
|
3175
|
+
styleText += this.styleToString(`${selector}:before`, {
|
|
3176
|
+
"content": "' '",
|
|
3177
|
+
"display": "inline-block",
|
|
3178
|
+
"background": `var(${valiable})`
|
|
3179
|
+
}, num.bullet.style);
|
|
3180
|
+
try {
|
|
3181
|
+
const imgData = await this.document.loadNumberingImage(num.bullet.src);
|
|
3182
|
+
styleText += `${this.rootSelector} { ${valiable}: url(${imgData}) }`;
|
|
3183
|
+
} catch (e) {
|
|
3184
|
+
if (this.options.debug) console.warn(`Can't load numbering image with src ${num.bullet.src}`);
|
|
3185
|
+
}
|
|
3186
|
+
} else if (num.levelText) {
|
|
3187
|
+
let counter = this.numberingCounter(num.id, num.level);
|
|
3188
|
+
const counterReset = counter + " " + (num.start - 1);
|
|
3189
|
+
if (num.level > 0) styleText += this.styleToString(`p.${this.numberingClass(num.id, num.level - 1)}`, { "counter-set": counterReset });
|
|
3190
|
+
resetCounters.push(counterReset);
|
|
3191
|
+
styleText += this.styleToString(`${selector}:before`, {
|
|
3192
|
+
"content": this.levelTextToContent(num.levelText, num.suff, num.id, this.numFormatToCssValue(num.format)),
|
|
3193
|
+
"counter-increment": counter,
|
|
3194
|
+
...num.rStyle
|
|
3195
|
+
});
|
|
3196
|
+
} else listStyleType = this.numFormatToCssValue(num.format);
|
|
3197
|
+
styleText += this.styleToString(selector, {
|
|
3198
|
+
"display": "list-item",
|
|
3199
|
+
"list-style-position": "inside",
|
|
3200
|
+
"list-style-type": listStyleType,
|
|
3201
|
+
...num.pStyle
|
|
3202
|
+
});
|
|
3203
|
+
}
|
|
3204
|
+
if (resetCounters.length > 0) styleText += this.styleToString(this.rootSelector, { "counter-reset": resetCounters.join(" ") });
|
|
3205
|
+
return [this.h({
|
|
3206
|
+
tagName: "#comment",
|
|
3207
|
+
children: ["docxjs document numbering styles"]
|
|
3208
|
+
}), this.h({
|
|
3209
|
+
tagName: "style",
|
|
3210
|
+
children: [styleText]
|
|
3211
|
+
})];
|
|
3212
|
+
}
|
|
3213
|
+
renderStyles(styles) {
|
|
3214
|
+
var styleText = "";
|
|
3215
|
+
const stylesMap = this.styleMap;
|
|
3216
|
+
const defautStyles = keyBy(styles.filter((s) => s.isDefault), (s) => s.target);
|
|
3217
|
+
for (const style of styles) {
|
|
3218
|
+
var subStyles = style.styles;
|
|
3219
|
+
if (style.linked) {
|
|
3220
|
+
var linkedStyle = style.linked && stylesMap[style.linked];
|
|
3221
|
+
if (linkedStyle) subStyles = subStyles.concat(linkedStyle.styles);
|
|
3222
|
+
else if (this.options.debug) console.warn(`Can't find linked style ${style.linked}`);
|
|
3223
|
+
}
|
|
3224
|
+
for (const subStyle of subStyles) {
|
|
3225
|
+
var selector = `${style.target ?? ""}.${style.cssName}`;
|
|
3226
|
+
if (style.target != subStyle.target) selector += ` ${subStyle.target}`;
|
|
3227
|
+
if (style.id == null && subStyle.target == "span") selector = `.${this.className}`;
|
|
3228
|
+
if (defautStyles[style.target] == style) selector = `.${this.className} ${style.target}, ` + selector;
|
|
3229
|
+
styleText += this.styleToString(selector, subStyle.values);
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
return [this.h({
|
|
3233
|
+
tagName: "#comment",
|
|
3234
|
+
children: ["docxjs document styles"]
|
|
3235
|
+
}), this.h({
|
|
3236
|
+
tagName: "style",
|
|
3237
|
+
children: [styleText]
|
|
3238
|
+
})];
|
|
3239
|
+
}
|
|
3240
|
+
renderNotes(noteIds, notesMap) {
|
|
3241
|
+
var notes = noteIds.map((id) => notesMap[id]).filter((x) => x);
|
|
3242
|
+
if (notes.length > 0) return this.h({
|
|
3243
|
+
tagName: "ol",
|
|
3244
|
+
children: this.renderElements(notes)
|
|
3245
|
+
});
|
|
3246
|
+
}
|
|
3247
|
+
renderElement(elem) {
|
|
3248
|
+
switch (elem.type) {
|
|
3249
|
+
case DomType.Paragraph: return this.renderParagraph(elem);
|
|
3250
|
+
case DomType.BookmarkStart: return this.renderBookmarkStart(elem);
|
|
3251
|
+
case DomType.BookmarkEnd: return null;
|
|
3252
|
+
case DomType.Run: return this.renderRun(elem);
|
|
3253
|
+
case DomType.Table: return this.renderTable(elem);
|
|
3254
|
+
case DomType.Row: return this.renderTableRow(elem);
|
|
3255
|
+
case DomType.Cell: return this.renderTableCell(elem);
|
|
3256
|
+
case DomType.Hyperlink: return this.renderHyperlink(elem);
|
|
3257
|
+
case DomType.SmartTag: return this.renderSmartTag(elem);
|
|
3258
|
+
case DomType.Drawing: return this.renderDrawing(elem);
|
|
3259
|
+
case DomType.Image: return this.renderImage(elem);
|
|
3260
|
+
case DomType.Text: return this.renderText(elem);
|
|
3261
|
+
case DomType.Text: return this.renderText(elem);
|
|
3262
|
+
case DomType.DeletedText: return this.renderDeletedText(elem);
|
|
3263
|
+
case DomType.Tab: return this.renderTab(elem);
|
|
3264
|
+
case DomType.Symbol: return this.renderSymbol(elem);
|
|
3265
|
+
case DomType.Break: return this.renderBreak(elem);
|
|
3266
|
+
case DomType.Footer: return this.renderContainer(elem, "footer");
|
|
3267
|
+
case DomType.Header: return this.renderContainer(elem, "header");
|
|
3268
|
+
case DomType.Footnote:
|
|
3269
|
+
case DomType.Endnote: return this.renderContainer(elem, "li");
|
|
3270
|
+
case DomType.FootnoteReference: return this.renderFootnoteReference(elem);
|
|
3271
|
+
case DomType.EndnoteReference: return this.renderEndnoteReference(elem);
|
|
3272
|
+
case DomType.NoBreakHyphen: return this.h({ tagName: "wbr" });
|
|
3273
|
+
case DomType.VmlPicture: return this.renderVmlPicture(elem);
|
|
3274
|
+
case DomType.VmlElement: return this.renderVmlElement(elem);
|
|
3275
|
+
case DomType.MmlMath: return this.renderContainerNS(elem, ns.mathML, "math", { xmlns: ns.mathML });
|
|
3276
|
+
case DomType.MmlMathParagraph: return this.renderContainer(elem, "span");
|
|
3277
|
+
case DomType.MmlFraction: return this.renderContainerNS(elem, ns.mathML, "mfrac");
|
|
3278
|
+
case DomType.MmlBase: return this.renderContainerNS(elem, ns.mathML, elem.parent.type == DomType.MmlMatrixRow ? "mtd" : "mrow");
|
|
3279
|
+
case DomType.MmlNumerator:
|
|
3280
|
+
case DomType.MmlDenominator:
|
|
3281
|
+
case DomType.MmlFunction:
|
|
3282
|
+
case DomType.MmlLimit:
|
|
3283
|
+
case DomType.MmlBox: return this.renderContainerNS(elem, ns.mathML, "mrow");
|
|
3284
|
+
case DomType.MmlGroupChar: return this.renderMmlGroupChar(elem);
|
|
3285
|
+
case DomType.MmlLimitLower: return this.renderContainerNS(elem, ns.mathML, "munder");
|
|
3286
|
+
case DomType.MmlMatrix: return this.renderContainerNS(elem, ns.mathML, "mtable");
|
|
3287
|
+
case DomType.MmlMatrixRow: return this.renderContainerNS(elem, ns.mathML, "mtr");
|
|
3288
|
+
case DomType.MmlRadical: return this.renderMmlRadical(elem);
|
|
3289
|
+
case DomType.MmlSuperscript: return this.renderContainerNS(elem, ns.mathML, "msup");
|
|
3290
|
+
case DomType.MmlSubscript: return this.renderContainerNS(elem, ns.mathML, "msub");
|
|
3291
|
+
case DomType.MmlDegree:
|
|
3292
|
+
case DomType.MmlSuperArgument:
|
|
3293
|
+
case DomType.MmlSubArgument: return this.renderContainerNS(elem, ns.mathML, "mn");
|
|
3294
|
+
case DomType.MmlFunctionName: return this.renderContainerNS(elem, ns.mathML, "ms");
|
|
3295
|
+
case DomType.MmlDelimiter: return this.renderMmlDelimiter(elem);
|
|
3296
|
+
case DomType.MmlRun: return this.renderMmlRun(elem);
|
|
3297
|
+
case DomType.MmlNary: return this.renderMmlNary(elem);
|
|
3298
|
+
case DomType.MmlPreSubSuper: return this.renderMmlPreSubSuper(elem);
|
|
3299
|
+
case DomType.MmlBar: return this.renderMmlBar(elem);
|
|
3300
|
+
case DomType.MmlEquationArray: return this.renderMllList(elem);
|
|
3301
|
+
case DomType.Inserted: return this.renderInserted(elem);
|
|
3302
|
+
case DomType.Deleted: return this.renderDeleted(elem);
|
|
3303
|
+
case DomType.CommentRangeStart: return this.renderCommentRangeStart(elem);
|
|
3304
|
+
case DomType.CommentRangeEnd: return this.renderCommentRangeEnd(elem);
|
|
3305
|
+
case DomType.CommentReference: return this.renderCommentReference(elem);
|
|
3306
|
+
case DomType.AltChunk: return this.renderAltChunk(elem);
|
|
3307
|
+
}
|
|
3308
|
+
return null;
|
|
3309
|
+
}
|
|
3310
|
+
renderElements(elems, into) {
|
|
3311
|
+
if (elems == null) return null;
|
|
3312
|
+
var result = elems.flatMap((e) => this.renderElement(e)).filter((e) => e != null);
|
|
3313
|
+
if (into) result.forEach((c) => into.appendChild(isString(c) ? document.createTextNode(c) : c));
|
|
3314
|
+
return result;
|
|
3315
|
+
}
|
|
3316
|
+
renderContainer(elem, tagName, props) {
|
|
3317
|
+
return this.h({
|
|
3318
|
+
tagName,
|
|
3319
|
+
children: this.renderElements(elem.children),
|
|
3320
|
+
...props
|
|
3321
|
+
});
|
|
3322
|
+
}
|
|
3323
|
+
renderContainerNS(elem, ns, tagName, props) {
|
|
3324
|
+
return this.h({
|
|
3325
|
+
ns,
|
|
3326
|
+
tagName,
|
|
3327
|
+
children: this.renderElements(elem.children),
|
|
3328
|
+
...props
|
|
3329
|
+
});
|
|
3330
|
+
}
|
|
3331
|
+
/**
|
|
3332
|
+
* Marks the cached-result runs of PAGE / NUMPAGES complex fields with a
|
|
3333
|
+
* marker class (`docx-field-page` / `docx-field-numpages`), so the actual
|
|
3334
|
+
* numbers can be substituted once page layout is final (see the end of
|
|
3335
|
+
* renderSections and pagination.ts). Field runs themselves are skipped
|
|
3336
|
+
* by renderRun, leaving the cached value as a plain run between the
|
|
3337
|
+
* separate and end markers.
|
|
3338
|
+
*/
|
|
3339
|
+
markPageFieldRuns(elem) {
|
|
3340
|
+
let instruction = null;
|
|
3341
|
+
let inResult = false;
|
|
3342
|
+
for (const run of elem.children ?? []) {
|
|
3343
|
+
if (run.type != DomType.Run) continue;
|
|
3344
|
+
for (const sub of run.children ?? []) if (sub.type == DomType.ComplexField) {
|
|
3345
|
+
const charType = sub.charType;
|
|
3346
|
+
if (charType == "begin") {
|
|
3347
|
+
instruction = null;
|
|
3348
|
+
inResult = false;
|
|
3349
|
+
} else if (charType == "separate") inResult = true;
|
|
3350
|
+
else if (charType == "end") {
|
|
3351
|
+
instruction = null;
|
|
3352
|
+
inResult = false;
|
|
3353
|
+
}
|
|
3354
|
+
} else if (sub.type == DomType.Instruction) instruction = sub.text?.trim().split(/\s/)[0]?.toUpperCase();
|
|
3355
|
+
if (inResult && (instruction == "PAGE" || instruction == "NUMPAGES") && !run.fieldRun) run.className = cx(run.className, `${this.className}-field-${instruction.toLowerCase()}`);
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
/**
|
|
3359
|
+
* Substitutes final page numbers into PAGE / NUMPAGES field markers
|
|
3360
|
+
* across the given page sections. Numbering is continuous from 1 —
|
|
3361
|
+
* per-section restarts (w:pgNumType) are not modeled yet.
|
|
3362
|
+
*/
|
|
3363
|
+
resolvePageFields(pages) {
|
|
3364
|
+
const total = `${pages.length}`;
|
|
3365
|
+
pages.forEach((page, i) => {
|
|
3366
|
+
for (const el of Array.from(page.querySelectorAll(`.${this.className}-field-page`))) el.textContent = `${i + 1}`;
|
|
3367
|
+
for (const el of Array.from(page.querySelectorAll(`.${this.className}-field-numpages`))) el.textContent = total;
|
|
3368
|
+
});
|
|
3369
|
+
}
|
|
3370
|
+
renderParagraph(elem) {
|
|
3371
|
+
this.markPageFieldRuns(elem);
|
|
3372
|
+
var result = this.toHTML(elem, ns.html, "p");
|
|
3373
|
+
const style = this.findStyle(elem.styleName);
|
|
3374
|
+
elem.tabs ?? (elem.tabs = style?.paragraphProps?.tabs);
|
|
3375
|
+
const numbering = elem.numbering ?? style?.paragraphProps?.numbering;
|
|
3376
|
+
if (numbering) result.classList.add(this.numberingClass(numbering.id, numbering.level));
|
|
3377
|
+
if (this.legacyCompat && this.textAlignOf(elem) === "justify" && / {2,}| {2,}| /.test(result.textContent)) result.style.setProperty("text-align-last", "justify");
|
|
3378
|
+
return result;
|
|
3379
|
+
}
|
|
3380
|
+
/** Effective text-align of a paragraph: direct formatting, then its
|
|
3381
|
+
* style, then the default paragraph style, then docDefaults. */
|
|
3382
|
+
textAlignOf(elem) {
|
|
3383
|
+
if (elem.cssStyle?.["text-align"]) return elem.cssStyle["text-align"];
|
|
3384
|
+
const from = (s) => s?.styles?.find((x) => x.target == "p")?.values?.["text-align"];
|
|
3385
|
+
return from(this.findStyle(elem.styleName)) ?? from(this.defaultParagraphStyle) ?? from(this.docDefaultsStyle) ?? null;
|
|
3386
|
+
}
|
|
3387
|
+
renderHyperlink(elem) {
|
|
3388
|
+
const res = this.toH(elem, ns.html, "a");
|
|
3389
|
+
res.href = "";
|
|
3390
|
+
if (elem.id) res.href = this.document.documentPart.rels.find((it) => it.id == elem.id && it.targetMode === "External")?.target ?? res.href;
|
|
3391
|
+
if (elem.anchor) res.href += `#${elem.anchor}`;
|
|
3392
|
+
return this.h(res);
|
|
3393
|
+
}
|
|
3394
|
+
renderSmartTag(elem) {
|
|
3395
|
+
return this.renderContainer(elem, "span");
|
|
3396
|
+
}
|
|
3397
|
+
renderCommentRangeStart(commentStart) {
|
|
3398
|
+
if (!this.options.renderComments) return null;
|
|
3399
|
+
const rng = new Range();
|
|
3400
|
+
this.commentHighlight?.add(rng);
|
|
3401
|
+
const result = this.h({
|
|
3402
|
+
tagName: "#comment",
|
|
3403
|
+
children: [`start of comment #${commentStart.id}`]
|
|
3404
|
+
});
|
|
3405
|
+
this.later(() => rng.setStart(result, 0));
|
|
3406
|
+
this.commentMap[commentStart.id] = rng;
|
|
3407
|
+
return result;
|
|
3408
|
+
}
|
|
3409
|
+
renderCommentRangeEnd(commentEnd) {
|
|
3410
|
+
if (!this.options.renderComments) return null;
|
|
3411
|
+
const rng = this.commentMap[commentEnd.id];
|
|
3412
|
+
const result = this.h({
|
|
3413
|
+
tagName: "#comment",
|
|
3414
|
+
children: [`end of comment #${commentEnd.id}`]
|
|
3415
|
+
});
|
|
3416
|
+
this.later(() => rng?.setEnd(result, 0));
|
|
3417
|
+
return result;
|
|
3418
|
+
}
|
|
3419
|
+
renderCommentReference(commentRef) {
|
|
3420
|
+
if (!this.options.renderComments) return null;
|
|
3421
|
+
var comment = this.document.commentsPart?.commentMap[commentRef.id];
|
|
3422
|
+
if (!comment) return null;
|
|
3423
|
+
const commentRefEl = this.h({
|
|
3424
|
+
tagName: "span",
|
|
3425
|
+
className: `${this.className}-comment-ref`,
|
|
3426
|
+
children: ["💬"]
|
|
3427
|
+
});
|
|
3428
|
+
const commentsContainerEl = this.h({
|
|
3429
|
+
tagName: "div",
|
|
3430
|
+
className: `${this.className}-comment-popover`,
|
|
3431
|
+
children: [
|
|
3432
|
+
this.h({
|
|
3433
|
+
tagName: "div",
|
|
3434
|
+
className: `${this.className}-comment-author`,
|
|
3435
|
+
children: [comment.author]
|
|
3436
|
+
}),
|
|
3437
|
+
this.h({
|
|
3438
|
+
tagName: "div",
|
|
3439
|
+
className: `${this.className}-comment-date`,
|
|
3440
|
+
children: [new Date(comment.date).toLocaleString()]
|
|
3441
|
+
}),
|
|
3442
|
+
...this.renderElements(comment.children)
|
|
3443
|
+
]
|
|
3444
|
+
});
|
|
3445
|
+
return this.h({
|
|
3446
|
+
tagName: "#fragment",
|
|
3447
|
+
children: [
|
|
3448
|
+
this.h({
|
|
3449
|
+
tagName: "#comment",
|
|
3450
|
+
children: [`comment #${comment.id} by ${comment.author} on ${comment.date}`]
|
|
3451
|
+
}),
|
|
3452
|
+
commentRefEl,
|
|
3453
|
+
commentsContainerEl
|
|
3454
|
+
]
|
|
3455
|
+
});
|
|
3456
|
+
}
|
|
3457
|
+
renderAltChunk(elem) {
|
|
3458
|
+
if (!this.options.renderAltChunks) return null;
|
|
3459
|
+
var result = this.h({ tagName: "iframe" });
|
|
3460
|
+
this.tasks.push(this.document.loadAltChunk(elem.id, this.currentPart).then((x) => {
|
|
3461
|
+
result.srcdoc = x;
|
|
3462
|
+
}));
|
|
3463
|
+
return result;
|
|
3464
|
+
}
|
|
3465
|
+
renderDrawing(elem) {
|
|
3466
|
+
var result = this.toHTML(elem, ns.html, "div");
|
|
3467
|
+
result.style.display = "inline-block";
|
|
3468
|
+
result.style.position = "relative";
|
|
3469
|
+
result.style.textIndent = "0px";
|
|
3470
|
+
return result;
|
|
3471
|
+
}
|
|
3472
|
+
renderImage(elem) {
|
|
3473
|
+
let result = this.toHTML(elem, ns.html, "img", []);
|
|
3474
|
+
let transform = elem.cssStyle?.transform;
|
|
3475
|
+
if (elem.srcRect && elem.srcRect.some((x) => x != 0)) {
|
|
3476
|
+
var [left, top, right, bottom] = elem.srcRect;
|
|
3477
|
+
transform = `scale(${1 / (1 - left - right)}, ${1 / (1 - top - bottom)})`;
|
|
3478
|
+
result.style["clip-path"] = `rect(${(100 * top).toFixed(2)}% ${(100 * (1 - right)).toFixed(2)}% ${(100 * (1 - bottom)).toFixed(2)}% ${(100 * left).toFixed(2)}%)`;
|
|
3479
|
+
}
|
|
3480
|
+
if (elem.rotation) transform = `rotate(${elem.rotation}deg) ${transform ?? ""}`;
|
|
3481
|
+
result.style.transform = transform?.trim();
|
|
3482
|
+
if (this.document) this.tasks.push(this.document.loadDocumentImage(elem.src, this.currentPart).then((x) => {
|
|
3483
|
+
result.src = x;
|
|
3484
|
+
}));
|
|
3485
|
+
return result;
|
|
3486
|
+
}
|
|
3487
|
+
renderText(elem) {
|
|
3488
|
+
return this.h(elem.text);
|
|
3489
|
+
}
|
|
3490
|
+
renderDeletedText(elem) {
|
|
3491
|
+
return this.options.renderChanges ? this.renderText(elem) : null;
|
|
3492
|
+
}
|
|
3493
|
+
renderBreak(elem) {
|
|
3494
|
+
return elem.break == "textWrapping" ? this.h({ tagName: "br" }) : null;
|
|
3495
|
+
}
|
|
3496
|
+
renderInserted(elem) {
|
|
3497
|
+
if (this.options.renderChanges) return this.renderChange(elem, "ins");
|
|
3498
|
+
return this.renderElements(elem.children);
|
|
3499
|
+
}
|
|
3500
|
+
renderDeleted(elem) {
|
|
3501
|
+
if (this.options.renderChanges) return this.renderChange(elem, "del");
|
|
3502
|
+
return null;
|
|
3503
|
+
}
|
|
3504
|
+
renderChange(elem, tag) {
|
|
3505
|
+
return this.renderContainer(elem, tag, { dateTime: elem.date });
|
|
3506
|
+
}
|
|
3507
|
+
renderSymbol(elem) {
|
|
3508
|
+
return this.h({
|
|
3509
|
+
tagName: "span",
|
|
3510
|
+
children: [String.fromCharCode(elem.char)],
|
|
3511
|
+
style: { fontFamily: elem.font }
|
|
3512
|
+
});
|
|
3513
|
+
}
|
|
3514
|
+
renderFootnoteReference(elem) {
|
|
3515
|
+
this.currentFootnoteIds.push(elem.id);
|
|
3516
|
+
return this.h({
|
|
3517
|
+
tagName: "sup",
|
|
3518
|
+
children: [`${this.currentFootnoteIds.length}`]
|
|
3519
|
+
});
|
|
3520
|
+
}
|
|
3521
|
+
renderEndnoteReference(elem) {
|
|
3522
|
+
this.currentEndnoteIds.push(elem.id);
|
|
3523
|
+
return this.h({
|
|
3524
|
+
tagName: "sup",
|
|
3525
|
+
children: [`${this.currentEndnoteIds.length}`]
|
|
3526
|
+
});
|
|
3527
|
+
}
|
|
3528
|
+
renderTab(elem) {
|
|
3529
|
+
var tabSpan = this.h({
|
|
3530
|
+
tagName: "span",
|
|
3531
|
+
children: [" "]
|
|
3532
|
+
});
|
|
3533
|
+
if (this.options.experimental) {
|
|
3534
|
+
tabSpan.className = this.tabStopClass();
|
|
3535
|
+
var stops = findParent(elem, DomType.Paragraph)?.tabs;
|
|
3536
|
+
this.currentTabs.push({
|
|
3537
|
+
stops,
|
|
3538
|
+
span: tabSpan
|
|
3539
|
+
});
|
|
3540
|
+
}
|
|
3541
|
+
return tabSpan;
|
|
3542
|
+
}
|
|
3543
|
+
renderBookmarkStart(elem) {
|
|
3544
|
+
return this.h({
|
|
3545
|
+
tagName: "span",
|
|
3546
|
+
id: elem.name
|
|
3547
|
+
});
|
|
3548
|
+
}
|
|
3549
|
+
renderRun(elem) {
|
|
3550
|
+
if (elem.fieldRun) return null;
|
|
3551
|
+
let children = this.renderElements(elem.children);
|
|
3552
|
+
if (elem.verticalAlign) children = [this.h({
|
|
3553
|
+
tagName: elem.verticalAlign,
|
|
3554
|
+
children: this.renderElements(elem.children)
|
|
3555
|
+
})];
|
|
3556
|
+
const result = this.toHTML(elem, ns.html, "span", children);
|
|
3557
|
+
if (elem.id) result.id = elem.id;
|
|
3558
|
+
return result;
|
|
3559
|
+
}
|
|
3560
|
+
renderTable(elem) {
|
|
3561
|
+
this.tableCellPositions.push(this.currentCellPosition);
|
|
3562
|
+
this.tableVerticalMerges.push(this.currentVerticalMerge);
|
|
3563
|
+
this.currentVerticalMerge = {};
|
|
3564
|
+
this.currentCellPosition = {
|
|
3565
|
+
col: 0,
|
|
3566
|
+
row: 0
|
|
3567
|
+
};
|
|
3568
|
+
const children = [];
|
|
3569
|
+
if (elem.columns) children.push(this.renderTableColumns(elem.columns));
|
|
3570
|
+
children.push(...this.renderElements(elem.children));
|
|
3571
|
+
this.currentVerticalMerge = this.tableVerticalMerges.pop();
|
|
3572
|
+
this.currentCellPosition = this.tableCellPositions.pop();
|
|
3573
|
+
return this.toHTML(elem, ns.html, "table", children);
|
|
3574
|
+
}
|
|
3575
|
+
renderTableColumns(columns) {
|
|
3576
|
+
const children = columns.map((x) => this.h({
|
|
3577
|
+
tagName: "col",
|
|
3578
|
+
style: { width: x.width }
|
|
3579
|
+
}));
|
|
3580
|
+
return this.h({
|
|
3581
|
+
tagName: "colgroup",
|
|
3582
|
+
children
|
|
3583
|
+
});
|
|
3584
|
+
}
|
|
3585
|
+
renderTableRow(elem) {
|
|
3586
|
+
this.currentCellPosition.col = 0;
|
|
3587
|
+
const children = [];
|
|
3588
|
+
if (elem.gridBefore) children.push(this.renderTableCellPlaceholder(elem.gridBefore));
|
|
3589
|
+
children.push(...this.renderElements(elem.children));
|
|
3590
|
+
if (elem.gridAfter) children.push(this.renderTableCellPlaceholder(elem.gridAfter));
|
|
3591
|
+
this.currentCellPosition.row++;
|
|
3592
|
+
return this.toHTML(elem, ns.html, "tr", children);
|
|
3593
|
+
}
|
|
3594
|
+
renderTableCellPlaceholder(colSpan) {
|
|
3595
|
+
return this.h({
|
|
3596
|
+
tagName: "td",
|
|
3597
|
+
colSpan,
|
|
3598
|
+
style: { border: "none" }
|
|
3599
|
+
});
|
|
3600
|
+
}
|
|
3601
|
+
renderTableCell(elem) {
|
|
3602
|
+
let result = this.toHTML(elem, ns.html, "td");
|
|
3603
|
+
const key = this.currentCellPosition.col;
|
|
3604
|
+
if (elem.verticalMerge) {
|
|
3605
|
+
if (elem.verticalMerge == "restart") {
|
|
3606
|
+
this.currentVerticalMerge[key] = result;
|
|
3607
|
+
result.rowSpan = 1;
|
|
3608
|
+
} else if (this.currentVerticalMerge[key]) {
|
|
3609
|
+
this.currentVerticalMerge[key].rowSpan += 1;
|
|
3610
|
+
result.style.display = "none";
|
|
3611
|
+
}
|
|
3612
|
+
} else this.currentVerticalMerge[key] = null;
|
|
3613
|
+
if (elem.span) result.colSpan = elem.span;
|
|
3614
|
+
this.currentCellPosition.col += result.colSpan;
|
|
3615
|
+
return result;
|
|
3616
|
+
}
|
|
3617
|
+
renderVmlPicture(elem) {
|
|
3618
|
+
return this.renderContainer(elem, "div");
|
|
3619
|
+
}
|
|
3620
|
+
renderVmlElement(elem) {
|
|
3621
|
+
var container = this.h({
|
|
3622
|
+
ns: ns.svg,
|
|
3623
|
+
tagName: "svg",
|
|
3624
|
+
style: elem.cssStyleText
|
|
3625
|
+
});
|
|
3626
|
+
const result = this.renderVmlChildElement(elem);
|
|
3627
|
+
if (elem.imageHref?.id) this.tasks.push(this.document?.loadDocumentImage(elem.imageHref.id, this.currentPart).then((x) => result.setAttribute("href", x)));
|
|
3628
|
+
container.appendChild(result);
|
|
3629
|
+
requestAnimationFrame(() => {
|
|
3630
|
+
const bb = container.firstElementChild.getBBox();
|
|
3631
|
+
container.setAttribute("width", `${Math.ceil(bb.x + bb.width)}`);
|
|
3632
|
+
container.setAttribute("height", `${Math.ceil(bb.y + bb.height)}`);
|
|
3633
|
+
});
|
|
3634
|
+
return container;
|
|
3635
|
+
}
|
|
3636
|
+
renderVmlChildElement(elem) {
|
|
3637
|
+
const result = this.createSvgElement(elem.tagName);
|
|
3638
|
+
Object.entries(elem.attrs).forEach(([k, v]) => result.setAttribute(k, v));
|
|
3639
|
+
for (let child of elem.children) if (child.type == DomType.VmlElement) result.appendChild(this.renderVmlChildElement(child));
|
|
3640
|
+
else result.appendChild(...asArray(this.renderElement(child)));
|
|
3641
|
+
return result;
|
|
3642
|
+
}
|
|
3643
|
+
renderMmlRadical(elem) {
|
|
3644
|
+
const base = elem.children.find((el) => el.type == DomType.MmlBase);
|
|
3645
|
+
if (elem.props?.hideDegree) return this.createMathMLElement("msqrt", null, this.renderElements([base]));
|
|
3646
|
+
const degree = elem.children.find((el) => el.type == DomType.MmlDegree);
|
|
3647
|
+
return this.createMathMLElement("mroot", null, this.renderElements([base, degree]));
|
|
3648
|
+
}
|
|
3649
|
+
renderMmlDelimiter(elem) {
|
|
3650
|
+
const children = [];
|
|
3651
|
+
children.push(this.createMathMLElement("mo", null, [elem.props.beginChar ?? "("]));
|
|
3652
|
+
children.push(...this.renderElements(elem.children));
|
|
3653
|
+
children.push(this.createMathMLElement("mo", null, [elem.props.endChar ?? ")"]));
|
|
3654
|
+
return this.createMathMLElement("mrow", null, children);
|
|
3655
|
+
}
|
|
3656
|
+
renderMmlNary(elem) {
|
|
3657
|
+
const children = [];
|
|
3658
|
+
const grouped = keyBy(elem.children, (x) => x.type);
|
|
3659
|
+
const sup = grouped[DomType.MmlSuperArgument];
|
|
3660
|
+
const sub = grouped[DomType.MmlSubArgument];
|
|
3661
|
+
const supElem = sup ? this.createMathMLElement("mo", null, asArray(this.renderElement(sup))) : null;
|
|
3662
|
+
const subElem = sub ? this.createMathMLElement("mo", null, asArray(this.renderElement(sub))) : null;
|
|
3663
|
+
const charElem = this.createMathMLElement("mo", null, [elem.props?.char ?? "∫"]);
|
|
3664
|
+
if (supElem || subElem) children.push(this.createMathMLElement("munderover", null, [
|
|
3665
|
+
charElem,
|
|
3666
|
+
subElem,
|
|
3667
|
+
supElem
|
|
3668
|
+
]));
|
|
3669
|
+
else if (supElem) children.push(this.createMathMLElement("mover", null, [charElem, supElem]));
|
|
3670
|
+
else if (subElem) children.push(this.createMathMLElement("munder", null, [charElem, subElem]));
|
|
3671
|
+
else children.push(charElem);
|
|
3672
|
+
children.push(...this.renderElements(grouped[DomType.MmlBase].children));
|
|
3673
|
+
return this.createMathMLElement("mrow", null, children);
|
|
3674
|
+
}
|
|
3675
|
+
renderMmlPreSubSuper(elem) {
|
|
3676
|
+
const children = [];
|
|
3677
|
+
const grouped = keyBy(elem.children, (x) => x.type);
|
|
3678
|
+
const sup = grouped[DomType.MmlSuperArgument];
|
|
3679
|
+
const sub = grouped[DomType.MmlSubArgument];
|
|
3680
|
+
const supElem = sup ? this.createMathMLElement("mo", null, asArray(this.renderElement(sup))) : null;
|
|
3681
|
+
const subElem = sub ? this.createMathMLElement("mo", null, asArray(this.renderElement(sub))) : null;
|
|
3682
|
+
const stubElem = this.createMathMLElement("mo", null);
|
|
3683
|
+
children.push(this.createMathMLElement("msubsup", null, [
|
|
3684
|
+
stubElem,
|
|
3685
|
+
subElem,
|
|
3686
|
+
supElem
|
|
3687
|
+
]));
|
|
3688
|
+
children.push(...this.renderElements(grouped[DomType.MmlBase].children));
|
|
3689
|
+
return this.createMathMLElement("mrow", null, children);
|
|
3690
|
+
}
|
|
3691
|
+
renderMmlGroupChar(elem) {
|
|
3692
|
+
const tagName = elem.props.verticalJustification === "bot" ? "mover" : "munder";
|
|
3693
|
+
const result = this.renderContainerNS(elem, ns.mathML, tagName);
|
|
3694
|
+
if (elem.props.char) result.appendChild(this.createMathMLElement("mo", null, [elem.props.char]));
|
|
3695
|
+
return result;
|
|
3696
|
+
}
|
|
3697
|
+
renderMmlBar(elem) {
|
|
3698
|
+
const style = {};
|
|
3699
|
+
switch (elem.props.position) {
|
|
3700
|
+
case "top":
|
|
3701
|
+
style.textDecoration = "overline";
|
|
3702
|
+
break;
|
|
3703
|
+
case "bottom": style.textDecoration = "underline";
|
|
3704
|
+
}
|
|
3705
|
+
return this.renderContainerNS(elem, ns.mathML, "mrow", { style });
|
|
3706
|
+
}
|
|
3707
|
+
renderMmlRun(elem) {
|
|
3708
|
+
return this.toHTML(elem, ns.mathML, "ms");
|
|
3709
|
+
}
|
|
3710
|
+
renderMllList(elem) {
|
|
3711
|
+
const children = this.renderElements(elem.children).map((x) => this.createMathMLElement("mtr", null, [this.createMathMLElement("mtd", null, [x])]));
|
|
3712
|
+
return this.toHTML(elem, ns.mathML, "mtable", children);
|
|
3713
|
+
}
|
|
3714
|
+
toH(elem, ns, tagName, children = null) {
|
|
3715
|
+
const { "$lang": lang, ...style } = elem.cssStyle ?? {};
|
|
3716
|
+
return {
|
|
3717
|
+
ns,
|
|
3718
|
+
tagName,
|
|
3719
|
+
className: cx(elem.className, elem.styleName && this.processStyleName(elem.styleName)),
|
|
3720
|
+
lang,
|
|
3721
|
+
style,
|
|
3722
|
+
children: children ?? this.renderElements(elem.children)
|
|
3723
|
+
};
|
|
3724
|
+
}
|
|
3725
|
+
toHTML(elem, ns, tagName, children = null) {
|
|
3726
|
+
return this.h(this.toH(elem, ns, tagName, children));
|
|
3727
|
+
}
|
|
3728
|
+
findStyle(styleName) {
|
|
3729
|
+
return styleName && this.styleMap?.[styleName];
|
|
3730
|
+
}
|
|
3731
|
+
numberingClass(id, lvl) {
|
|
3732
|
+
return `${this.className}-num-${id}-${lvl}`;
|
|
3733
|
+
}
|
|
3734
|
+
tabStopClass() {
|
|
3735
|
+
return `${this.className}-tab-stop`;
|
|
3736
|
+
}
|
|
3737
|
+
styleToString(selectors, values, cssText = null) {
|
|
3738
|
+
let result = `${selectors} {\r\n`;
|
|
3739
|
+
for (const key in values) {
|
|
3740
|
+
if (key.startsWith("$")) continue;
|
|
3741
|
+
result += ` ${key}: ${values[key]};\r\n`;
|
|
3742
|
+
}
|
|
3743
|
+
if (cssText) result += cssText;
|
|
3744
|
+
return result + "}\r\n";
|
|
3745
|
+
}
|
|
3746
|
+
numberingCounter(id, lvl) {
|
|
3747
|
+
return `${this.className}-num-${id}-${lvl}`;
|
|
3748
|
+
}
|
|
3749
|
+
levelTextToContent(text, suff, id, numformat) {
|
|
3750
|
+
return `"${text.replace(/%\d*/g, (s) => {
|
|
3751
|
+
let lvl = parseInt(s.substring(1), 10) - 1;
|
|
3752
|
+
return `"counter(${this.numberingCounter(id, lvl)}, ${numformat})"`;
|
|
3753
|
+
})}${{
|
|
3754
|
+
"tab": "\\9",
|
|
3755
|
+
"space": "\\a0"
|
|
3756
|
+
}[suff] ?? ""}"`;
|
|
3757
|
+
}
|
|
3758
|
+
numFormatToCssValue(format) {
|
|
3759
|
+
return {
|
|
3760
|
+
none: "none",
|
|
3761
|
+
bullet: "disc",
|
|
3762
|
+
decimal: "decimal",
|
|
3763
|
+
lowerLetter: "lower-alpha",
|
|
3764
|
+
upperLetter: "upper-alpha",
|
|
3765
|
+
lowerRoman: "lower-roman",
|
|
3766
|
+
upperRoman: "upper-roman",
|
|
3767
|
+
decimalZero: "decimal-leading-zero",
|
|
3768
|
+
aiueo: "katakana",
|
|
3769
|
+
aiueoFullWidth: "katakana",
|
|
3770
|
+
chineseCounting: "simp-chinese-informal",
|
|
3771
|
+
chineseCountingThousand: "simp-chinese-informal",
|
|
3772
|
+
chineseLegalSimplified: "simp-chinese-formal",
|
|
3773
|
+
chosung: "hangul-consonant",
|
|
3774
|
+
ideographDigital: "cjk-ideographic",
|
|
3775
|
+
ideographTraditional: "cjk-heavenly-stem",
|
|
3776
|
+
ideographLegalTraditional: "trad-chinese-formal",
|
|
3777
|
+
ideographZodiac: "cjk-earthly-branch",
|
|
3778
|
+
iroha: "katakana-iroha",
|
|
3779
|
+
irohaFullWidth: "katakana-iroha",
|
|
3780
|
+
japaneseCounting: "japanese-informal",
|
|
3781
|
+
japaneseDigitalTenThousand: "cjk-decimal",
|
|
3782
|
+
japaneseLegal: "japanese-formal",
|
|
3783
|
+
thaiNumbers: "thai",
|
|
3784
|
+
koreanCounting: "korean-hangul-formal",
|
|
3785
|
+
koreanDigital: "korean-hangul-formal",
|
|
3786
|
+
koreanDigital2: "korean-hanja-informal",
|
|
3787
|
+
hebrew1: "hebrew",
|
|
3788
|
+
hebrew2: "hebrew",
|
|
3789
|
+
hindiNumbers: "devanagari",
|
|
3790
|
+
ganada: "hangul",
|
|
3791
|
+
taiwaneseCounting: "cjk-ideographic",
|
|
3792
|
+
taiwaneseCountingThousand: "cjk-ideographic",
|
|
3793
|
+
taiwaneseDigital: "cjk-decimal"
|
|
3794
|
+
}[format] ?? format;
|
|
3795
|
+
}
|
|
3796
|
+
refreshTabStops() {
|
|
3797
|
+
if (!this.options.experimental) return;
|
|
3798
|
+
setTimeout(() => {
|
|
3799
|
+
const pixelToPoint = computePixelToPoint();
|
|
3800
|
+
for (let tab of this.currentTabs) updateTabStop(tab.span, tab.stops, this.defaultTabSize, pixelToPoint);
|
|
3801
|
+
}, 500);
|
|
3802
|
+
}
|
|
3803
|
+
createElementNS(ns, tagName, props, children) {
|
|
3804
|
+
return this.h({
|
|
3805
|
+
ns,
|
|
3806
|
+
tagName,
|
|
3807
|
+
children,
|
|
3808
|
+
...props
|
|
3809
|
+
});
|
|
3810
|
+
}
|
|
3811
|
+
createElement(tagName, props, children) {
|
|
3812
|
+
return this.createElementNS(ns.html, tagName, props, children);
|
|
3813
|
+
}
|
|
3814
|
+
createMathMLElement(tagName, props, children) {
|
|
3815
|
+
return this.createElementNS(ns.mathML, tagName, props, children);
|
|
3816
|
+
}
|
|
3817
|
+
createSvgElement(tagName, props, children) {
|
|
3818
|
+
return this.createElementNS(ns.svg, tagName, props, children);
|
|
3819
|
+
}
|
|
3820
|
+
later(func) {
|
|
3821
|
+
this.postRenderTasks.push(func);
|
|
3822
|
+
}
|
|
3823
|
+
};
|
|
3824
|
+
function findParent(elem, type) {
|
|
3825
|
+
var parent = elem.parent;
|
|
3826
|
+
while (parent != null && parent.type != type) parent = parent.parent;
|
|
3827
|
+
return parent;
|
|
3828
|
+
}
|
|
3829
|
+
//#endregion
|
|
3830
|
+
//#region src/pagination.ts
|
|
3831
|
+
var FIT_EPSILON = 1;
|
|
3832
|
+
var MAX_PAGES_PER_SECTION = 1e3;
|
|
3833
|
+
function paginateWrapper(container, className) {
|
|
3834
|
+
const sections = Array.from(container.querySelectorAll(`section.${className}`));
|
|
3835
|
+
let pageCount = 0;
|
|
3836
|
+
for (const section of sections) pageCount += paginateSection(section, className);
|
|
3837
|
+
if (pageCount > 0) {
|
|
3838
|
+
const style = document.createElement("style");
|
|
3839
|
+
style.textContent = `.${className} .${className}-continuation { text-indent: 0 !important; }.${className} .${className}-continuation::before { content: none !important; }`;
|
|
3840
|
+
container.prepend(style);
|
|
3841
|
+
const pages = container.querySelectorAll(`section.${className}`);
|
|
3842
|
+
const total = `${pages.length}`;
|
|
3843
|
+
pages.forEach((page, i) => {
|
|
3844
|
+
for (const el of Array.from(page.querySelectorAll(`.${className}-field-page`))) el.textContent = `${i + 1}`;
|
|
3845
|
+
for (const el of Array.from(page.querySelectorAll(`.${className}-field-numpages`))) el.textContent = total;
|
|
3846
|
+
});
|
|
3847
|
+
}
|
|
3848
|
+
return pageCount;
|
|
3849
|
+
}
|
|
3850
|
+
function paginateSection(section, className) {
|
|
3851
|
+
const article = section.querySelector(":scope > article");
|
|
3852
|
+
if (!article) return 0;
|
|
3853
|
+
const sectionStyle = getComputedStyle(section);
|
|
3854
|
+
const pageHeight = parseFloat(sectionStyle.minHeight);
|
|
3855
|
+
if (!pageHeight || Number.isNaN(pageHeight)) return 0;
|
|
3856
|
+
const columns = getComputedStyle(article).columnCount;
|
|
3857
|
+
if (columns !== "auto" && columns !== "1") return 0;
|
|
3858
|
+
const contentLimit = pageHeight - parseFloat(sectionStyle.paddingTop) - parseFloat(sectionStyle.paddingBottom);
|
|
3859
|
+
if (!(contentLimit > 0) || article.scrollHeight <= contentLimit + FIT_EPSILON) return 0;
|
|
3860
|
+
const header = section.querySelector(":scope > header");
|
|
3861
|
+
const footer = section.querySelector(":scope > footer");
|
|
3862
|
+
const notes = Array.from(section.querySelectorAll(":scope > ol"));
|
|
3863
|
+
const pages = [section];
|
|
3864
|
+
let currentArticle = article;
|
|
3865
|
+
let lastPage = section;
|
|
3866
|
+
function newPage() {
|
|
3867
|
+
const shell = section.cloneNode(false);
|
|
3868
|
+
if (header) shell.appendChild(header.cloneNode(true));
|
|
3869
|
+
const pageArticle = article.cloneNode(false);
|
|
3870
|
+
shell.appendChild(pageArticle);
|
|
3871
|
+
if (footer) shell.appendChild(footer.cloneNode(true));
|
|
3872
|
+
lastPage.after(shell);
|
|
3873
|
+
lastPage = shell;
|
|
3874
|
+
pages.push(shell);
|
|
3875
|
+
currentArticle = pageArticle;
|
|
3876
|
+
}
|
|
3877
|
+
const fits = () => currentArticle.scrollHeight <= contentLimit + FIT_EPSILON;
|
|
3878
|
+
const bottomLimit = () => currentArticle.getBoundingClientRect().top + contentLimit;
|
|
3879
|
+
const blocks = Array.from(article.children);
|
|
3880
|
+
for (const b of blocks) b.remove();
|
|
3881
|
+
for (let i = 0; i < blocks.length && pages.length < MAX_PAGES_PER_SECTION; i++) {
|
|
3882
|
+
const block = blocks[i];
|
|
3883
|
+
currentArticle.appendChild(block);
|
|
3884
|
+
if (fits()) continue;
|
|
3885
|
+
const tail = splitBlock(block, bottomLimit(), className);
|
|
3886
|
+
if (tail === "allfit") continue;
|
|
3887
|
+
if (tail) {
|
|
3888
|
+
blocks.splice(i + 1, 0, tail);
|
|
3889
|
+
newPage();
|
|
3890
|
+
continue;
|
|
3891
|
+
}
|
|
3892
|
+
if (tail) {
|
|
3893
|
+
blocks.splice(i + 1, 0, tail);
|
|
3894
|
+
newPage();
|
|
3895
|
+
continue;
|
|
3896
|
+
}
|
|
3897
|
+
block.remove();
|
|
3898
|
+
if (currentArticle.childElementCount === 0) {
|
|
3899
|
+
currentArticle.appendChild(block);
|
|
3900
|
+
continue;
|
|
3901
|
+
}
|
|
3902
|
+
newPage();
|
|
3903
|
+
i--;
|
|
3904
|
+
}
|
|
3905
|
+
if (notes.length > 0 && pages.length > 1) {
|
|
3906
|
+
const lastFooter = lastPage.querySelector(":scope > footer");
|
|
3907
|
+
for (const ol of notes) lastPage.insertBefore(ol, lastFooter);
|
|
3908
|
+
}
|
|
3909
|
+
if (pages.length <= 1) return 0;
|
|
3910
|
+
return pages.length;
|
|
3911
|
+
}
|
|
3912
|
+
/**
|
|
3913
|
+
* Splits `block` (already appended to a page and known to overflow) so its
|
|
3914
|
+
* head fits above `bottomLimit` (viewport coordinate). Returns the detached
|
|
3915
|
+
* tail fragment, "allfit" when the block's content doesn't actually
|
|
3916
|
+
* overflow (keep it on the current page), or null when the block can't
|
|
3917
|
+
* usefully be split here.
|
|
3918
|
+
*/
|
|
3919
|
+
function splitBlock(block, bottomLimit, className) {
|
|
3920
|
+
if (block.tagName === "TABLE") return splitTable(block, bottomLimit);
|
|
3921
|
+
const pos = findTextSplitOffset(block, bottomLimit);
|
|
3922
|
+
if (pos === "allfit") return "allfit";
|
|
3923
|
+
if (pos === null) return null;
|
|
3924
|
+
const tail = splitAt(block, pos.node, pos.offset);
|
|
3925
|
+
tail.classList.add(`${className}-continuation`);
|
|
3926
|
+
return tail;
|
|
3927
|
+
}
|
|
3928
|
+
function splitTable(table, bottomLimit) {
|
|
3929
|
+
const bodyRows = [];
|
|
3930
|
+
for (const tbody of Array.from(table.tBodies)) bodyRows.push(...Array.from(tbody.rows));
|
|
3931
|
+
if (bodyRows.length < 2) return null;
|
|
3932
|
+
const splitIndex = bodyRows.findIndex((r) => r.getBoundingClientRect().bottom > bottomLimit);
|
|
3933
|
+
if (splitIndex <= 0) return null;
|
|
3934
|
+
const tail = table.cloneNode(false);
|
|
3935
|
+
const colgroup = table.querySelector(":scope > colgroup");
|
|
3936
|
+
if (colgroup) tail.appendChild(colgroup.cloneNode(true));
|
|
3937
|
+
if (table.tHead) tail.appendChild(table.tHead.cloneNode(true));
|
|
3938
|
+
for (let i = splitIndex; i < bodyRows.length; i++) tail.appendChild(bodyRows[i]);
|
|
3939
|
+
return tail;
|
|
3940
|
+
}
|
|
3941
|
+
/**
|
|
3942
|
+
* Finds the latest text position inside `el` that still renders above
|
|
3943
|
+
* `bottomLimit`, by walking text nodes in order and binary-searching each
|
|
3944
|
+
* one with Range rects. Returns:
|
|
3945
|
+
* - { node, offset } — split here;
|
|
3946
|
+
* - "allfit" — every text node fits; the overflow comes from the block's
|
|
3947
|
+
* own box (min-height overshoot), not its content, so it should stay;
|
|
3948
|
+
* - null — even the first character overflows (or there is no text).
|
|
3949
|
+
*/
|
|
3950
|
+
function findTextSplitOffset(el, bottomLimit) {
|
|
3951
|
+
const doc = el.ownerDocument;
|
|
3952
|
+
const walker = doc.createTreeWalker(el, NodeFilter.SHOW_TEXT);
|
|
3953
|
+
const range = doc.createRange();
|
|
3954
|
+
let hasFittingContent = false;
|
|
3955
|
+
let sawText = false;
|
|
3956
|
+
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
3957
|
+
if (node.length === 0) continue;
|
|
3958
|
+
sawText = true;
|
|
3959
|
+
let lo = 0, hi = node.length, fit = 0;
|
|
3960
|
+
while (lo <= hi) {
|
|
3961
|
+
const mid = lo + hi >> 1;
|
|
3962
|
+
range.setStart(node, 0);
|
|
3963
|
+
range.setEnd(node, mid);
|
|
3964
|
+
const rects = range.getClientRects();
|
|
3965
|
+
const last = rects[rects.length - 1];
|
|
3966
|
+
if (last && last.bottom <= bottomLimit) {
|
|
3967
|
+
fit = mid;
|
|
3968
|
+
lo = mid + 1;
|
|
3969
|
+
} else hi = mid - 1;
|
|
3970
|
+
}
|
|
3971
|
+
if (fit === 0) return hasFittingContent ? {
|
|
3972
|
+
node,
|
|
3973
|
+
offset: 0
|
|
3974
|
+
} : null;
|
|
3975
|
+
if (fit < node.length) {
|
|
3976
|
+
let back = fit;
|
|
3977
|
+
const floor = Math.max(0, fit - 30);
|
|
3978
|
+
while (back > floor && !/\s/.test(node.data[back - 1])) back--;
|
|
3979
|
+
return {
|
|
3980
|
+
node,
|
|
3981
|
+
offset: back > floor ? back : fit
|
|
3982
|
+
};
|
|
3983
|
+
}
|
|
3984
|
+
hasFittingContent = true;
|
|
3985
|
+
}
|
|
3986
|
+
return sawText ? "allfit" : null;
|
|
3987
|
+
}
|
|
3988
|
+
/**
|
|
3989
|
+
* Splits `el` at (textNode, offset): `el` keeps everything before the point,
|
|
3990
|
+
* and the returned element (a shallow clone of `el` with cloned inline
|
|
3991
|
+
* ancestors) gets everything from the point on.
|
|
3992
|
+
*/
|
|
3993
|
+
function splitAt(el, textNode, offset) {
|
|
3994
|
+
const tail = el.cloneNode(false);
|
|
3995
|
+
moveTail(textNode.splitText(offset), el, tail);
|
|
3996
|
+
return tail;
|
|
3997
|
+
}
|
|
3998
|
+
/** Moves the split point's following content into a parallel clone
|
|
3999
|
+
* hierarchy under `tailRoot`: `node` and its following siblings go into a
|
|
4000
|
+
* clone of their parent, and each ancestor up to `root` contributes a clone
|
|
4001
|
+
* plus its following siblings. The ORIGINAL ancestors stay in the head —
|
|
4002
|
+
* moving them would rip the head's split-span text out (and reverse the
|
|
4003
|
+
* order of the two text halves in the tail). */
|
|
4004
|
+
function moveTail(node, root, tailRoot) {
|
|
4005
|
+
let child = node;
|
|
4006
|
+
let container = tailRoot;
|
|
4007
|
+
while (child.parentNode && child.parentNode !== root) {
|
|
4008
|
+
const parent = child.parentNode;
|
|
4009
|
+
const parentClone = parent.cloneNode(false);
|
|
4010
|
+
container.appendChild(parentClone);
|
|
4011
|
+
parentClone.appendChild(child);
|
|
4012
|
+
while (child.nextSibling) parentClone.appendChild(child.nextSibling);
|
|
4013
|
+
child = parent;
|
|
4014
|
+
container = parentClone;
|
|
4015
|
+
}
|
|
4016
|
+
if (child === node) tailRoot.appendChild(child);
|
|
4017
|
+
while (child.nextSibling) tailRoot.appendChild(child.nextSibling);
|
|
4018
|
+
}
|
|
4019
|
+
//#endregion
|
|
4020
|
+
//#region src/docx-preview.ts
|
|
4021
|
+
var defaultOptions = {
|
|
4022
|
+
ignoreHeight: false,
|
|
4023
|
+
ignoreWidth: false,
|
|
4024
|
+
ignoreFonts: false,
|
|
4025
|
+
breakPages: true,
|
|
4026
|
+
paginate: false,
|
|
4027
|
+
debug: false,
|
|
4028
|
+
experimental: false,
|
|
4029
|
+
className: "docx",
|
|
4030
|
+
inWrapper: true,
|
|
4031
|
+
hideWrapperOnPrint: false,
|
|
4032
|
+
trimXmlDeclaration: true,
|
|
4033
|
+
ignoreLastRenderedPageBreak: true,
|
|
4034
|
+
renderHeaders: true,
|
|
4035
|
+
renderFooters: true,
|
|
4036
|
+
renderFootnotes: true,
|
|
4037
|
+
renderEndnotes: true,
|
|
4038
|
+
useBase64URL: false,
|
|
4039
|
+
renderChanges: false,
|
|
4040
|
+
renderComments: false,
|
|
4041
|
+
renderAltChunks: true,
|
|
4042
|
+
h
|
|
4043
|
+
};
|
|
4044
|
+
function parseAsync(data, userOptions) {
|
|
4045
|
+
const ops = {
|
|
4046
|
+
...defaultOptions,
|
|
4047
|
+
...userOptions
|
|
4048
|
+
};
|
|
4049
|
+
return WordDocument.load(data, new DocumentParser(ops), ops);
|
|
4050
|
+
}
|
|
4051
|
+
async function renderDocument(document, userOptions) {
|
|
4052
|
+
const ops = {
|
|
4053
|
+
...defaultOptions,
|
|
4054
|
+
...userOptions
|
|
4055
|
+
};
|
|
4056
|
+
return await new HtmlRenderer().render(document, ops);
|
|
4057
|
+
}
|
|
4058
|
+
function renderFontFaces(fonts) {
|
|
4059
|
+
const css = fonts.map((f) => {
|
|
4060
|
+
const props = [`font-family: "${f.name.replace(/["\\\r\n]/g, "")}"`, `src: ${f.src}`];
|
|
4061
|
+
if (f.weight != null) props.push(`font-weight: ${f.weight}`);
|
|
4062
|
+
if (f.style != null) props.push(`font-style: ${f.style}`);
|
|
4063
|
+
return `@font-face { ${props.join("; ")}; }`;
|
|
4064
|
+
}).join("\n");
|
|
4065
|
+
const el = document.createElement("style");
|
|
4066
|
+
el.textContent = css;
|
|
4067
|
+
return el;
|
|
4068
|
+
}
|
|
4069
|
+
async function renderAsync(data, bodyContainer, styleContainer, userOptions) {
|
|
4070
|
+
const doc = await parseAsync(data, userOptions);
|
|
4071
|
+
const nodes = await renderDocument(doc, userOptions);
|
|
4072
|
+
const ops = {
|
|
4073
|
+
...defaultOptions,
|
|
4074
|
+
...userOptions
|
|
4075
|
+
};
|
|
4076
|
+
styleContainer ?? (styleContainer = bodyContainer);
|
|
4077
|
+
styleContainer.innerHTML = "";
|
|
4078
|
+
bodyContainer.innerHTML = "";
|
|
4079
|
+
if (ops.fonts?.length) nodes.unshift(renderFontFaces(ops.fonts));
|
|
4080
|
+
for (let n of nodes) (n.nodeName === "STYLE" ? styleContainer : bodyContainer).appendChild(n);
|
|
4081
|
+
const fontSet = typeof document !== "undefined" ? document.fonts : null;
|
|
4082
|
+
if (ops.fonts?.length && fontSet?.load) try {
|
|
4083
|
+
await Promise.all(ops.fonts.map((f) => fontSet.load(`${f.style ?? "normal"} ${f.weight ?? "normal"} 12px "${f.name.replace(/["\\\r\n]/g, "")}"`)));
|
|
4084
|
+
} catch {}
|
|
4085
|
+
if (ops.paginate) {
|
|
4086
|
+
if (fontSet?.ready) try {
|
|
4087
|
+
await fontSet.ready;
|
|
4088
|
+
} catch {}
|
|
4089
|
+
paginateWrapper(bodyContainer, ops.className);
|
|
4090
|
+
}
|
|
4091
|
+
return doc;
|
|
4092
|
+
}
|
|
4093
|
+
//#endregion
|
|
4094
|
+
export { defaultOptions, parseAsync, renderAsync, renderDocument };
|
|
4095
|
+
|
|
4096
|
+
//# sourceMappingURL=docx-preview.mjs.map
|