md2gost 1.5.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -1
- package/README.md +418 -19
- package/assets/styles.xml +126 -0
- package/assets/template.dotm +0 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +2329 -415
- package/dist/index.js.map +3 -3
- package/dist/index.mjs +2281 -365
- package/dist/index.mjs.map +3 -3
- package/package.json +35 -7
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const __MD2GOST_DIRNAME__ = __dirname;
|
|
1
2
|
"use strict";
|
|
2
3
|
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
@@ -31,18 +32,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
32
|
var index_exports = {};
|
|
32
33
|
__export(index_exports, {
|
|
33
34
|
MDRenderError: () => MDRenderError,
|
|
35
|
+
UserInputError: () => UserInputError,
|
|
34
36
|
default: () => renderMarkdown
|
|
35
37
|
});
|
|
36
38
|
module.exports = __toCommonJS(index_exports);
|
|
37
|
-
var
|
|
38
|
-
var import_url = require("url");
|
|
39
|
+
var import_path5 = __toESM(require("path"));
|
|
39
40
|
|
|
40
41
|
// src/main.ts
|
|
41
|
-
var
|
|
42
|
+
var import_path4 = __toESM(require("path"));
|
|
42
43
|
|
|
43
44
|
// src/doc.ts
|
|
44
45
|
var Doc = class {
|
|
45
46
|
nodes = [];
|
|
47
|
+
header = { type: "none" };
|
|
48
|
+
footer = { type: "auto" };
|
|
46
49
|
rainbow = false;
|
|
47
50
|
numberingLazy = false;
|
|
48
51
|
numberingSections = false;
|
|
@@ -102,36 +105,66 @@ var Doc = class {
|
|
|
102
105
|
spacing: { before: void 0, after: void 0 },
|
|
103
106
|
text: { size: void 0 }
|
|
104
107
|
};
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
formula = {
|
|
109
|
+
spacing: { before: 8, after: 8 }
|
|
110
|
+
};
|
|
111
|
+
admonition = {
|
|
112
|
+
note: { title: "**\u041F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435**", indent: 1.25, spacing: { before: 8, after: 8 }, padding: { top: 6, right: 10, bottom: 6, left: 10 }, background: "f5f5f5", color: "666666", bar_width: 2.25, icon: true, icon_size: 16, title_color: true },
|
|
113
|
+
info: { title: "**\u041F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435**", indent: 1.25, spacing: { before: 8, after: 8 }, padding: { top: 6, right: 10, bottom: 6, left: 10 }, background: "f0f7ff", color: "1976d2", bar_width: 2.25, icon: true, icon_size: 16, title_color: true },
|
|
114
|
+
tip: { title: "**\u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0430\u0446\u0438\u044F**", indent: 1.25, spacing: { before: 8, after: 8 }, padding: { top: 6, right: 10, bottom: 6, left: 10 }, background: "f2f8f2", color: "2e7d32", bar_width: 2.25, icon: true, icon_size: 16, title_color: true },
|
|
115
|
+
warning: { title: "**\u0412\u043D\u0438\u043C\u0430\u043D\u0438\u0435**", indent: 1.25, spacing: { before: 8, after: 8 }, padding: { top: 6, right: 10, bottom: 6, left: 10 }, background: "fff7ea", color: "b26a00", bar_width: 2.25, icon: true, icon_size: 16, title_color: true },
|
|
116
|
+
danger: { title: "**\u041A\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043A\u0438 \u0432\u0430\u0436\u043D\u043E**", indent: 1.25, spacing: { before: 8, after: 8 }, padding: { top: 6, right: 10, bottom: 6, left: 10 }, background: "fff0f0", color: "d32f2f", bar_width: 2.25, icon: true, icon_size: 16, title_color: true }
|
|
117
|
+
};
|
|
118
|
+
appendText(text, sourceLine) {
|
|
119
|
+
this.nodes.push({ type: "text", text, sourceLine });
|
|
107
120
|
}
|
|
108
|
-
appendTitle(text, level) {
|
|
109
|
-
this.nodes.push({ type: "title", text, level });
|
|
121
|
+
appendTitle(text, level, sourceLine) {
|
|
122
|
+
this.nodes.push({ type: "title", text, level, sourceLine });
|
|
110
123
|
}
|
|
111
124
|
appendNode(node) {
|
|
112
125
|
this.nodes.push(node);
|
|
113
126
|
}
|
|
127
|
+
lineSkips = [];
|
|
128
|
+
addLineSkip(line, delta) {
|
|
129
|
+
this.lineSkips.push({ ln: line, d: delta });
|
|
130
|
+
}
|
|
131
|
+
mapSourceLine(line) {
|
|
132
|
+
for (const { ln, d } of this.lineSkips)
|
|
133
|
+
if (ln <= line) line -= d;
|
|
134
|
+
return line;
|
|
135
|
+
}
|
|
114
136
|
};
|
|
115
|
-
function tableRow(...items) {
|
|
116
|
-
return items.map((v) => ({ type: "text", text: v }));
|
|
137
|
+
function tableRow(sourceLine, ...items) {
|
|
138
|
+
return items.map((v) => ({ type: "text", text: v, sourceLine }));
|
|
117
139
|
}
|
|
118
140
|
|
|
119
141
|
// src/parser.ts
|
|
120
|
-
var
|
|
142
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
121
143
|
|
|
122
144
|
// src/utils.ts
|
|
123
145
|
var import_promises = __toESM(require("fs/promises"));
|
|
124
146
|
var import_fs = __toESM(require("fs"));
|
|
125
147
|
var path = __toESM(require("node:path"));
|
|
126
|
-
|
|
127
|
-
|
|
148
|
+
|
|
149
|
+
// src/errors.ts
|
|
150
|
+
var UserInputError = class extends Error {
|
|
151
|
+
constructor(message) {
|
|
152
|
+
super(message);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// src/utils.ts
|
|
157
|
+
function choice(first, ...options) {
|
|
158
|
+
const all = [first, ...options];
|
|
159
|
+
return all[randomInt(all.length)];
|
|
128
160
|
}
|
|
129
161
|
function randomInt(maxmin, max, rnd = Math.random) {
|
|
130
162
|
if (max != void 0)
|
|
131
|
-
return Math.floor(rnd() * (
|
|
163
|
+
return Math.floor(rnd() * (max - maxmin)) + maxmin;
|
|
132
164
|
return Math.floor(rnd() * maxmin);
|
|
133
165
|
}
|
|
134
166
|
function trimEnd(str, ...chs) {
|
|
167
|
+
chs = chs.filter((ch) => ch.length > 0);
|
|
135
168
|
if (!chs || chs.length == 0) return str.trimEnd();
|
|
136
169
|
let trimmed = false;
|
|
137
170
|
do {
|
|
@@ -145,6 +178,7 @@ function trimEnd(str, ...chs) {
|
|
|
145
178
|
return str;
|
|
146
179
|
}
|
|
147
180
|
function trimStart(str, ...chs) {
|
|
181
|
+
chs = chs.filter((ch) => ch.length > 0);
|
|
148
182
|
if (!chs || chs.length == 0) return str.trimStart();
|
|
149
183
|
let trimmed = false;
|
|
150
184
|
do {
|
|
@@ -160,10 +194,10 @@ function trimStart(str, ...chs) {
|
|
|
160
194
|
function toCapitalCase(str) {
|
|
161
195
|
return str.slice(0, 1).toUpperCase() + str.slice(1);
|
|
162
196
|
}
|
|
163
|
-
async function checkIfFileIsBlocked(
|
|
197
|
+
async function checkIfFileIsBlocked(path7) {
|
|
164
198
|
let file;
|
|
165
199
|
try {
|
|
166
|
-
file = await import_promises.default.open(
|
|
200
|
+
file = await import_promises.default.open(path7, "r+");
|
|
167
201
|
return false;
|
|
168
202
|
} catch (err) {
|
|
169
203
|
if (!err) return false;
|
|
@@ -216,22 +250,791 @@ function realpathAllowMissing(inputPath) {
|
|
|
216
250
|
function isMissingPathError(err) {
|
|
217
251
|
return err instanceof Error && "code" in err && (err.code === "ENOENT" || err.code === "ENOTDIR");
|
|
218
252
|
}
|
|
253
|
+
function getSafePathResolver(workdir, checkFilesIsInsidePath) {
|
|
254
|
+
const allowedRealPath = checkFilesIsInsidePath === false ? false : realpathAllowMissing(checkFilesIsInsidePath === "" ? workdir : checkFilesIsInsidePath);
|
|
255
|
+
const resolvePath = (fname, origin) => {
|
|
256
|
+
if (process.platform != "win32") fname = fname.replaceAll("\\", "/");
|
|
257
|
+
if (process.platform == "win32" && fname.startsWith("/")) fname = "." + fname;
|
|
258
|
+
const targetPath = path.isAbsolute(fname) ? path.resolve(fname) : path.resolve(origin || workdir, fname);
|
|
259
|
+
if (allowedRealPath !== false) {
|
|
260
|
+
const targetRealPath = realpathAllowMissing(targetPath);
|
|
261
|
+
const relative2 = path.relative(allowedRealPath, targetRealPath);
|
|
262
|
+
const isOutside = relative2 === ".." || relative2.startsWith(`..${path.sep}`) || path.isAbsolute(relative2);
|
|
263
|
+
if (isOutside) throw new UserInputError(`Access denied: "${fname}" resolves outside the working directory.`);
|
|
264
|
+
}
|
|
265
|
+
return targetPath;
|
|
266
|
+
};
|
|
267
|
+
const getRelative = (fname) => {
|
|
268
|
+
const targetPath = resolvePath(fname);
|
|
269
|
+
if (allowedRealPath !== false)
|
|
270
|
+
return path.relative(allowedRealPath, targetPath);
|
|
271
|
+
return path.relative(workdir, targetPath);
|
|
272
|
+
};
|
|
273
|
+
return { resolvePath, getRelative };
|
|
274
|
+
}
|
|
275
|
+
function isLocalFilePath(link) {
|
|
276
|
+
const value = link.trim();
|
|
277
|
+
if (value.startsWith("//")) return false;
|
|
278
|
+
if (path.win32.isAbsolute(value)) return true;
|
|
279
|
+
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) return false;
|
|
280
|
+
if (value.startsWith("#")) return false;
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
function deepOverwrite(target, source) {
|
|
284
|
+
const result = Object.assign(/* @__PURE__ */ Object.create(null), target);
|
|
285
|
+
for (const [key, sourceValue] of Object.entries(source)) {
|
|
286
|
+
if (key === "__proto__" || key === "prototype" || key === "constructor")
|
|
287
|
+
continue;
|
|
288
|
+
const targetValue = Object.hasOwn(result, key) ? result[key] : void 0;
|
|
289
|
+
result[key] = isJSONDict(targetValue) && isJSONDict(sourceValue) ? deepOverwrite(targetValue, sourceValue) : sourceValue;
|
|
290
|
+
}
|
|
291
|
+
return result;
|
|
292
|
+
function isJSONDict(value) {
|
|
293
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
219
296
|
|
|
220
297
|
// src/parser.ts
|
|
298
|
+
var import_path2 = __toESM(require("path"));
|
|
299
|
+
|
|
300
|
+
// src/preprocessor.ts
|
|
301
|
+
var import_promises2 = __toESM(require("fs/promises"));
|
|
221
302
|
var import_path = __toESM(require("path"));
|
|
222
|
-
async function
|
|
223
|
-
const
|
|
303
|
+
async function preprocess(file, variables, checkFilesIsInsidePath, options, signal, logwarn = console.warn) {
|
|
304
|
+
const rootFile = import_path.default.resolve(file);
|
|
305
|
+
const { resolvePath, getRelative } = getSafePathResolver(import_path.default.dirname(rootFile), checkFilesIsInsidePath);
|
|
306
|
+
const ctx = {
|
|
307
|
+
options,
|
|
308
|
+
workdir: import_path.default.dirname(rootFile),
|
|
309
|
+
includes: 0,
|
|
310
|
+
iterations: 0,
|
|
311
|
+
inputLength: 0,
|
|
312
|
+
generatedLength: 0,
|
|
313
|
+
signal: signal || void 0,
|
|
314
|
+
logwarn,
|
|
315
|
+
countAsOutput(v) {
|
|
316
|
+
ctx.generatedLength += v.length;
|
|
317
|
+
if (options.maxGeneratedLength !== void 0 && ctx.generatedLength > options.maxGeneratedLength)
|
|
318
|
+
throw new UserInputError(`Maximum output length exceeded (${options.maxGeneratedLength})`);
|
|
319
|
+
return v;
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
const rootNode = { type: "include", path: rootFile, dict: {}, trimEnd: false, trimStart: false };
|
|
323
|
+
return processInclude(rootNode, variables, resolvePath, getRelative, [], ctx);
|
|
324
|
+
}
|
|
325
|
+
async function processInclude(node, variables, resolvePath, getRelative, stack, ctx) {
|
|
326
|
+
ctx.signal?.throwIfAborted();
|
|
327
|
+
const fpath = resolvePath(node.path);
|
|
328
|
+
const relativePath = getRelative(fpath);
|
|
329
|
+
const dir = import_path.default.dirname(fpath);
|
|
330
|
+
if (ctx.options.maxIncludeDepth !== void 0 && stack.length > ctx.options.maxIncludeDepth)
|
|
331
|
+
throw new UserInputError(`Maximum include depth exceeded while including "${relativePath}":
|
|
332
|
+
${stack.join(" -> ")}`);
|
|
333
|
+
if (stack.length > 0) {
|
|
334
|
+
ctx.includes++;
|
|
335
|
+
const maxIncludes = ctx.options.maxIncludes === void 0 ? 1e3 : ctx.options.maxIncludes;
|
|
336
|
+
if (ctx.includes > maxIncludes)
|
|
337
|
+
throw new UserInputError(`Maximum include count exceeded (${maxIncludes})`);
|
|
338
|
+
}
|
|
339
|
+
let rawContent;
|
|
340
|
+
try {
|
|
341
|
+
if (ctx.options.maxFileLength !== void 0) {
|
|
342
|
+
const size = (await import_promises2.default.stat(fpath)).size;
|
|
343
|
+
if (size > ctx.options.maxFileLength * 3)
|
|
344
|
+
throw new UserInputError(`File is too large (${size} > ${ctx.options.maxFileLength * 3}) "${relativePath}"`);
|
|
345
|
+
}
|
|
346
|
+
rawContent = await import_promises2.default.readFile(fpath, { encoding: "utf8", signal: ctx.signal });
|
|
347
|
+
if (ctx.options.maxFileLength !== void 0 && rawContent.length > ctx.options.maxFileLength)
|
|
348
|
+
throw new UserInputError(`File is too large (${rawContent.length} > ${ctx.options.maxFileLength}) "${relativePath}"`);
|
|
349
|
+
ctx.inputLength += rawContent.length;
|
|
350
|
+
if (ctx.options.maxTotalInputLength !== void 0 && ctx.inputLength > ctx.options.maxTotalInputLength)
|
|
351
|
+
throw new UserInputError(`Maximum total input length exceeded (${ctx.inputLength} > ${ctx.options.maxTotalInputLength})`);
|
|
352
|
+
} catch (err) {
|
|
353
|
+
ctx.signal?.throwIfAborted();
|
|
354
|
+
if (err instanceof UserInputError) throw err;
|
|
355
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
356
|
+
throw new UserInputError(`Failed to read included file "${relativePath}": ${reason}`);
|
|
357
|
+
}
|
|
358
|
+
let content = rawContent;
|
|
359
|
+
if (variables) {
|
|
360
|
+
variables = deepOverwrite(variables, node.dict);
|
|
361
|
+
const nodes = parseFExpr(rawContent);
|
|
362
|
+
nodes.forEach((n) => n.type == "expr" && (n.source.fname = relativePath));
|
|
363
|
+
content = processExpressions(nodes, variables || {}, ctx);
|
|
364
|
+
} else {
|
|
365
|
+
variables = {};
|
|
366
|
+
ctx.countAsOutput(content);
|
|
367
|
+
}
|
|
368
|
+
content = await fixLinks(content, fixLink);
|
|
369
|
+
if (!ctx.options.allowIncludes) return content;
|
|
370
|
+
const includes = await parseIncludes(content, ctx.logwarn);
|
|
371
|
+
let result = "";
|
|
372
|
+
for (const node2 of includes) {
|
|
373
|
+
if (node2.type == "text") result += node2.value;
|
|
374
|
+
else {
|
|
375
|
+
let r = await processInclude(node2, variables, resolvePath, getRelative, [...stack, relativePath], ctx);
|
|
376
|
+
if (node2.trimStart) r = r.trimStart();
|
|
377
|
+
if (node2.trimEnd) r = r.trimEnd();
|
|
378
|
+
result += r;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (node.sourceLineCount) {
|
|
382
|
+
const delta = result.split("\n").length - node.sourceLineCount + 1;
|
|
383
|
+
result += "\n\uE100" + delta + "\uE101";
|
|
384
|
+
}
|
|
385
|
+
return result;
|
|
386
|
+
function fixLink(oldpath) {
|
|
387
|
+
oldpath = oldpath.trim();
|
|
388
|
+
oldpath = trimEnd(trimStart(oldpath, "<", '"'), ">", '"');
|
|
389
|
+
const newpath = !isLocalFilePath(oldpath) ? oldpath : import_path.default.relative(ctx.workdir, resolvePath(oldpath, dir)).split(import_path.default.sep).join("/");
|
|
390
|
+
if (newpath.includes(" ")) return `<${newpath}>`;
|
|
391
|
+
return newpath;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
function parseFExpr(content) {
|
|
395
|
+
const nodes = [];
|
|
396
|
+
let textStart = 0;
|
|
397
|
+
let ln = 1;
|
|
398
|
+
function countLines(start, end) {
|
|
399
|
+
for (let i = start; i < end; i++)
|
|
400
|
+
if (content[i] == "\n") ln++;
|
|
401
|
+
}
|
|
402
|
+
function pushText(start, end) {
|
|
403
|
+
if (start >= end) return;
|
|
404
|
+
const value = content.slice(start, end);
|
|
405
|
+
const prev = nodes.at(-1);
|
|
406
|
+
countLines(start, end);
|
|
407
|
+
if (prev?.type == "text")
|
|
408
|
+
prev.value += value;
|
|
409
|
+
else
|
|
410
|
+
nodes.push({ type: "text", value });
|
|
411
|
+
}
|
|
412
|
+
function isEscaped(i) {
|
|
413
|
+
let slashes = 0;
|
|
414
|
+
for (i--; i >= 0 && content[i] == "\\"; i--)
|
|
415
|
+
slashes++;
|
|
416
|
+
return slashes % 2 != 0;
|
|
417
|
+
}
|
|
418
|
+
for (let i = 0; i < content.length; i++) {
|
|
419
|
+
if (content.startsWith("<!--", i)) {
|
|
420
|
+
const end2 = content.indexOf("-->", i + 4);
|
|
421
|
+
if (end2 < 0) break;
|
|
422
|
+
i = end2 + 2;
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (!content.startsWith("{{", i))
|
|
426
|
+
continue;
|
|
427
|
+
if (isEscaped(i)) {
|
|
428
|
+
pushText(textStart, i - 1);
|
|
429
|
+
pushText(i, i + 2);
|
|
430
|
+
textStart = i + 2;
|
|
431
|
+
i++;
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
const trimLeft = content[i + 2] == "-" && !/\d/.test(content[i + 3] || "");
|
|
435
|
+
const valueStart = i + 2 + (trimLeft ? 1 : 0);
|
|
436
|
+
let end = -1;
|
|
437
|
+
let inString = false;
|
|
438
|
+
let escaped = false;
|
|
439
|
+
for (let j = valueStart; j < content.length - 1; j++) {
|
|
440
|
+
const ch = content[j];
|
|
441
|
+
if (inString) {
|
|
442
|
+
if (escaped) escaped = false;
|
|
443
|
+
else if (ch === "\\") escaped = true;
|
|
444
|
+
else if (ch === '"') inString = false;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
if (ch == '"') {
|
|
448
|
+
inString = true;
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
if (ch === "}" && content[j + 1] === "}") {
|
|
452
|
+
end = j;
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if (end < 0) continue;
|
|
457
|
+
const trimRight = end > valueStart && content[end - 1] == "-";
|
|
458
|
+
const valueEnd = trimRight ? end - 1 : end;
|
|
459
|
+
const hasQuotes = i > 0 && content[i - 1] == '"' && !isEscaped(i - 1) && content[end + 2] === '"';
|
|
460
|
+
let textEnd = hasQuotes ? i - 1 : i;
|
|
461
|
+
const untrimmedTextEnd = textEnd;
|
|
462
|
+
if (trimLeft)
|
|
463
|
+
while (textEnd > textStart && /\s/.test(content[textEnd - 1])) textEnd--;
|
|
464
|
+
pushText(textStart, textEnd);
|
|
465
|
+
countLines(textEnd, untrimmedTextEnd);
|
|
466
|
+
const lineStart = content.lastIndexOf("\n", i + 2);
|
|
467
|
+
const source = { ln, col: lineStart < 0 ? i + 1 : i - lineStart };
|
|
468
|
+
nodes.push({ type: "expr", value: content.slice(valueStart, valueEnd), hasQuotes, trimLeft, trimRight, source });
|
|
469
|
+
let next = hasQuotes ? end + 3 : end + 2;
|
|
470
|
+
if (trimRight) {
|
|
471
|
+
const untrimmedNext = next;
|
|
472
|
+
while (next < content.length && /\s/.test(content[next])) next++;
|
|
473
|
+
countLines(untrimmedNext, next);
|
|
474
|
+
}
|
|
475
|
+
textStart = next;
|
|
476
|
+
i = next - 1;
|
|
477
|
+
}
|
|
478
|
+
pushText(textStart, content.length);
|
|
479
|
+
return nodes;
|
|
480
|
+
}
|
|
481
|
+
function getByPath(value, path7) {
|
|
482
|
+
for (const key of path7) {
|
|
483
|
+
if (value === null || value === void 0)
|
|
484
|
+
return value;
|
|
485
|
+
const obj = Object(value);
|
|
486
|
+
if (!Object.hasOwn(obj, key))
|
|
487
|
+
return void 0;
|
|
488
|
+
value = obj[key];
|
|
489
|
+
}
|
|
490
|
+
return value;
|
|
491
|
+
}
|
|
492
|
+
function processExpressions(nodes, variables, ctx) {
|
|
493
|
+
const expr = parseExp(nodes, ctx);
|
|
494
|
+
const tree = buildExpTree(expr, ctx);
|
|
495
|
+
const res = execExpTree(tree, variables, ctx);
|
|
496
|
+
return res.join("");
|
|
497
|
+
}
|
|
498
|
+
function parseExp(nodes, ctx) {
|
|
499
|
+
const reVar = String.raw`[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*|\.\d+|\[\d+\])*`;
|
|
500
|
+
const reIdent = String.raw`[A-Za-z_$][A-Za-z0-9_$]*`;
|
|
501
|
+
const reStr = String.raw`"(?:\\.|[^"\\])*"`;
|
|
502
|
+
const reNum = String.raw`-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?`;
|
|
503
|
+
const reVal = String.raw`(?:${reStr}|${reNum}|true|false|null)`;
|
|
504
|
+
const reOCompare = String.raw`(?:==|!=|<|<=|>|>=|\bin\b)`;
|
|
505
|
+
const reOMath = String.raw`(?:\+|-|/|//|%|\*|\*\*)`;
|
|
506
|
+
const reOLogical = String.raw`(?:and|or|&&|\|\||\?\?)`;
|
|
507
|
+
const re_var = new RegExp(String.raw`^\s*((?:${reVar})|${reVal})\s*(?:(${reOMath}|${reOLogical})\s*((?:${reVar})|${reVal}))?\s*$`);
|
|
508
|
+
const re_val = new RegExp(String.raw`^${reVal}$`);
|
|
509
|
+
const re_str = new RegExp(String.raw`^\s*(${reStr})\s*$`);
|
|
510
|
+
const re_else = /^:else\s*$/;
|
|
511
|
+
const re_for = new RegExp(String.raw`^#for\s+(${reIdent})(?:\s*,\s*(${reIdent}))?\s+in\s+(${reVar}|${reNum}|${reStr})\s*$`);
|
|
512
|
+
const re_forEnd = /^\/for\s*$/;
|
|
513
|
+
const re_if = new RegExp(String.raw`^#if\s+((?:${reVar})|${reVal})\s*(?:(${reOCompare}|${reOLogical})\s*((?:${reVar})|${reVal}))?\s*$`);
|
|
514
|
+
const re_ifEnd = /^\/if\s*$/;
|
|
515
|
+
const re_path = /[A-Za-z_$][A-Za-z0-9_$]*|\d+/g;
|
|
516
|
+
return nodes.map((node) => {
|
|
517
|
+
ctx.signal?.throwIfAborted();
|
|
518
|
+
if (node.type == "text") return { type: "text", value: node.value };
|
|
519
|
+
const enode = node;
|
|
520
|
+
const source = node.source;
|
|
521
|
+
const expression = `{{${node.trimLeft ? "-" : ""}${node.value}${node.trimRight ? "-" : ""}}}`;
|
|
522
|
+
const rawValue = node.hasQuotes ? `"${expression}"` : expression;
|
|
523
|
+
function retWarn(msg) {
|
|
524
|
+
ctx.logwarn(msg + pos(enode));
|
|
525
|
+
return { type: "text", value: rawValue };
|
|
526
|
+
}
|
|
527
|
+
const mvar = re_var.exec(node.value);
|
|
528
|
+
if (mvar) {
|
|
529
|
+
const left = parseOperand(mvar[1]);
|
|
530
|
+
if (!left) return retWarn("Unsupported expression: " + rawValue);
|
|
531
|
+
const operator = mvar[2];
|
|
532
|
+
if (!operator) return { type: "var", left, source, hasQuotes: node.hasQuotes };
|
|
533
|
+
const right = parseOperand(mvar[3]);
|
|
534
|
+
if (!right) return retWarn("Unsupported expression: " + rawValue);
|
|
535
|
+
return { type: "var", left, operator, right, source, hasQuotes: node.hasQuotes };
|
|
536
|
+
}
|
|
537
|
+
const mstr = re_str.exec(node.value);
|
|
538
|
+
if (mstr) {
|
|
539
|
+
const str = safeParse(node.value);
|
|
540
|
+
if (str === void 0) return retWarn("Cant parse string: " + node.value);
|
|
541
|
+
return { type: "text", value: node.hasQuotes ? JSON.stringify(str) : `${str}` };
|
|
542
|
+
}
|
|
543
|
+
const mfor = re_for.exec(node.value);
|
|
544
|
+
if (mfor) {
|
|
545
|
+
if (!ctx.options.allowLoops) return retWarn("Loop expressions are not allowed");
|
|
546
|
+
const varValue = mfor[1];
|
|
547
|
+
const varKey = mfor[2];
|
|
548
|
+
const array = parseOperand(mfor[3]);
|
|
549
|
+
if (!array) return retWarn("Unsupported expression: " + rawValue);
|
|
550
|
+
return { type: "for", varValue, varKey, array, source };
|
|
551
|
+
}
|
|
552
|
+
const mif = re_if.exec(node.value);
|
|
553
|
+
if (mif) {
|
|
554
|
+
const left = parseOperand(mif[1]);
|
|
555
|
+
if (!left) return retWarn("Unsupported expression: " + rawValue);
|
|
556
|
+
const operator = mif[2];
|
|
557
|
+
if (!operator) return { type: "if", left, source };
|
|
558
|
+
const right = parseOperand(mif[3]);
|
|
559
|
+
if (!right) return retWarn("Unsupported expression: " + rawValue);
|
|
560
|
+
return { type: "if", left, operator, right, source };
|
|
561
|
+
}
|
|
562
|
+
if (re_else.exec(node.value)) return { type: "else", source };
|
|
563
|
+
if (re_ifEnd.exec(node.value)) return { type: "ifEnd", source };
|
|
564
|
+
if (re_forEnd.exec(node.value)) {
|
|
565
|
+
if (!ctx.options.allowLoops) return retWarn("Loop expressions are not allowed");
|
|
566
|
+
return { type: "forEnd", source };
|
|
567
|
+
}
|
|
568
|
+
return retWarn("Unsupported expression: " + rawValue);
|
|
569
|
+
});
|
|
570
|
+
function pos(node) {
|
|
571
|
+
return ` [Ln ${node.source.ln}, Col ${node.source.col}] (${node.source.fname})`;
|
|
572
|
+
}
|
|
573
|
+
function safeParse(value) {
|
|
574
|
+
try {
|
|
575
|
+
return JSON.parse(value);
|
|
576
|
+
} catch {
|
|
577
|
+
return void 0;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
function parsePath(value) {
|
|
581
|
+
const parts = value?.match(re_path);
|
|
582
|
+
if (!parts?.length) return void 0;
|
|
583
|
+
return parts.map((part) => /^\d+$/.test(part) ? Number(part) : part);
|
|
584
|
+
}
|
|
585
|
+
function parseOperand(value) {
|
|
586
|
+
if (!value) return void 0;
|
|
587
|
+
if (re_val.test(value)) {
|
|
588
|
+
const val = safeParse(value);
|
|
589
|
+
if (val === void 0) return null;
|
|
590
|
+
return { t: "val", val };
|
|
591
|
+
}
|
|
592
|
+
const path7 = parsePath(value);
|
|
593
|
+
if (!path7) return null;
|
|
594
|
+
return { t: "var", path: path7 };
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function buildExpTree(nodes, ctx, depth = 0, source) {
|
|
598
|
+
ctx.signal?.throwIfAborted();
|
|
599
|
+
if (ctx.options.maxDepth !== void 0 && depth >= ctx.options.maxDepth) {
|
|
600
|
+
ctx.logwarn(`Maximum expression nesting depth exceeded (${ctx.options.maxDepth})${source ? pos({ source }) : ""}`);
|
|
601
|
+
return nodes.map((n) => {
|
|
602
|
+
if (n.type == "text" || n.type == "var") return n;
|
|
603
|
+
if (n.type == "else" || n.type == "forEnd" || n.type == "ifEnd") {
|
|
604
|
+
const str = n.type == "else" ? "{{:else}}" : n.type == "ifEnd" ? "{{/if}}" : "{{/for}}";
|
|
605
|
+
return { type: "text", value: str };
|
|
606
|
+
}
|
|
607
|
+
if (n.type == "for") {
|
|
608
|
+
const second = n.varKey ? `, ${n.varKey}` : "";
|
|
609
|
+
return { type: "text", value: `{{#for ${n.varValue}${second} in ${strOperand(n.array)}}}` };
|
|
610
|
+
}
|
|
611
|
+
if (n.type == "if") {
|
|
612
|
+
const right = n.right ? ` ${n.operator} ${strOperand(n.right)}` : "";
|
|
613
|
+
return { type: "text", value: `{{#if ${strOperand(n.left)}${right}}}` };
|
|
614
|
+
}
|
|
615
|
+
n;
|
|
616
|
+
return { type: "text", value: "" };
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
const tree = [];
|
|
620
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
621
|
+
const node = nodes[i];
|
|
622
|
+
if (node.type == "text" || node.type == "var") {
|
|
623
|
+
tree.push(node);
|
|
624
|
+
}
|
|
625
|
+
if (node.type == "else" || node.type == "forEnd" || node.type == "ifEnd") {
|
|
626
|
+
const str = node.type == "else" ? "{{:else}}" : node.type == "ifEnd" ? "{{/if}}" : "{{/for}}";
|
|
627
|
+
ctx.logwarn(`Unexpected expression (${str})` + pos(node));
|
|
628
|
+
tree.push({ type: "text", value: str });
|
|
629
|
+
}
|
|
630
|
+
if (node.type == "for" || node.type == "if") {
|
|
631
|
+
const r = parseBlockWithElse(i);
|
|
632
|
+
if (!r) {
|
|
633
|
+
if (node.type == "for") {
|
|
634
|
+
const second = node.varKey ? `, ${node.varKey}` : "";
|
|
635
|
+
tree.push({ type: "text", value: `{{#for ${node.varValue}${second} in ${strOperand(node.array)}}}` });
|
|
636
|
+
} else if (node.type == "if") {
|
|
637
|
+
const right = node.right ? ` ${node.operator} ${strOperand(node.right)}` : "";
|
|
638
|
+
tree.push({ type: "text", value: `{{#if ${strOperand(node.left)}${right}}}` });
|
|
639
|
+
}
|
|
640
|
+
} else {
|
|
641
|
+
const { body, bodyElse, endI } = r;
|
|
642
|
+
i = endI;
|
|
643
|
+
tree.push({ ...node, body, bodyElse });
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
return tree;
|
|
648
|
+
function pos(node) {
|
|
649
|
+
return ` [Ln ${node.source.ln}, Col ${node.source.col}] (${node.source.fname})`;
|
|
650
|
+
}
|
|
651
|
+
function strOperand(v) {
|
|
652
|
+
return v.t == "val" ? JSON.stringify(v.val) : v.path.join(".");
|
|
653
|
+
}
|
|
654
|
+
function findBlockEnd(startI, type, skipElse) {
|
|
655
|
+
const stack = [];
|
|
656
|
+
for (let i = startI; i < nodes.length; i++) {
|
|
657
|
+
const node = nodes[i];
|
|
658
|
+
if (node.type == "if") stack.push("if");
|
|
659
|
+
else if (node.type == "for") stack.push("for");
|
|
660
|
+
else if (node.type == "forEnd" && stack.at(-1) == "for") stack.pop();
|
|
661
|
+
else if (node.type == "ifEnd" && stack.at(-1) == "if") stack.pop();
|
|
662
|
+
else if (stack.length == 0) {
|
|
663
|
+
if (node.type == "else" && !skipElse) return i;
|
|
664
|
+
if (node.type == "forEnd" && type == "for") return i;
|
|
665
|
+
if (node.type == "ifEnd" && type == "if") return i;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return -1;
|
|
669
|
+
}
|
|
670
|
+
function parseBlockWithElse(startI) {
|
|
671
|
+
const node = nodes[startI];
|
|
672
|
+
if (node.type != "for" && node.type != "if") return null;
|
|
673
|
+
const n = node;
|
|
674
|
+
const type = n.type;
|
|
675
|
+
const unclosed = (m) => ctx.logwarn(`Unclosed ${type} ${m}` + pos(n));
|
|
676
|
+
const bodyEnd = findBlockEnd(startI + 1, type, false);
|
|
677
|
+
if (bodyEnd < 0) {
|
|
678
|
+
unclosed("body");
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
const bodyNodes = nodes.slice(startI + 1, bodyEnd);
|
|
682
|
+
const body = buildExpTree(bodyNodes, ctx, depth + 1, n.source);
|
|
683
|
+
let endI = bodyEnd;
|
|
684
|
+
let bodyElse;
|
|
685
|
+
if (nodes[bodyEnd].type == "else") {
|
|
686
|
+
const bodyElseEnd = findBlockEnd(bodyEnd + 1, type, true);
|
|
687
|
+
if (bodyElseEnd < 0) {
|
|
688
|
+
unclosed("else block");
|
|
689
|
+
return null;
|
|
690
|
+
}
|
|
691
|
+
const bodyElseNodes = nodes.slice(bodyEnd + 1, bodyElseEnd);
|
|
692
|
+
bodyElse = buildExpTree(bodyElseNodes, ctx, depth + 1, n.source);
|
|
693
|
+
endI = bodyElseEnd;
|
|
694
|
+
}
|
|
695
|
+
return { body, bodyElse, endI };
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
function execExpTree(tree, variables, ctx) {
|
|
699
|
+
return tree.flatMap((node) => {
|
|
700
|
+
ctx.signal?.throwIfAborted();
|
|
701
|
+
if (node.type == "text") return ctx.countAsOutput(node.value);
|
|
702
|
+
if (node.type == "var") {
|
|
703
|
+
const n = node;
|
|
704
|
+
const value = execOperator(node.left, node.operator, node.right);
|
|
705
|
+
const rawValue = () => {
|
|
706
|
+
const right = n.right ? ` ${n.operator} ${strOperand(n.right)}` : "";
|
|
707
|
+
const value2 = `{{${strOperand(n.left)}${right}}}`;
|
|
708
|
+
return n.hasQuotes ? `"${value2}"` : value2;
|
|
709
|
+
};
|
|
710
|
+
if (value === void 0) ctx.logwarn("Undefined variable: " + rawValue() + pos(node));
|
|
711
|
+
const result = value === void 0 ? rawValue() : typeof value == "object" || node.hasQuotes ? JSON.stringify(value) : `${value}`;
|
|
712
|
+
return ctx.countAsOutput(result);
|
|
713
|
+
}
|
|
714
|
+
if (node.type == "if") {
|
|
715
|
+
const res = execOperator(node.left, node.operator, node.right);
|
|
716
|
+
const result = res && typeof res == "object" ? (Array.isArray(res) ? res : Object.keys(res)).length > 0 : !!res;
|
|
717
|
+
if (result) return execExpTree(node.body, variables, ctx);
|
|
718
|
+
if (node.bodyElse) return execExpTree(node.bodyElse, variables, ctx);
|
|
719
|
+
return "";
|
|
720
|
+
}
|
|
721
|
+
if (node.type == "for") {
|
|
722
|
+
if (!ctx.options.allowLoops) throw new UserInputError("Loop expressions are not allowed");
|
|
723
|
+
const arr = node.array.t == "val" ? node.array.val : getByPath(variables, node.array.path);
|
|
724
|
+
if (!arr || typeof arr == "object" && (Array.isArray(arr) ? arr : Object.keys(arr)).length == 0) {
|
|
725
|
+
if (node.bodyElse) return execExpTree(node.bodyElse, variables, ctx);
|
|
726
|
+
return "";
|
|
727
|
+
}
|
|
728
|
+
const values = (() => {
|
|
729
|
+
const n = node;
|
|
730
|
+
function checkLoopIterations(count, final = true) {
|
|
731
|
+
const max = ctx.options.maxLoopIterations === void 0 ? 1e3 : ctx.options.maxLoopIterations;
|
|
732
|
+
if (count > max)
|
|
733
|
+
throw new UserInputError(`Maximum loop iterations exceeded (${final ? count + " " : ""}> ${max})` + pos(n));
|
|
734
|
+
if (ctx.options.maxTotalIterations !== void 0 && ctx.iterations + count > ctx.options.maxTotalIterations)
|
|
735
|
+
throw new UserInputError(`Maximum total loop iterations exceeded (${ctx.options.maxTotalIterations})`);
|
|
736
|
+
}
|
|
737
|
+
if (typeof arr == "number" && Number.isInteger(arr)) {
|
|
738
|
+
if (arr < 0) {
|
|
739
|
+
ctx.logwarn(`For loop source is a negative integer (${arr})` + pos(node));
|
|
740
|
+
return [];
|
|
741
|
+
}
|
|
742
|
+
checkLoopIterations(arr);
|
|
743
|
+
return Array.from({ length: arr }, (_, i) => ({ k: i, v: i + 1 }));
|
|
744
|
+
}
|
|
745
|
+
if (typeof arr == "object") {
|
|
746
|
+
if (Array.isArray(arr)) {
|
|
747
|
+
checkLoopIterations(arr.length);
|
|
748
|
+
return arr.map((v, i) => ({ k: i, v }));
|
|
749
|
+
}
|
|
750
|
+
const keys = Object.keys(arr);
|
|
751
|
+
checkLoopIterations(keys.length);
|
|
752
|
+
return keys.map((k) => ({ k, v: arr[k] }));
|
|
753
|
+
}
|
|
754
|
+
if (typeof arr == "string") {
|
|
755
|
+
const values2 = [];
|
|
756
|
+
for (const v of arr) {
|
|
757
|
+
values2.push({ k: values2.length, v });
|
|
758
|
+
checkLoopIterations(values2.length, false);
|
|
759
|
+
}
|
|
760
|
+
return values2;
|
|
761
|
+
}
|
|
762
|
+
})();
|
|
763
|
+
if (!values) {
|
|
764
|
+
ctx.logwarn(`For loop source is not object, string or integer (${arr})` + pos(node));
|
|
765
|
+
return "";
|
|
766
|
+
}
|
|
767
|
+
return values.flatMap(({ k, v }) => {
|
|
768
|
+
ctx.signal?.throwIfAborted();
|
|
769
|
+
ctx.iterations++;
|
|
770
|
+
if (ctx.options.maxTotalIterations !== void 0 && ctx.iterations > ctx.options.maxTotalIterations)
|
|
771
|
+
throw new UserInputError(`Maximum total loop iterations exceeded (${ctx.options.maxTotalIterations})`);
|
|
772
|
+
const locals = Object.assign(/* @__PURE__ */ Object.create(null), variables);
|
|
773
|
+
locals[node.varValue] = v;
|
|
774
|
+
if (node.varKey) locals[node.varKey] = k;
|
|
775
|
+
return execExpTree(node.body, locals, ctx);
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
node;
|
|
779
|
+
return "";
|
|
780
|
+
});
|
|
781
|
+
function pos(node) {
|
|
782
|
+
return ` [Ln ${node.source.ln}, Col ${node.source.col}] (${node.source.fname})`;
|
|
783
|
+
}
|
|
784
|
+
function strOperand(v) {
|
|
785
|
+
return v.t == "val" ? JSON.stringify(v.val) : v.path.join(".");
|
|
786
|
+
}
|
|
787
|
+
function isEqual(obj1, obj2) {
|
|
788
|
+
if (obj1 === obj2) return true;
|
|
789
|
+
if (obj1 == null || obj2 == null || typeof obj1 !== "object" || typeof obj2 !== "object")
|
|
790
|
+
return obj1 == obj2;
|
|
791
|
+
const keys1 = Object.keys(obj1);
|
|
792
|
+
const keys2 = Object.keys(obj2);
|
|
793
|
+
if (keys1.length !== keys2.length) return false;
|
|
794
|
+
for (const key of keys1)
|
|
795
|
+
if (!Object.hasOwn(obj2, key) || !isEqual(obj1[key], obj2[key]))
|
|
796
|
+
return false;
|
|
797
|
+
return true;
|
|
798
|
+
}
|
|
799
|
+
function execOperator(left, operator, right) {
|
|
800
|
+
const leftV = left.t == "val" ? left.val : getByPath(variables, left.path);
|
|
801
|
+
if (!operator || !right) return leftV;
|
|
802
|
+
const rightV = right.t == "val" ? right.val : getByPath(variables, right.path);
|
|
803
|
+
if (operator == "==") return isEqual(leftV, rightV);
|
|
804
|
+
if (operator == "!=") return !isEqual(leftV, rightV);
|
|
805
|
+
if (operator == "<=") return leftV < rightV || isEqual(leftV, rightV);
|
|
806
|
+
if (operator == ">=") return leftV > rightV || isEqual(leftV, rightV);
|
|
807
|
+
if (operator == "<") return leftV < rightV;
|
|
808
|
+
if (operator == ">") return leftV > rightV;
|
|
809
|
+
if (operator == "in") return !!rightV && typeof rightV == "object" && (Array.isArray(rightV) ? rightV : Object.keys(rightV)).some((v) => isEqual(v, leftV));
|
|
810
|
+
if (operator == "and" || operator == "&&") return leftV && rightV;
|
|
811
|
+
if (operator == "or" || operator == "||") return leftV || rightV;
|
|
812
|
+
if (operator == "??") return leftV ?? rightV;
|
|
813
|
+
if (operator == "+") return leftV + rightV;
|
|
814
|
+
if (operator == "-") return leftV - rightV;
|
|
815
|
+
if (operator == "*") return leftV * rightV;
|
|
816
|
+
if (operator == "**") return Math.pow(leftV, rightV);
|
|
817
|
+
if (operator == "/") return rightV == 0 ? 0 : leftV / rightV;
|
|
818
|
+
if (operator == "//") return Math.floor(rightV == 0 ? 0 : leftV / rightV);
|
|
819
|
+
if (operator == "%") return rightV == 0 ? 0 : leftV % rightV;
|
|
820
|
+
operator;
|
|
821
|
+
return false;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
function parseIncludePath(value) {
|
|
825
|
+
value = value.trim();
|
|
826
|
+
const trimLeft = value.startsWith("-");
|
|
827
|
+
if (trimLeft) value = value.slice(1).trimStart();
|
|
828
|
+
const trimLeftInner = value.startsWith("-");
|
|
829
|
+
if (trimLeftInner) value = value.slice(1).trimStart();
|
|
830
|
+
const trimRight = value.endsWith("-");
|
|
831
|
+
if (trimRight) value = value.slice(0, -1).trimEnd();
|
|
832
|
+
const trimRightInner = value.endsWith("-");
|
|
833
|
+
if (trimRightInner) value = value.slice(0, -1).trimEnd();
|
|
834
|
+
const path7 = trimEnd(trimStart(value, "<", '"'), ">", '"');
|
|
835
|
+
return { path: path7, trimLeft, trimLeftInner, trimRight, trimRightInner };
|
|
836
|
+
}
|
|
837
|
+
function findClosingBrace(content, start) {
|
|
838
|
+
let depth = 0;
|
|
839
|
+
let inString = false;
|
|
840
|
+
let lineComment = false;
|
|
841
|
+
let blockComment = false;
|
|
842
|
+
for (let i = start; i < content.length; i++) {
|
|
843
|
+
const ch = content[i];
|
|
844
|
+
if (lineComment) {
|
|
845
|
+
if (ch == "\n") lineComment = false;
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
if (blockComment) {
|
|
849
|
+
if (ch == "*" && content[i + 1] == "/") {
|
|
850
|
+
blockComment = false;
|
|
851
|
+
i++;
|
|
852
|
+
}
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
if (inString) {
|
|
856
|
+
if (ch == "\\") i++;
|
|
857
|
+
else if (ch == '"') inString = false;
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
860
|
+
if (ch == '"') {
|
|
861
|
+
inString = true;
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
if (ch == "/" && content[i + 1] == "/") {
|
|
865
|
+
lineComment = true;
|
|
866
|
+
i++;
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
if (ch == "/" && content[i + 1] == "*") {
|
|
870
|
+
blockComment = true;
|
|
871
|
+
i++;
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
if (ch == "{") depth++;
|
|
875
|
+
else if (ch == "}" && --depth == 0) return i;
|
|
876
|
+
}
|
|
877
|
+
return -1;
|
|
878
|
+
}
|
|
879
|
+
async function parseIncludes(content, logwarn) {
|
|
880
|
+
const { default: JSONC } = await import("jsonc-simple-parser");
|
|
881
|
+
const re_doc = /^!!\(([^{}]*)\)\s*{(.*)}$/s;
|
|
882
|
+
const re_codeFence = /^( {0,3})(`{3,})([^`]*)$/;
|
|
883
|
+
const nodes = [];
|
|
884
|
+
let textStart = 0;
|
|
885
|
+
function pushText(start, end) {
|
|
886
|
+
if (start >= end) return;
|
|
887
|
+
const value = content.slice(start, end);
|
|
888
|
+
const prev = nodes.at(-1);
|
|
889
|
+
if (prev?.type === "text")
|
|
890
|
+
prev.value += value;
|
|
891
|
+
else
|
|
892
|
+
nodes.push({ type: "text", value });
|
|
893
|
+
}
|
|
894
|
+
let codeFence = 0;
|
|
895
|
+
for (let i = 0; i < content.length; i++) {
|
|
896
|
+
if (i != 0 && content[i - 1] != "\n") continue;
|
|
897
|
+
const lineEnd = content.indexOf("\n", i);
|
|
898
|
+
const line = content.slice(i, lineEnd < 0 ? content.length : lineEnd).replace(/\r$/, "");
|
|
899
|
+
const m_fence = re_codeFence.exec(line);
|
|
900
|
+
if (m_fence) {
|
|
901
|
+
const ticks = m_fence[2].length;
|
|
902
|
+
const suffix = m_fence[3];
|
|
903
|
+
if (codeFence == 0) {
|
|
904
|
+
codeFence = ticks;
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
if (ticks >= codeFence && suffix.trim() == "") {
|
|
908
|
+
codeFence = 0;
|
|
909
|
+
continue;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
if (codeFence != 0) continue;
|
|
913
|
+
if (content.slice(i, i + 2) !== "!!") continue;
|
|
914
|
+
const m_start = /^!!\([^{}]*\)\s*{/.exec(content.slice(i));
|
|
915
|
+
if (!m_start) continue;
|
|
916
|
+
const end = findClosingBrace(content, i + m_start[0].length - 1);
|
|
917
|
+
if (end < 0) continue;
|
|
918
|
+
const block = content.slice(i, end + 1);
|
|
919
|
+
const m_doc = re_doc.exec(block);
|
|
920
|
+
if (!m_doc) continue;
|
|
921
|
+
const { path: path7, trimLeft, trimLeftInner, trimRight, trimRightInner } = parseIncludePath(m_doc[1]);
|
|
922
|
+
if (!path7.toLowerCase().endsWith(".md")) continue;
|
|
923
|
+
let dict = {};
|
|
924
|
+
try {
|
|
925
|
+
dict = JSONC.parse(`{${m_doc[2]}}`);
|
|
926
|
+
} catch {
|
|
927
|
+
logwarn(`Can't parse include dict: {${m_doc[2].replaceAll("\n", " ")}}`);
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
let textEnd = i;
|
|
931
|
+
if (trimLeft)
|
|
932
|
+
while (textEnd > textStart && /\s/.test(content[textEnd - 1])) textEnd--;
|
|
933
|
+
pushText(textStart, textEnd);
|
|
934
|
+
nodes.push({ type: "include", path: path7, dict, sourceLineCount: block.split("\n").length, trimStart: trimLeftInner, trimEnd: trimRightInner });
|
|
935
|
+
let next = end + 1;
|
|
936
|
+
if (trimRight)
|
|
937
|
+
while (next < content.length && /\s/.test(content[next])) next++;
|
|
938
|
+
textStart = next;
|
|
939
|
+
i = next - 1;
|
|
940
|
+
}
|
|
941
|
+
pushText(textStart, content.length);
|
|
942
|
+
return nodes;
|
|
943
|
+
}
|
|
944
|
+
async function fixLinks(content, fixLink) {
|
|
945
|
+
const { default: JSONC } = await import("jsonc-simple-parser");
|
|
946
|
+
const re_codeFence = /^( {0,3})(`{3,})([^`]*)$/;
|
|
947
|
+
let result = "";
|
|
948
|
+
let textStart = 0;
|
|
949
|
+
function append(start, end) {
|
|
950
|
+
if (start >= end) return;
|
|
951
|
+
result += content.slice(start, end);
|
|
952
|
+
}
|
|
953
|
+
let codeFence = 0;
|
|
954
|
+
for (let i = 0; i < content.length; i++) {
|
|
955
|
+
if (i != 0 && content[i - 1] != "\n") continue;
|
|
956
|
+
const lineEnd = content.indexOf("\n", i);
|
|
957
|
+
const lineTextEnd = lineEnd < 0 ? content.length : lineEnd - (content[lineEnd - 1] == "\r" ? 1 : 0);
|
|
958
|
+
const line = content.slice(i, lineTextEnd);
|
|
959
|
+
const m_fence = re_codeFence.exec(line);
|
|
960
|
+
if (m_fence) {
|
|
961
|
+
const ticks = m_fence[2].length;
|
|
962
|
+
const suffix = m_fence[3];
|
|
963
|
+
if (codeFence == 0) {
|
|
964
|
+
codeFence = ticks;
|
|
965
|
+
continue;
|
|
966
|
+
}
|
|
967
|
+
if (ticks >= codeFence && suffix.trim() == "") {
|
|
968
|
+
codeFence = 0;
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
if (codeFence != 0) continue;
|
|
973
|
+
if (content.at(i) != "!") continue;
|
|
974
|
+
const m_img = /^!\[(.*)\]\((.*)\)({(.*)})?$/.exec(line);
|
|
975
|
+
if (m_img) {
|
|
976
|
+
append(textStart, i);
|
|
977
|
+
result += `![${m_img[1]}](${fixLink(m_img[2])})${m_img[3] || ""}`;
|
|
978
|
+
textStart = lineTextEnd;
|
|
979
|
+
i = lineEnd < 0 ? content.length : lineEnd;
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
const m_start = /^!!\([^{}]*\)\s*{/.exec(content.slice(i));
|
|
983
|
+
if (!m_start) continue;
|
|
984
|
+
const end = findClosingBrace(content, i + m_start[0].length - 1);
|
|
985
|
+
if (end < 0) continue;
|
|
986
|
+
const block = content.slice(i, end + 1);
|
|
987
|
+
const m_doc = /^!!\(([^{}]*)\)(\s*{(.*)})$/s.exec(block);
|
|
988
|
+
if (!m_doc) continue;
|
|
989
|
+
try {
|
|
990
|
+
JSONC.parse(m_doc[2].trim());
|
|
991
|
+
} catch {
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
const { path: path7, trimLeft, trimLeftInner, trimRight, trimRightInner } = parseIncludePath(m_doc[1]);
|
|
995
|
+
let fixedPath = fixLink(path7);
|
|
996
|
+
if (fixedPath.startsWith("-") || fixedPath.endsWith("-"))
|
|
997
|
+
fixedPath = `"${fixedPath}"`;
|
|
998
|
+
append(textStart, i);
|
|
999
|
+
result += `!!(${trimLeft ? "-" : ""}${trimLeftInner ? "-" : ""}${fixedPath}${trimRightInner ? "-" : ""}${trimRight ? "-" : ""})${m_doc[2]}`;
|
|
1000
|
+
textStart = end + 1;
|
|
1001
|
+
i = end;
|
|
1002
|
+
}
|
|
1003
|
+
append(textStart, content.length);
|
|
1004
|
+
return result;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// src/parser.ts
|
|
1008
|
+
async function parseMD(file, variables, templateOptions = {}, checkFilesIsInsidePath = false, signal, logwarn = console.warn) {
|
|
1009
|
+
signal?.throwIfAborted();
|
|
1010
|
+
const preprocessed = await preprocess(file, variables, checkFilesIsInsidePath, templateOptions, signal, logwarn);
|
|
1011
|
+
signal?.throwIfAborted();
|
|
1012
|
+
const lines = preprocessed.split("\n");
|
|
224
1013
|
const doc = new Doc();
|
|
1014
|
+
const warnAt = (line, message) => logwarn(`Line ${doc.mapSourceLine(line)}: ${message}`);
|
|
1015
|
+
(function processLineSkips() {
|
|
1016
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1017
|
+
const line = lines[i];
|
|
1018
|
+
if (line.at(0) != "\uE100") continue;
|
|
1019
|
+
const m = /\uE100(-?\d+)\uE101/.exec(line);
|
|
1020
|
+
if (!m) {
|
|
1021
|
+
lines[i] = line.replaceAll("\uE100", "").replaceAll("\uE101", "");
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
lines[i] = line.slice(m?.[0].length);
|
|
1025
|
+
doc.addLineSkip(i + 1, parseInt(m[1]));
|
|
1026
|
+
}
|
|
1027
|
+
})();
|
|
225
1028
|
try {
|
|
226
|
-
doc.title = toCapitalCase(trimEnd(
|
|
227
|
-
const stats = await
|
|
1029
|
+
doc.title = toCapitalCase(trimEnd(import_path2.default.parse(file).name, ".g"));
|
|
1030
|
+
const stats = await import_promises3.default.stat(file);
|
|
228
1031
|
doc.ctime = stats.birthtime;
|
|
229
1032
|
} catch {
|
|
230
1033
|
}
|
|
231
1034
|
function parseList(text, parts, ordered, level = 0) {
|
|
232
1035
|
const startIndex = isFinite(parseInt(parts[0])) ? parseInt(parts[0]) : 1;
|
|
233
1036
|
const mark = ordered ? parts[1] == "." ? "." : ")" : parts[1] == "*" ? "*" : "-";
|
|
234
|
-
const node = { type: "list", ordered, mark, startIndex, items: [{ type: "listItem", text }] };
|
|
1037
|
+
const node = { type: "list", ordered, mark, startIndex, sourceLine: lineI, items: [{ type: "listItem", text, sourceLine: lineI }] };
|
|
235
1038
|
const P = ordered ? "1)" : "*";
|
|
236
1039
|
function skipEmptyLines() {
|
|
237
1040
|
let emptyFound = false;
|
|
@@ -263,7 +1066,7 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
263
1066
|
if (ln.prefix == "" && last?.type == "listItem")
|
|
264
1067
|
last.text += "\n" + ln.text;
|
|
265
1068
|
else if (ln.prefix == P)
|
|
266
|
-
node.items.push({ type: "listItem", text: ln.text });
|
|
1069
|
+
node.items.push({ type: "listItem", text: ln.text, sourceLine: lineI + 1 });
|
|
267
1070
|
else break;
|
|
268
1071
|
} else if (ln.level > level) {
|
|
269
1072
|
if (ln.level == level + 1) ln = parseLine(ln.text);
|
|
@@ -292,19 +1095,19 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
292
1095
|
if (isFinite(h)) height = h;
|
|
293
1096
|
}
|
|
294
1097
|
const src = trimEnd(trimStart(parts[1], "<"), ">");
|
|
295
|
-
return { type: "image", text, src, width, height };
|
|
1098
|
+
return { type: "image", text, src, width, height, sourceLine: lineI };
|
|
296
1099
|
}
|
|
297
1100
|
function parseCode(text) {
|
|
298
1101
|
const header = text.split(" ");
|
|
299
1102
|
const title = header.slice(1).join(" ").trim() || void 0;
|
|
300
|
-
const node = { type: "code", lang: header[0], title, code: "" };
|
|
301
|
-
|
|
1103
|
+
const node = { type: "code", lang: header[0], title, code: "", sourceLine: lineI };
|
|
1104
|
+
const code = [];
|
|
302
1105
|
while (lineI < lines.length) {
|
|
303
1106
|
const ln = lines[lineI++];
|
|
304
1107
|
if (ln.startsWith("```")) break;
|
|
305
|
-
|
|
1108
|
+
code.push(ln);
|
|
306
1109
|
}
|
|
307
|
-
node.code =
|
|
1110
|
+
node.code = code.join("\n");
|
|
308
1111
|
return node;
|
|
309
1112
|
}
|
|
310
1113
|
function parseSection(text) {
|
|
@@ -317,9 +1120,78 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
317
1120
|
return {
|
|
318
1121
|
type: "sectionBreak",
|
|
319
1122
|
pageStart: unpaged ? -1 : isFinite(num) ? num : null,
|
|
320
|
-
orientation: portrait ? "portrait" : landscape ? "landscape" : null
|
|
1123
|
+
orientation: portrait ? "portrait" : landscape ? "landscape" : null,
|
|
1124
|
+
sourceLine: lineI
|
|
321
1125
|
};
|
|
322
1126
|
}
|
|
1127
|
+
function parseHeaderFooter(kind, options) {
|
|
1128
|
+
const value = options.trim().toLowerCase();
|
|
1129
|
+
if (value == "none" || kind == "footer" && value == "auto") {
|
|
1130
|
+
setHeaderFooter(kind, { type: value });
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
const align = value == "" ? "left" : /^align=(left|center|right)$/.exec(value)?.[1];
|
|
1134
|
+
if (!align) {
|
|
1135
|
+
warnAt(lineI, `Wrong ${kind} options: "${options}". Expected align=left, align=center, align=right${kind == "footer" ? ", none, or auto" : ", or none"}.`);
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
const end = `!!end${kind}`;
|
|
1139
|
+
const content = [];
|
|
1140
|
+
const initialLineI = lineI;
|
|
1141
|
+
let closed = false;
|
|
1142
|
+
while (lineI < lines.length) {
|
|
1143
|
+
const line = lines[lineI++];
|
|
1144
|
+
if (line.trim().toLowerCase() == end) {
|
|
1145
|
+
closed = true;
|
|
1146
|
+
break;
|
|
1147
|
+
}
|
|
1148
|
+
content.push(line);
|
|
1149
|
+
}
|
|
1150
|
+
if (!closed) {
|
|
1151
|
+
lineI = initialLineI;
|
|
1152
|
+
warnAt(initialLineI, `${kind} block is not closed`);
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
const nodes = [{ type: "text", text: content.join("\n").trim(), sourceLine: initialLineI + 1 }];
|
|
1156
|
+
findTables(nodes, false);
|
|
1157
|
+
const supported = nodes.filter((node) => node.type == "text" || node.type == "table");
|
|
1158
|
+
if (supported.length != 1 || supported[0]?.type == "text" && !supported[0].text) {
|
|
1159
|
+
warnAt(initialLineI, `${kind} must contain text or one table`);
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
setHeaderFooter(kind, { type: "content", align, nodes: supported });
|
|
1163
|
+
function setHeaderFooter(kind2, value2) {
|
|
1164
|
+
let section;
|
|
1165
|
+
for (let i = doc.nodes.length - 1; i >= 0; i--) {
|
|
1166
|
+
if (doc.nodes[i]?.type == "sectionBreak") {
|
|
1167
|
+
section = doc.nodes[i];
|
|
1168
|
+
break;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
if (section?.type == "sectionBreak") section[kind2] = value2;
|
|
1172
|
+
else doc[kind2] = value2;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
function parseAdmonition(parts) {
|
|
1176
|
+
const marker = parts[0];
|
|
1177
|
+
const type = parts[1];
|
|
1178
|
+
const title = parts[2];
|
|
1179
|
+
const attributes = parts[3];
|
|
1180
|
+
const content = [];
|
|
1181
|
+
const close = new RegExp(`^${marker}\\s*$`);
|
|
1182
|
+
const initialLineI = lineI;
|
|
1183
|
+
let closed = false;
|
|
1184
|
+
while (lineI < lines.length) {
|
|
1185
|
+
const line = lines[lineI++];
|
|
1186
|
+
if (close.test(line)) {
|
|
1187
|
+
closed = true;
|
|
1188
|
+
break;
|
|
1189
|
+
}
|
|
1190
|
+
content.push(line);
|
|
1191
|
+
}
|
|
1192
|
+
if (!closed) warnAt(initialLineI, `Admonition "${type}" is not closed`);
|
|
1193
|
+
return { type: "admonition", admonitionType: type, title, text: content.filter((t) => !!t.trim()).join("\n").trim(), attributes, sourceLine: initialLineI };
|
|
1194
|
+
}
|
|
323
1195
|
class RuleError extends Error {
|
|
324
1196
|
}
|
|
325
1197
|
;
|
|
@@ -389,9 +1261,43 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
389
1261
|
"text spacing after": (v) => doc.text.spacing.after = tryParseInt(v),
|
|
390
1262
|
"img text size": (v) => doc.img.text.size = tryParseInt(v),
|
|
391
1263
|
"img spacing before": (v) => doc.img.spacing.before = tryParseInt(v),
|
|
392
|
-
"img spacing after": (v) => doc.img.spacing.after = tryParseInt(v)
|
|
1264
|
+
"img spacing after": (v) => doc.img.spacing.after = tryParseInt(v),
|
|
1265
|
+
"formula spacing before": (v) => doc.formula.spacing.before = tryParseInt(v),
|
|
1266
|
+
"formula spacing after": (v) => doc.formula.spacing.after = tryParseInt(v)
|
|
393
1267
|
};
|
|
394
1268
|
const reRules = [
|
|
1269
|
+
{
|
|
1270
|
+
re: /^(admonition (all|note|info|tip|warning|danger) (spacing (before|after)|padding|indent|background|color|bar_width|icon size|icon|title_color|title))(.*)/,
|
|
1271
|
+
n: (m) => ({ rule: m[1], value: (m.at(-1) || "").trim() }),
|
|
1272
|
+
f(m) {
|
|
1273
|
+
const type = m[2];
|
|
1274
|
+
const property = m[3];
|
|
1275
|
+
const value = (m.at(-1) || "").trim();
|
|
1276
|
+
const valueKeepCase = text.slice(m[1].length).trim();
|
|
1277
|
+
const types = type == "all" ? ["note", "info", "tip", "warning", "danger"] : [type];
|
|
1278
|
+
for (const type2 of types) {
|
|
1279
|
+
const style = doc.admonition[type2];
|
|
1280
|
+
if (property == "spacing before") style.spacing.before = tryParseInt(value);
|
|
1281
|
+
else if (property == "spacing after") style.spacing.after = tryParseInt(value);
|
|
1282
|
+
else if (property == "indent") style.indent = tryParseFloat(value);
|
|
1283
|
+
else if (property == "padding") {
|
|
1284
|
+
const padding = value.split(" ").map(tryParseInt);
|
|
1285
|
+
if (padding.length != 2) throw new RuleError("Two values are required: horizontal vertical");
|
|
1286
|
+
style.padding = { top: padding[1], right: padding[0], bottom: padding[1], left: padding[0] };
|
|
1287
|
+
} else if (property == "background") style.background = tryParseColor(value);
|
|
1288
|
+
else if (property == "color") style.color = tryParseColor(value);
|
|
1289
|
+
else if (property == "bar_width") style.bar_width = tryParseFloat(value);
|
|
1290
|
+
else if (property == "icon size") style.icon_size = tryParseInt(value);
|
|
1291
|
+
else if (property == "icon") {
|
|
1292
|
+
choices(value, "on", "off");
|
|
1293
|
+
style.icon = value == "on";
|
|
1294
|
+
} else if (property == "title_color") {
|
|
1295
|
+
choices(value, "on", "off");
|
|
1296
|
+
style.title_color = value == "on";
|
|
1297
|
+
} else if (property == "title") style.title = valueKeepCase;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
},
|
|
395
1301
|
{
|
|
396
1302
|
re: /^(headings (h[1-6])(\+?) (size|spacing (before|after)|uppercase|indent))(.*)/,
|
|
397
1303
|
n: (m) => ({ rule: m[1], value: (m.at(-1) || "").trim() }),
|
|
@@ -429,11 +1335,11 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
429
1335
|
rule = n.rule;
|
|
430
1336
|
value = n.value;
|
|
431
1337
|
}
|
|
432
|
-
if (e instanceof RuleError)
|
|
1338
|
+
if (e instanceof RuleError) warnAt(lineI, `Rule "${rule}" wrong value: "${value}". ${e.message}`);
|
|
433
1339
|
else throw e;
|
|
434
1340
|
}
|
|
435
1341
|
} else {
|
|
436
|
-
|
|
1342
|
+
warnAt(lineI, `Wrong rule: "${text}"`);
|
|
437
1343
|
}
|
|
438
1344
|
function tryParseInt(v) {
|
|
439
1345
|
const num = parseInt(v);
|
|
@@ -450,6 +1356,11 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
450
1356
|
if (!isFinite(date.valueOf())) throw new RuleError("Only ISO 8601 date is allowed");
|
|
451
1357
|
return date;
|
|
452
1358
|
}
|
|
1359
|
+
function tryParseColor(v) {
|
|
1360
|
+
const color = /^#?([\da-f]{6})$/i.exec(v)?.[1];
|
|
1361
|
+
if (!color) throw new RuleError("Only #RRGGBB colors are allowed");
|
|
1362
|
+
return color;
|
|
1363
|
+
}
|
|
453
1364
|
function choices(v, ...vars) {
|
|
454
1365
|
if (vars.includes(v)) return v;
|
|
455
1366
|
throw new RuleError("Allowed: " + vars.join(", "));
|
|
@@ -463,66 +1374,85 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
463
1374
|
while (lineI < lines.length) {
|
|
464
1375
|
const line = lines[lineI++];
|
|
465
1376
|
const { prefix, text, parts } = parseLine(line);
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
1377
|
+
try {
|
|
1378
|
+
switch (prefix) {
|
|
1379
|
+
case "#":
|
|
1380
|
+
doc.appendTitle(text, 1, lineI);
|
|
1381
|
+
break;
|
|
1382
|
+
case "##":
|
|
1383
|
+
doc.appendTitle(text, 2, lineI);
|
|
1384
|
+
break;
|
|
1385
|
+
case "###":
|
|
1386
|
+
doc.appendTitle(text, 3, lineI);
|
|
1387
|
+
break;
|
|
1388
|
+
case "####":
|
|
1389
|
+
doc.appendTitle(text, 4, lineI);
|
|
1390
|
+
break;
|
|
1391
|
+
case "#####":
|
|
1392
|
+
doc.appendTitle(text, 5, lineI);
|
|
1393
|
+
break;
|
|
1394
|
+
case "######":
|
|
1395
|
+
doc.appendTitle(text, 6, lineI);
|
|
1396
|
+
break;
|
|
1397
|
+
case "*":
|
|
1398
|
+
doc.appendNode(parseList(text, parts, false));
|
|
1399
|
+
break;
|
|
1400
|
+
case "1)":
|
|
1401
|
+
doc.appendNode(parseList(text, parts, true));
|
|
1402
|
+
break;
|
|
1403
|
+
case "Img":
|
|
1404
|
+
doc.appendNode(parseImg(parts));
|
|
1405
|
+
break;
|
|
1406
|
+
case "Code":
|
|
1407
|
+
doc.appendNode(parseCode(text));
|
|
1408
|
+
break;
|
|
1409
|
+
case "Admonition":
|
|
1410
|
+
doc.appendNode(parseAdmonition(parts));
|
|
1411
|
+
break;
|
|
1412
|
+
case "Comment":
|
|
1413
|
+
skipComment();
|
|
1414
|
+
break;
|
|
1415
|
+
case "!!section":
|
|
1416
|
+
doc.appendNode(parseSection(text));
|
|
1417
|
+
break;
|
|
1418
|
+
case "!!header":
|
|
1419
|
+
parseHeaderFooter("header", text);
|
|
1420
|
+
break;
|
|
1421
|
+
case "!!footer":
|
|
1422
|
+
parseHeaderFooter("footer", text);
|
|
1423
|
+
break;
|
|
1424
|
+
case "!!rule":
|
|
1425
|
+
apllyRule(text);
|
|
1426
|
+
break;
|
|
1427
|
+
case "---":
|
|
1428
|
+
doc.appendNode({ type: "pageBreak", sourceLine: lineI });
|
|
1429
|
+
break;
|
|
1430
|
+
case "":
|
|
1431
|
+
case " ":
|
|
1432
|
+
const last = doc.nodes.at(-1);
|
|
1433
|
+
if (line.trim() != "" && last?.type == "text") {
|
|
1434
|
+
if (last.text == "") last.sourceLine = lineI;
|
|
1435
|
+
else last.text += "\n";
|
|
1436
|
+
last.text += line.trim();
|
|
1437
|
+
} else doc.appendText(line.trim(), lineI);
|
|
1438
|
+
break;
|
|
1439
|
+
default:
|
|
1440
|
+
prefix;
|
|
1441
|
+
throw new Error("switch default");
|
|
1442
|
+
}
|
|
1443
|
+
} catch (error) {
|
|
1444
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1445
|
+
throw new Error(`Line ${lineI}: ${message}`, { cause: error });
|
|
520
1446
|
}
|
|
521
1447
|
}
|
|
1448
|
+
doc.nodes.forEach((node) => {
|
|
1449
|
+
if (node.type == "admonition" && node.title == "\n")
|
|
1450
|
+
node.title = doc.admonition[node.admonitionType].title;
|
|
1451
|
+
});
|
|
522
1452
|
doc.nodes = doc.nodes.filter((n) => n.type != "text" || n.text != "");
|
|
523
|
-
findDocs(doc.nodes,
|
|
1453
|
+
await findDocs(doc.nodes, warnAt);
|
|
1454
|
+
findMath(doc.nodes, warnAt);
|
|
524
1455
|
findTables(doc.nodes);
|
|
525
|
-
findPageBreaks(doc.nodes);
|
|
526
1456
|
return doc;
|
|
527
1457
|
}
|
|
528
1458
|
function parseLine(line) {
|
|
@@ -539,7 +1469,7 @@ function parseLine(line) {
|
|
|
539
1469
|
const text = splited.slice(1).join(" ");
|
|
540
1470
|
let parts = [];
|
|
541
1471
|
if (prefix.startsWith("!")) {
|
|
542
|
-
const m_img =
|
|
1472
|
+
const m_img = /^!\[(.*)\]\((.*)\)({(.*)})?/.exec(line);
|
|
543
1473
|
if (m_img) return {
|
|
544
1474
|
prefix: "Img",
|
|
545
1475
|
text: line.trim(),
|
|
@@ -559,29 +1489,37 @@ function parseLine(line) {
|
|
|
559
1489
|
}
|
|
560
1490
|
}
|
|
561
1491
|
if (prefix.startsWith("```")) return { prefix: "Code", text: line.trim().slice(3), level, parts };
|
|
1492
|
+
const m_admonition = /^(:{3,})(note|info|tip|warning|danger)(?:\[([^\]]*)\])?(?:\{([^}]*)\})?\s*$/i.exec(line.trim());
|
|
1493
|
+
if (m_admonition) return {
|
|
1494
|
+
prefix: "Admonition",
|
|
1495
|
+
text: "",
|
|
1496
|
+
level,
|
|
1497
|
+
parts: [m_admonition[1], m_admonition[2].toLowerCase(), m_admonition[3] ?? "\n", m_admonition[4] || ""]
|
|
1498
|
+
};
|
|
562
1499
|
if (prefix.startsWith("<!--")) return { prefix: "Comment", text: line.trim().slice("<!--".length).trim(), level, parts };
|
|
563
|
-
if (["#", "##", "###", "####", "#####", "######", "*", "1)", "!!section", "!!rule"].includes(prefix))
|
|
1500
|
+
if (["#", "##", "###", "####", "#####", "######", "*", "1)", "!!section", "!!header", "!!footer", "!!rule"].includes(prefix))
|
|
564
1501
|
return { prefix, text, level: 0, parts };
|
|
565
1502
|
return { prefix: "", text: line.trim(), level, parts };
|
|
566
1503
|
}
|
|
567
|
-
function findDocs(nodes,
|
|
1504
|
+
async function findDocs(nodes, warnAt) {
|
|
1505
|
+
const { default: JSONC } = await import("jsonc-simple-parser");
|
|
568
1506
|
const re_doc = /^!!\(([^{}]*)\)\s*{(.*)}$/s;
|
|
569
|
-
const re_remTrailingComma = /,(\s*[}\]])/g;
|
|
570
1507
|
for (let i = 0; i < nodes.length; i++) {
|
|
571
1508
|
const node = nodes[i];
|
|
572
1509
|
if (node.type != "text") continue;
|
|
573
|
-
const m_doc = re_doc.exec(node.text.trimEnd()
|
|
1510
|
+
const m_doc = re_doc.exec(node.text.trimEnd());
|
|
574
1511
|
if (!m_doc) continue;
|
|
575
1512
|
let dict = {};
|
|
576
1513
|
try {
|
|
577
|
-
dict =
|
|
1514
|
+
dict = JSONC.parse(`{${m_doc[2]}}`);
|
|
578
1515
|
} catch {
|
|
579
|
-
|
|
1516
|
+
warnAt(node.sourceLine ?? -1, `Cant parse doc dict: {${m_doc[2].replaceAll("\n", " ")}}`);
|
|
580
1517
|
}
|
|
581
1518
|
nodes.splice(i, 1, {
|
|
582
1519
|
type: "externalDoc",
|
|
583
1520
|
path: trimEnd(trimStart(m_doc[1], "<", '"'), ">", '"'),
|
|
584
|
-
dict: stringifyDict(dict)
|
|
1521
|
+
dict: stringifyDict(dict),
|
|
1522
|
+
sourceLine: node.sourceLine
|
|
585
1523
|
});
|
|
586
1524
|
}
|
|
587
1525
|
}
|
|
@@ -605,7 +1543,7 @@ function stringifyDict(dict) {
|
|
|
605
1543
|
}
|
|
606
1544
|
return r;
|
|
607
1545
|
}
|
|
608
|
-
function findTables(nodes) {
|
|
1546
|
+
function findTables(nodes, hasHeader = true) {
|
|
609
1547
|
const re_sep = /^(\s*:?-+:?\s*(?<!\\)\|)+\s*:?-+:?\s*$/;
|
|
610
1548
|
const re_sep_oneCol = /^\|\s*:?-+:?\s*\|$/;
|
|
611
1549
|
const trim = (line) => {
|
|
@@ -620,19 +1558,20 @@ function findTables(nodes) {
|
|
|
620
1558
|
const lines = node.text.split("\n");
|
|
621
1559
|
const offset = lines[0].includes("|") ? 0 : 1;
|
|
622
1560
|
if (lines.length < 2 + offset) continue;
|
|
623
|
-
const
|
|
624
|
-
if (!re_sep.test(
|
|
625
|
-
const cols =
|
|
1561
|
+
const sep2 = trim(lines[1 + offset]);
|
|
1562
|
+
if (!re_sep.test(sep2) && !re_sep_oneCol.test(lines[1 + offset])) continue;
|
|
1563
|
+
const cols = sep2.split("|");
|
|
626
1564
|
const header = trim(lines[offset]).split(/(?<!\\)\|/).map((v) => v.trim());
|
|
627
1565
|
if (header.length != cols.length) continue;
|
|
628
1566
|
const align = cols.map((v) => v.trim()).map((v) => v.startsWith(":") && v.endsWith(":") ? "c" : v.endsWith(":") ? "r" : "l");
|
|
629
|
-
|
|
1567
|
+
let sourceLine = node.sourceLine + offset;
|
|
1568
|
+
const rows = [tableRow(sourceLine++, ...header)];
|
|
630
1569
|
for (const line of lines.slice(2 + offset)) {
|
|
631
1570
|
const row = trim(line).split(/(?<!\\)\|/).map((v) => v.trim());
|
|
632
1571
|
while (row.length < cols.length) row.push("");
|
|
633
|
-
rows.push(tableRow(...row));
|
|
1572
|
+
rows.push(tableRow(++sourceLine, ...row));
|
|
634
1573
|
}
|
|
635
|
-
const table = { type: "table", align, rows };
|
|
1574
|
+
const table = { type: "table", align, rows, sourceLine: node.sourceLine, ...hasHeader ? {} : { header: false } };
|
|
636
1575
|
nodes.splice(i, 1, table);
|
|
637
1576
|
const prev = nodes[i - 1];
|
|
638
1577
|
if (offset > 0) {
|
|
@@ -644,43 +1583,133 @@ function findTables(nodes) {
|
|
|
644
1583
|
}
|
|
645
1584
|
}
|
|
646
1585
|
}
|
|
647
|
-
function
|
|
648
|
-
const re_sep = /^\s*---+\s*$/;
|
|
1586
|
+
function findMath(nodes, warnAt) {
|
|
649
1587
|
for (let i = 0; i < nodes.length; i++) {
|
|
650
1588
|
const node = nodes[i];
|
|
651
|
-
if (node
|
|
652
|
-
if (!node.text.includes("---")) continue;
|
|
1589
|
+
if (node?.type != "text") continue;
|
|
653
1590
|
const lines = node.text.split("\n");
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
1591
|
+
const replacement = [];
|
|
1592
|
+
let text = [];
|
|
1593
|
+
let textStart = 0;
|
|
1594
|
+
const flush = () => {
|
|
1595
|
+
if (text.length)
|
|
1596
|
+
replacement.push({ type: "text", text: text.join("\n"), sourceLine: (node.sourceLine ?? 0) + textStart });
|
|
1597
|
+
text = [];
|
|
1598
|
+
};
|
|
1599
|
+
for (let line = 0; line < lines.length; line++) {
|
|
1600
|
+
if (lines[line]?.trim() != "$$") {
|
|
1601
|
+
if (!text.length) textStart = line;
|
|
1602
|
+
text.push(lines[line]);
|
|
1603
|
+
continue;
|
|
661
1604
|
}
|
|
1605
|
+
const start = line;
|
|
1606
|
+
let end = line + 1;
|
|
1607
|
+
while (end < lines.length && lines[end]?.trim() != "$$") end++;
|
|
1608
|
+
if (end >= lines.length) {
|
|
1609
|
+
warnAt((node.sourceLine ?? 0) + start, "Formula is not closed");
|
|
1610
|
+
if (!text.length) textStart = line;
|
|
1611
|
+
text.push(lines[line]);
|
|
1612
|
+
continue;
|
|
1613
|
+
}
|
|
1614
|
+
let id;
|
|
1615
|
+
let manualNumber;
|
|
1616
|
+
const idMatch = /^\s*(\[[a-zA-Zа-яА-ЯёЁ_\d#]+\])\s*$/.exec(text.at(-1) || "");
|
|
1617
|
+
const numberMatch = /^\s*(\(.*\))\s*$/.exec(text.at(-1) || "");
|
|
1618
|
+
if (idMatch) {
|
|
1619
|
+
id = `(${idMatch[1]})`;
|
|
1620
|
+
text.pop();
|
|
1621
|
+
} else if (numberMatch) {
|
|
1622
|
+
manualNumber = numberMatch[1];
|
|
1623
|
+
text.pop();
|
|
1624
|
+
}
|
|
1625
|
+
flush();
|
|
1626
|
+
replacement.push({
|
|
1627
|
+
type: "math",
|
|
1628
|
+
latex: lines.slice(start + 1, end).join("\n").trim(),
|
|
1629
|
+
title: id ?? manualNumber,
|
|
1630
|
+
sourceLine: (node.sourceLine ?? 0) + start
|
|
1631
|
+
});
|
|
1632
|
+
line = end;
|
|
1633
|
+
textStart = end + 1;
|
|
1634
|
+
}
|
|
1635
|
+
flush();
|
|
1636
|
+
if (replacement.some((n) => n.type == "math")) {
|
|
1637
|
+
nodes.splice(i, 1, ...replacement);
|
|
1638
|
+
i += replacement.length - 1;
|
|
662
1639
|
}
|
|
663
|
-
nodes.splice(i, 1, ...newNodes);
|
|
664
|
-
i += newNodes.length - 1;
|
|
665
1640
|
}
|
|
666
1641
|
}
|
|
667
1642
|
function runifyDoc(doc) {
|
|
668
1643
|
function runifyNode(node) {
|
|
669
|
-
if ("text" in node && node.text)
|
|
1644
|
+
if ("text" in node && node.text !== void 0)
|
|
670
1645
|
node.text = runifyText(node.text, doc.rainbow);
|
|
671
|
-
if ("title" in node && node.title)
|
|
1646
|
+
if ("title" in node && node.title !== void 0)
|
|
672
1647
|
node.title = runifyText(node.title, doc.rainbow);
|
|
673
1648
|
if (node.type == "table")
|
|
674
1649
|
node.rows.forEach((row) => row.forEach(runifyNode));
|
|
675
1650
|
if (node.type == "list")
|
|
676
1651
|
node.items.forEach(runifyNode);
|
|
1652
|
+
if (node.type == "sectionBreak") {
|
|
1653
|
+
runifyHeaderFooter(node.header);
|
|
1654
|
+
runifyHeaderFooter(node.footer);
|
|
1655
|
+
}
|
|
677
1656
|
}
|
|
678
1657
|
doc.nodes.forEach(runifyNode);
|
|
1658
|
+
runifyHeaderFooter(doc.header);
|
|
1659
|
+
runifyHeaderFooter(doc.footer);
|
|
679
1660
|
return doc;
|
|
1661
|
+
function runifyHeaderFooter(value) {
|
|
1662
|
+
if (value?.type != "content") return;
|
|
1663
|
+
value.nodes.forEach(runifyNode);
|
|
1664
|
+
}
|
|
680
1665
|
}
|
|
681
1666
|
var rainbowI = 0;
|
|
682
1667
|
function runifyText(text, rainbow = false) {
|
|
683
|
-
|
|
1668
|
+
const formulas = [];
|
|
1669
|
+
const codeSpans = [];
|
|
1670
|
+
function findClosingBackticks(from, count) {
|
|
1671
|
+
for (let i = from; i < text.length; ) {
|
|
1672
|
+
const start = text.indexOf("`", i);
|
|
1673
|
+
if (start < 0) return -1;
|
|
1674
|
+
let end = start + 1;
|
|
1675
|
+
while (text[end] == "`") end++;
|
|
1676
|
+
if (end - start == count) return start;
|
|
1677
|
+
i = end;
|
|
1678
|
+
}
|
|
1679
|
+
return -1;
|
|
1680
|
+
}
|
|
1681
|
+
;
|
|
1682
|
+
let preprocessedText = "";
|
|
1683
|
+
for (let i = 0; i < text.length; ) {
|
|
1684
|
+
if (text[i] == "`") {
|
|
1685
|
+
let delimiterEnd = i + 1;
|
|
1686
|
+
while (text[delimiterEnd] == "`") delimiterEnd++;
|
|
1687
|
+
const delimiterLength = delimiterEnd - i;
|
|
1688
|
+
const end = findClosingBackticks(delimiterEnd, delimiterLength);
|
|
1689
|
+
if (end >= 0) {
|
|
1690
|
+
codeSpans.push(text.slice(delimiterEnd, end));
|
|
1691
|
+
preprocessedText += `\uE002${codeSpans.length - 1}\uE003`;
|
|
1692
|
+
i = end + delimiterLength;
|
|
1693
|
+
continue;
|
|
1694
|
+
}
|
|
1695
|
+
preprocessedText += text.slice(i, delimiterEnd);
|
|
1696
|
+
i = delimiterEnd;
|
|
1697
|
+
continue;
|
|
1698
|
+
}
|
|
1699
|
+
if (text[i] == "$" && text[i - 1] != "\\" && text[i + 1] != "$") {
|
|
1700
|
+
let end = i + 1;
|
|
1701
|
+
while (end < text.length && text[end] != "\n" && (text[end] != "$" || text[end - 1] == "\\")) end++;
|
|
1702
|
+
if (end < text.length && text[end] == "$") {
|
|
1703
|
+
formulas.push(text.slice(i + 1, end));
|
|
1704
|
+
preprocessedText += `\uE000${formulas.length - 1}\uE001`;
|
|
1705
|
+
i = end + 1;
|
|
1706
|
+
continue;
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
preprocessedText += text[i++];
|
|
1710
|
+
}
|
|
1711
|
+
text = preprocessedText.replaceAll("\\$", "$");
|
|
1712
|
+
return replaceAmpCodes(text).replaceAll("\n", "	\n").replaceAll(/\s*<br>\s*/g, "\n").replaceAll("\u2014", "-").replaceAll(" - ", " \u2013 ").replaceAll(/(^|[^\p{L}\d_])"([\p{L}\d_])/gu, "$1\xAB$2").replaceAll(/([\p{L}\d_])"([^\p{L}\d_]|$)/gu, "$1\xBB$2").replaceAll(/(^|\s)(\*+)($|\s)/g, (sub) => sub.replaceAll("*", "⋆")).split(/(\[[^\]]*\]\([^)]*\))/g).map((p) => {
|
|
684
1713
|
const m = /\[([^\]]*)\]\(([^)]*)\)/.exec(p);
|
|
685
1714
|
if (!m) return { text: p };
|
|
686
1715
|
return { text: m[1], link: m[2] };
|
|
@@ -706,17 +1735,23 @@ function runifyText(text, rainbow = false) {
|
|
|
706
1735
|
link: rune.link,
|
|
707
1736
|
bold: rune.bold,
|
|
708
1737
|
italic: i % 2 == 1 && i != arr.length - 1 || rune.italic
|
|
709
|
-
}))).flat().map((rune) => rune.text.split(
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
1738
|
+
}))).flat().map((rune) => rune.text.split(/(\uE002\d+\uE003)/).map((p, i) => {
|
|
1739
|
+
const m = /^\uE002(\d+)\uE003$/.exec(p);
|
|
1740
|
+
return {
|
|
1741
|
+
...rune,
|
|
1742
|
+
text: m ? codeSpans[parseInt(m[1])] : p,
|
|
1743
|
+
mono: m ? true : rune.mono,
|
|
1744
|
+
linebreak: i == 0 && rune.linebreak
|
|
1745
|
+
};
|
|
1746
|
+
})).flat().map((rune) => rune.mono ? [rune] : rune.text.split(/(\uE000\d+\uE001)/).map((p, i) => {
|
|
1747
|
+
const m = /^\uE000(\d+)\uE001$/.exec(p);
|
|
1748
|
+
return {
|
|
1749
|
+
...rune,
|
|
1750
|
+
text: m ? formulas[parseInt(m[1])] : p,
|
|
1751
|
+
type: m ? "math" : rune.type,
|
|
1752
|
+
linebreak: i == 0 && rune.linebreak
|
|
1753
|
+
};
|
|
1754
|
+
})).flat().map((rune) => rune.link || rune.mono ? [rune] : rune.text.split(/(\[!?[a-zA-Zа-яА-ЯёЁ_\d#]+\s*[+-]?\s*\d*\])/g).map((p, i) => {
|
|
720
1755
|
const m = /\[(!?([a-zA-Zа-яА-ЯёЁ_\d]+|#)(\s*[-+]\s*\d+)?)\]/.exec(p);
|
|
721
1756
|
const v = m && m[1];
|
|
722
1757
|
const isVal = v?.at(0) == "!";
|
|
@@ -726,12 +1761,14 @@ function runifyText(text, rainbow = false) {
|
|
|
726
1761
|
type: v ? isVal ? "val" : "ref" : rune.type,
|
|
727
1762
|
linebreak: i == 0 && rune.linebreak
|
|
728
1763
|
};
|
|
729
|
-
})).flat().map((rune) => ({
|
|
1764
|
+
})).flat().map((rune, i, arr) => ({
|
|
730
1765
|
...rune,
|
|
731
1766
|
...!rune.type || rune.type == "text" ? {
|
|
732
|
-
text:
|
|
1767
|
+
text: formatTypography(
|
|
1768
|
+
arr[i + 1]?.type == "ref" || arr[i + 1]?.type == "val" ? rune.text.replace(/[ \t]+$/, NBSP) : rune.text
|
|
1769
|
+
).replaceAll("⋆", "*").replaceAll("​", "")
|
|
733
1770
|
} : {}
|
|
734
|
-
})).map((rune) => !rainbow || rune.type && rune.type != "text" ? [rune] : rune.text.split("").map((p, i) => ({
|
|
1771
|
+
})).map((rune) => !rainbow || rune.mono || rune.type && rune.type != "text" ? [rune] : rune.text.split("").map((p, i) => ({
|
|
735
1772
|
...rune,
|
|
736
1773
|
text: p,
|
|
737
1774
|
linebreak: i == 0 && rune.linebreak,
|
|
@@ -741,6 +1778,7 @@ function runifyText(text, rainbow = false) {
|
|
|
741
1778
|
function replaceAmpCodes(text) {
|
|
742
1779
|
const codes = {
|
|
743
1780
|
"|": "|",
|
|
1781
|
+
"&": "&",
|
|
744
1782
|
"­": "\xAD",
|
|
745
1783
|
"«": "\xAB",
|
|
746
1784
|
"»": "\xBB",
|
|
@@ -812,95 +1850,621 @@ function replaceAmpCodes(text) {
|
|
|
812
1850
|
}
|
|
813
1851
|
return text;
|
|
814
1852
|
}
|
|
1853
|
+
var NBSP = "\xA0";
|
|
1854
|
+
var NON_BREAKING_HYPHEN = "\u2011";
|
|
1855
|
+
var shortWords = ["\u0430", "\u0432", "\u0432\u043E", "\u0438", "\u043A", "\u043A\u043E", "\u043E", "\u043E\u0431", "\u043E\u0442", "\u0441", "\u0441\u043E", "\u0443", "\u0431\u0435\u0437", "\u0434\u043E", "\u0437\u0430", "\u0438\u0437", "\u043D\u0430", "\u043D\u0435", "\u043F\u043E", "\u043F\u043E\u0434", "\u043F\u0440\u0438", "\u043F\u0440\u043E", "\u0434\u043B\u044F"];
|
|
1856
|
+
var referenceTerms = [
|
|
1857
|
+
"\u0440\u0438\u0441\u0443\u043D\u043E\u043A",
|
|
1858
|
+
"\u0440\u0438\u0441\u0443\u043D\u043A\u0430",
|
|
1859
|
+
"\u0440\u0438\u0441\u0443\u043D\u043A\u0435",
|
|
1860
|
+
"\u0440\u0438\u0441\u0443\u043D\u043A\u0443",
|
|
1861
|
+
"\u0440\u0438\u0441\u0443\u043D\u043A\u043E\u043C",
|
|
1862
|
+
"\u0440\u0438\u0441\u0443\u043D\u043A\u0438",
|
|
1863
|
+
"\u0440\u0438\u0441\u0443\u043D\u043A\u043E\u0432",
|
|
1864
|
+
"\u0440\u0438\u0441\u0443\u043D\u043A\u0430\u0445",
|
|
1865
|
+
"\u0442\u0430\u0431\u043B\u0438\u0446\u0430",
|
|
1866
|
+
"\u0442\u0430\u0431\u043B\u0438\u0446\u044B",
|
|
1867
|
+
"\u0442\u0430\u0431\u043B\u0438\u0446\u0435",
|
|
1868
|
+
"\u0442\u0430\u0431\u043B\u0438\u0446\u0443",
|
|
1869
|
+
"\u0442\u0430\u0431\u043B\u0438\u0446\u0435\u0439",
|
|
1870
|
+
"\u0442\u0430\u0431\u043B\u0438\u0446",
|
|
1871
|
+
"\u0442\u0430\u0431\u043B\u0438\u0446\u0430\u0445",
|
|
1872
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433",
|
|
1873
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0430",
|
|
1874
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0435",
|
|
1875
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0443",
|
|
1876
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u043E\u043C",
|
|
1877
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0438",
|
|
1878
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u043E\u0432",
|
|
1879
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0430\u0445",
|
|
1880
|
+
"\u0444\u043E\u0440\u043C\u0443\u043B\u0430",
|
|
1881
|
+
"\u0444\u043E\u0440\u043C\u0443\u043B\u044B",
|
|
1882
|
+
"\u0444\u043E\u0440\u043C\u0443\u043B\u0435",
|
|
1883
|
+
"\u0444\u043E\u0440\u043C\u0443\u043B\u0443",
|
|
1884
|
+
"\u0444\u043E\u0440\u043C\u0443\u043B\u043E\u0439",
|
|
1885
|
+
"\u0444\u043E\u0440\u043C\u0443\u043B",
|
|
1886
|
+
"\u0444\u043E\u0440\u043C\u0443\u043B\u0430\u0445",
|
|
1887
|
+
"\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u0435",
|
|
1888
|
+
"\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u044F",
|
|
1889
|
+
"\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u0438",
|
|
1890
|
+
"\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u044E",
|
|
1891
|
+
"\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u0435\u043C",
|
|
1892
|
+
"\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u0439",
|
|
1893
|
+
"\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u044F\u0445",
|
|
1894
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B",
|
|
1895
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B\u0430",
|
|
1896
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B\u0435",
|
|
1897
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B\u0443",
|
|
1898
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B\u043E\u043C",
|
|
1899
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B\u044B",
|
|
1900
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B\u043E\u0432",
|
|
1901
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B\u0430\u0445",
|
|
1902
|
+
"\u0433\u043B\u0430\u0432\u0430",
|
|
1903
|
+
"\u0433\u043B\u0430\u0432\u044B",
|
|
1904
|
+
"\u0433\u043B\u0430\u0432\u0435",
|
|
1905
|
+
"\u0433\u043B\u0430\u0432\u0443",
|
|
1906
|
+
"\u0433\u043B\u0430\u0432\u043E\u0439",
|
|
1907
|
+
"\u0433\u043B\u0430\u0432",
|
|
1908
|
+
"\u0433\u043B\u0430\u0432\u0430\u0445",
|
|
1909
|
+
"\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435",
|
|
1910
|
+
"\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F",
|
|
1911
|
+
"\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438",
|
|
1912
|
+
"\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044E",
|
|
1913
|
+
"\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435\u043C",
|
|
1914
|
+
"\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0439",
|
|
1915
|
+
"\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F\u0445",
|
|
1916
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440",
|
|
1917
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440\u0430",
|
|
1918
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440\u0435",
|
|
1919
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440\u0443",
|
|
1920
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440\u043E\u043C",
|
|
1921
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440\u044B",
|
|
1922
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440\u043E\u0432",
|
|
1923
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440\u0430\u0445",
|
|
1924
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C",
|
|
1925
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0430",
|
|
1926
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0435",
|
|
1927
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0443",
|
|
1928
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u043E\u043C",
|
|
1929
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u044B",
|
|
1930
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u043E\u0432",
|
|
1931
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0430\u0445",
|
|
1932
|
+
"\u0441\u0445\u0435\u043C\u0430",
|
|
1933
|
+
"\u0441\u0445\u0435\u043C\u044B",
|
|
1934
|
+
"\u0441\u0445\u0435\u043C\u0435",
|
|
1935
|
+
"\u0441\u0445\u0435\u043C\u0443",
|
|
1936
|
+
"\u0441\u0445\u0435\u043C\u043E\u0439",
|
|
1937
|
+
"\u0441\u0445\u0435\u043C",
|
|
1938
|
+
"\u0441\u0445\u0435\u043C\u0430\u0445"
|
|
1939
|
+
];
|
|
1940
|
+
var referenceAbbreviations = ["\u0440\u0438\u0441", "\u0442\u0430\u0431\u043B", "\u043B\u0438\u0441\u0442", "\u0444\u043E\u0440\u043C", "\u0443\u0440", "\u0440\u0430\u0437\u0434", "\u0433\u043B", "\u043F\u0440\u0438\u043B"];
|
|
1941
|
+
var units = ["%", "\xB0C", "\xB0F", "\u043C\u043C", "\u0441\u043C", "\u0434\u043C", "\u043A\u043C", "\u043C\u043A\u043C", "\u043D\u043C", "\u043C", "\u043C\u0433", "\u043A\u0433", "\u0433", "\u043C\u043B", "\u043B", "\u043C\u0441", "\u043C\u043A\u0441", "\u043D\u0441", "\u0441", "\u043C\u0438\u043D", "\u0447", "\u0413\u0446", "\u043A\u0413\u0446", "\u041C\u0413\u0446", "\u0413\u0413\u0446", "\u0412\u0442", "\u043A\u0412\u0442", "\u041C\u0412\u0442", "\u0412", "\u043A\u0412", "\u0410", "\u043C\u0410", "\u041E\u043C", "\u043A\u041E\u043C", "\u041C\u041E\u043C", "\u0411", "\u041A\u0411", "\u041C\u0411", "\u0413\u0411", "\u0422\u0411", "\u0431\u0438\u0442", "\u041A\u0431\u0438\u0442", "\u041C\u0431\u0438\u0442", "\u0413\u0431\u0438\u0442", "\u0431\u0438\u0442/\u0441", "\u041A\u0431\u0438\u0442/\u0441", "\u041C\u0431\u0438\u0442/\u0441", "\u0413\u0431\u0438\u0442/\u0441"];
|
|
1942
|
+
function escapeRegExp(value) {
|
|
1943
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1944
|
+
}
|
|
1945
|
+
function createAlternation(values) {
|
|
1946
|
+
return [...values].sort((a, b) => b.length - a.length).map(escapeRegExp).join("|");
|
|
1947
|
+
}
|
|
1948
|
+
var shortWordsPattern = createAlternation(shortWords);
|
|
1949
|
+
var referenceTermsPattern = createAlternation(referenceTerms);
|
|
1950
|
+
var referenceAbbreviationsPattern = createAlternation(referenceAbbreviations);
|
|
1951
|
+
var unitsPattern = createAlternation(units);
|
|
1952
|
+
var referenceIdentifierPattern = String.raw`(?:\(\s*\d+(?:\.\d+)*\s*\)|\d+(?:\.\d+)*|[А-ЯЁA-Z](?:[^\p{L}]|$))`;
|
|
1953
|
+
function formatTypography(text) {
|
|
1954
|
+
return text.replace(
|
|
1955
|
+
new RegExp(`(^|[^\\p{L}\\p{N}_])(${shortWordsPattern})[ \\t]+(?=\\S)`, "giu"),
|
|
1956
|
+
(_, prefix, word) => `${prefix}${word}${NBSP}`
|
|
1957
|
+
).replace(
|
|
1958
|
+
new RegExp(`(^|[^\\p{L}\\p{N}_])(${referenceTermsPattern})[ \\t]+(?=${referenceIdentifierPattern})`, "giu"),
|
|
1959
|
+
(_, prefix, term) => `${prefix}${term}${NBSP}`
|
|
1960
|
+
).replace(
|
|
1961
|
+
new RegExp(`(^|[^\\p{L}\\p{N}_])(${referenceAbbreviationsPattern})\\.[ \\t]+(?=${referenceIdentifierPattern})`, "giu"),
|
|
1962
|
+
(_, prefix, abbreviation) => `${prefix}${abbreviation}.${NBSP}`
|
|
1963
|
+
).replace(/\b(п|пп|ч|ст|с|стр|абз)\.[ \t]+(?=\d)/giu, `$1.${NBSP}`).replace(/№[ \t]+(?=\d)/gu, `\u2116${NBSP}`).replace(/(§§?)[ \t]+(?=\d)/gu, `$1${NBSP}`).replace(/\b(ГОСТ)[ \t]+(?=Р(?:[ \t]+\d)|\d)/giu, `$1${NBSP}`).replace(
|
|
1964
|
+
/\b(ГОСТ\u00A0Р|ГОСТ Р)[ \t]+(?=\d)/giu,
|
|
1965
|
+
(_, standard) => `${standard.replace(" ", NBSP)}${NBSP}`
|
|
1966
|
+
).replace(/\b(ISO|IEC|IEEE|RFC|DIN|EN)[ \t]+(?=\d)/g, `$1${NBSP}`).replace(/\b(г|ул|пр|просп|пер|бул|наб|пл|д|корп|стр|кв)\.[ \t]+(?=\S)/giu, `$1.${NBSP}`).replace(/(\d{4})[ \t]+(?=гг?\.)/gu, `$1${NBSP}`).replace(/([А-ЯЁA-Z])\.[ \t]+([А-ЯЁA-Z])\.[ \t]+(?=[А-ЯЁA-Z][\p{L}-]+)/gu, `$1.${NBSP}$2.${NBSP}`).replace(/([А-ЯЁA-Z])\.[ \t]+(?=[А-ЯЁA-Z][\p{L}-]+)/gu, `$1.${NBSP}`).replace(new RegExp(`(\\d)[ \\t]+(?=(?:${unitsPattern})(?=$|[^\\p{L}]))`, "giu"), `$1${NBSP}`).replaceAll(/(?<=[\p{L}\p{N}])-(?=[\p{L}\p{N}])/gu, NON_BREAKING_HYPHEN);
|
|
1967
|
+
}
|
|
815
1968
|
|
|
816
1969
|
// src/serializer.ts
|
|
817
|
-
var
|
|
818
|
-
var
|
|
1970
|
+
var fs4 = __toESM(require("fs"));
|
|
1971
|
+
var import_docx2 = require("docx");
|
|
819
1972
|
var import_image_size = require("image-size");
|
|
820
|
-
var
|
|
821
|
-
var import_prismjs = __toESM(require("prismjs"));
|
|
822
|
-
var import_prism_clike = require("prismjs/components/prism-clike");
|
|
823
|
-
var import_prism_markup = require("prismjs/components/prism-markup");
|
|
824
|
-
var import_prism_css = require("prismjs/components/prism-css");
|
|
825
|
-
var import_prism_javascript = require("prismjs/components/prism-javascript");
|
|
826
|
-
var import_prism_typescript = require("prismjs/components/prism-typescript");
|
|
827
|
-
var import_prism_jsx = require("prismjs/components/prism-jsx");
|
|
828
|
-
var import_prism_tsx = require("prismjs/components/prism-tsx");
|
|
829
|
-
var import_prism_json = require("prismjs/components/prism-json");
|
|
830
|
-
var import_prism_bash = require("prismjs/components/prism-bash");
|
|
831
|
-
var import_prism_powershell = require("prismjs/components/prism-powershell");
|
|
832
|
-
var import_prism_python = require("prismjs/components/prism-python");
|
|
833
|
-
var import_prism_java = require("prismjs/components/prism-java");
|
|
834
|
-
var import_prism_c = require("prismjs/components/prism-c");
|
|
835
|
-
var import_prism_cpp = require("prismjs/components/prism-cpp");
|
|
836
|
-
var import_prism_csharp = require("prismjs/components/prism-csharp");
|
|
837
|
-
var import_prism_go = require("prismjs/components/prism-go");
|
|
838
|
-
var import_prism_rust = require("prismjs/components/prism-rust");
|
|
839
|
-
var import_prism_php = require("prismjs/components/prism-php");
|
|
840
|
-
var import_prism_ruby = require("prismjs/components/prism-ruby");
|
|
841
|
-
var import_prism_swift = require("prismjs/components/prism-swift");
|
|
842
|
-
var import_prism_kotlin = require("prismjs/components/prism-kotlin");
|
|
843
|
-
var import_prism_sql = require("prismjs/components/prism-sql");
|
|
844
|
-
var import_prism_yaml = require("prismjs/components/prism-yaml");
|
|
845
|
-
var import_prism_markdown = require("prismjs/components/prism-markdown");
|
|
846
|
-
var import_prism_docker = require("prismjs/components/prism-docker");
|
|
847
|
-
var import_prism_nginx = require("prismjs/components/prism-nginx");
|
|
1973
|
+
var import_path3 = __toESM(require("path"));
|
|
848
1974
|
|
|
849
|
-
// src/
|
|
850
|
-
var
|
|
851
|
-
|
|
852
|
-
|
|
1975
|
+
// src/math.ts
|
|
1976
|
+
var import_docx = require("docx");
|
|
1977
|
+
var Omml = class extends import_docx.XmlComponent {
|
|
1978
|
+
constructor(tag, children = [], attributes = {}) {
|
|
1979
|
+
super(tag);
|
|
1980
|
+
if (Object.keys(attributes).length) this.root.push({ _attr: attributes });
|
|
1981
|
+
this.root.push(...children);
|
|
1982
|
+
}
|
|
1983
|
+
};
|
|
1984
|
+
var symbols = {
|
|
1985
|
+
alpha: "\u03B1",
|
|
1986
|
+
beta: "\u03B2",
|
|
1987
|
+
gamma: "\u03B3",
|
|
1988
|
+
delta: "\u03B4",
|
|
1989
|
+
epsilon: "\u03B5",
|
|
1990
|
+
varepsilon: "\u03B5",
|
|
1991
|
+
zeta: "\u03B6",
|
|
1992
|
+
eta: "\u03B7",
|
|
1993
|
+
theta: "\u03B8",
|
|
1994
|
+
vartheta: "\u03D1",
|
|
1995
|
+
iota: "\u03B9",
|
|
1996
|
+
kappa: "\u03BA",
|
|
1997
|
+
lambda: "\u03BB",
|
|
1998
|
+
mu: "\u03BC",
|
|
1999
|
+
nu: "\u03BD",
|
|
2000
|
+
xi: "\u03BE",
|
|
2001
|
+
pi: "\u03C0",
|
|
2002
|
+
rho: "\u03C1",
|
|
2003
|
+
sigma: "\u03C3",
|
|
2004
|
+
tau: "\u03C4",
|
|
2005
|
+
upsilon: "\u03C5",
|
|
2006
|
+
phi: "\u03C6",
|
|
2007
|
+
varphi: "\u03D5",
|
|
2008
|
+
chi: "\u03C7",
|
|
2009
|
+
psi: "\u03C8",
|
|
2010
|
+
omega: "\u03C9",
|
|
2011
|
+
Gamma: "\u0393",
|
|
2012
|
+
Delta: "\u0394",
|
|
2013
|
+
Theta: "\u0398",
|
|
2014
|
+
Lambda: "\u039B",
|
|
2015
|
+
Xi: "\u039E",
|
|
2016
|
+
Pi: "\u03A0",
|
|
2017
|
+
Sigma: "\u03A3",
|
|
2018
|
+
Upsilon: "\u03A5",
|
|
2019
|
+
Phi: "\u03A6",
|
|
2020
|
+
Psi: "\u03A8",
|
|
2021
|
+
Omega: "\u03A9",
|
|
2022
|
+
cdot: "\xB7",
|
|
2023
|
+
times: "\xD7",
|
|
2024
|
+
div: "\xF7",
|
|
2025
|
+
pm: "\xB1",
|
|
2026
|
+
mp: "\u2213",
|
|
2027
|
+
ast: "\u2217",
|
|
2028
|
+
star: "\u22C6",
|
|
2029
|
+
circ: "\u2218",
|
|
2030
|
+
bullet: "\u2219",
|
|
2031
|
+
oplus: "\u2295",
|
|
2032
|
+
ominus: "\u2296",
|
|
2033
|
+
otimes: "\u2297",
|
|
2034
|
+
oslash: "\u2298",
|
|
2035
|
+
odot: "\u2299",
|
|
2036
|
+
setminus: "\u2216",
|
|
2037
|
+
wedge: "\u2227",
|
|
2038
|
+
vee: "\u2228",
|
|
2039
|
+
le: "\u2264",
|
|
2040
|
+
leq: "\u2264",
|
|
2041
|
+
ge: "\u2265",
|
|
2042
|
+
geq: "\u2265",
|
|
2043
|
+
neq: "\u2260",
|
|
2044
|
+
ne: "\u2260",
|
|
2045
|
+
approx: "\u2248",
|
|
2046
|
+
equiv: "\u2261",
|
|
2047
|
+
sim: "\u223C",
|
|
2048
|
+
simeq: "\u2243",
|
|
2049
|
+
cong: "\u2245",
|
|
2050
|
+
propto: "\u221D",
|
|
2051
|
+
ll: "\u226A",
|
|
2052
|
+
gg: "\u226B",
|
|
2053
|
+
infty: "\u221E",
|
|
2054
|
+
partial: "\u2202",
|
|
2055
|
+
nabla: "\u2207",
|
|
2056
|
+
in: "\u2208",
|
|
2057
|
+
notin: "\u2209",
|
|
2058
|
+
ni: "\u220B",
|
|
2059
|
+
subset: "\u2282",
|
|
2060
|
+
subseteq: "\u2286",
|
|
2061
|
+
superset: "\u2283",
|
|
2062
|
+
supseteq: "\u2287",
|
|
2063
|
+
emptyset: "\u2205",
|
|
2064
|
+
varnothing: "\u2205",
|
|
2065
|
+
cup: "\u222A",
|
|
2066
|
+
cap: "\u2229",
|
|
2067
|
+
perp: "\u22A5",
|
|
2068
|
+
parallel: "\u2225",
|
|
2069
|
+
mid: "\u2223",
|
|
2070
|
+
angle: "\u2220",
|
|
2071
|
+
therefore: "\u2234",
|
|
2072
|
+
because: "\u2235",
|
|
2073
|
+
to: "\u2192",
|
|
2074
|
+
rightarrow: "\u2192",
|
|
2075
|
+
leftarrow: "\u2190",
|
|
2076
|
+
leftrightarrow: "\u2194",
|
|
2077
|
+
mapsto: "\u21A6",
|
|
2078
|
+
Rightarrow: "\u21D2",
|
|
2079
|
+
Leftarrow: "\u21D0",
|
|
2080
|
+
Leftrightarrow: "\u21D4",
|
|
2081
|
+
uparrow: "\u2191",
|
|
2082
|
+
downarrow: "\u2193",
|
|
2083
|
+
updownarrow: "\u2195",
|
|
2084
|
+
forall: "\u2200",
|
|
2085
|
+
exists: "\u2203",
|
|
2086
|
+
neg: "\xAC",
|
|
2087
|
+
land: "\u2227",
|
|
2088
|
+
lor: "\u2228",
|
|
2089
|
+
degree: "\xB0",
|
|
2090
|
+
ell: "\u2113",
|
|
2091
|
+
prime: "\u2032",
|
|
2092
|
+
hbar: "\u210F",
|
|
2093
|
+
aleph: "\u2135",
|
|
2094
|
+
Re: "\u211C",
|
|
2095
|
+
Im: "\u2111",
|
|
2096
|
+
ldots: "\u2026",
|
|
2097
|
+
dots: "\u2026",
|
|
2098
|
+
cdots: "\u22EF",
|
|
2099
|
+
vdots: "\u22EE",
|
|
2100
|
+
ddots: "\u22F1"
|
|
2101
|
+
};
|
|
2102
|
+
var operators = { sin: "sin", cos: "cos", tan: "tan", cot: "cot", sec: "sec", csc: "csc", log: "log", ln: "ln", exp: "exp", max: "max", min: "min", gcd: "gcd", det: "det" };
|
|
2103
|
+
function run(text, style) {
|
|
2104
|
+
const value = style == "plain" ? "p" : style == "bold" ? "b" : style == "italic" ? "i" : void 0;
|
|
2105
|
+
return new Omml("m:r", [
|
|
2106
|
+
...style == "double-struck" || style == "script" ? [new Omml("m:rPr", [new Omml("m:scr", [], { "m:val": style }), new Omml("m:sty", [], { "m:val": "p" })])] : [],
|
|
2107
|
+
...value ? [new Omml("m:rPr", [new Omml("m:sty", [], { "m:val": value })])] : [],
|
|
2108
|
+
new Omml("m:t", [text], { "xml:space": "preserve" })
|
|
2109
|
+
]);
|
|
2110
|
+
}
|
|
2111
|
+
function arg(children) {
|
|
2112
|
+
return new Omml("m:e", children);
|
|
2113
|
+
}
|
|
2114
|
+
function delimiter(children, begin, end) {
|
|
2115
|
+
return new Omml("m:d", [
|
|
2116
|
+
new Omml("m:dPr", [new Omml("m:begChr", [], { "m:val": begin }), new Omml("m:endChr", [], { "m:val": end })]),
|
|
2117
|
+
arg(children)
|
|
2118
|
+
]);
|
|
2119
|
+
}
|
|
2120
|
+
function latexToOmml(latex, para = false) {
|
|
2121
|
+
const parser = new LatexParser(latex);
|
|
2122
|
+
const body = parser.sequence();
|
|
2123
|
+
if (!parser.done()) throw new Error(`unexpected "${parser.peek()}"`);
|
|
2124
|
+
const math = new Omml("m:oMath", body);
|
|
2125
|
+
return para ? new Omml("m:oMathPara", [math]) : math;
|
|
2126
|
+
}
|
|
2127
|
+
var LatexParser = class {
|
|
2128
|
+
constructor(source) {
|
|
2129
|
+
this.source = source;
|
|
2130
|
+
}
|
|
2131
|
+
source;
|
|
2132
|
+
i = 0;
|
|
2133
|
+
done() {
|
|
2134
|
+
return this.i >= this.source.length;
|
|
2135
|
+
}
|
|
2136
|
+
peek() {
|
|
2137
|
+
return this.source[this.i] || "";
|
|
2138
|
+
}
|
|
2139
|
+
take() {
|
|
2140
|
+
return this.source[this.i++] || "";
|
|
2141
|
+
}
|
|
2142
|
+
skipWhitespace() {
|
|
2143
|
+
while (/\s/.test(this.peek())) this.take();
|
|
2144
|
+
}
|
|
2145
|
+
command(name) {
|
|
2146
|
+
return this.source.slice(this.i).startsWith(`\\${name}`) && !/[A-Za-z]/.test(this.source[this.i + name.length + 1] || "");
|
|
2147
|
+
}
|
|
2148
|
+
sequence(stop, style, preserveWhitespace = false, boundary) {
|
|
2149
|
+
const result = [];
|
|
2150
|
+
while (!this.done() && this.peek() != stop && !boundary?.()) {
|
|
2151
|
+
if (/\s/.test(this.peek())) {
|
|
2152
|
+
this.take();
|
|
2153
|
+
if (preserveWhitespace) result.push(run(" ", style));
|
|
2154
|
+
continue;
|
|
2155
|
+
}
|
|
2156
|
+
if (this.command("displaystyle") || this.command("textstyle")) {
|
|
2157
|
+
this.take();
|
|
2158
|
+
while (/[A-Za-z]/.test(this.peek())) this.take();
|
|
2159
|
+
continue;
|
|
2160
|
+
}
|
|
2161
|
+
if (this.source.startsWith("\\!", this.i)) {
|
|
2162
|
+
this.i += 2;
|
|
2163
|
+
continue;
|
|
2164
|
+
}
|
|
2165
|
+
result.push(this.decoratedAtom(style, preserveWhitespace));
|
|
2166
|
+
}
|
|
2167
|
+
if (stop) {
|
|
2168
|
+
if (this.take() != stop) throw new Error(`missing "${stop}"`);
|
|
2169
|
+
}
|
|
2170
|
+
return result;
|
|
2171
|
+
}
|
|
2172
|
+
decoratedAtom(style, preserveWhitespace = false) {
|
|
2173
|
+
let value = this.atom(style, preserveWhitespace);
|
|
2174
|
+
let sub;
|
|
2175
|
+
let sup;
|
|
2176
|
+
while (true) {
|
|
2177
|
+
const beforeSpace = this.i;
|
|
2178
|
+
this.skipWhitespace();
|
|
2179
|
+
if (this.peek() != "_" && this.peek() != "^") {
|
|
2180
|
+
this.i = beforeSpace;
|
|
2181
|
+
break;
|
|
2182
|
+
}
|
|
2183
|
+
const marker = this.take();
|
|
2184
|
+
const script = this.groupOrAtom(style);
|
|
2185
|
+
if (marker == "_" ? sub : sup) throw new Error("duplicate script");
|
|
2186
|
+
if (marker == "_") sub = script;
|
|
2187
|
+
else sup = script;
|
|
2188
|
+
}
|
|
2189
|
+
if (sub && sup) value = new Omml("m:sSubSup", [new Omml("m:e", [value]), new Omml("m:sub", sub), new Omml("m:sup", sup)]);
|
|
2190
|
+
else if (sub) value = new Omml("m:sSub", [new Omml("m:e", [value]), new Omml("m:sub", sub)]);
|
|
2191
|
+
else if (sup) value = new Omml("m:sSup", [new Omml("m:e", [value]), new Omml("m:sup", sup)]);
|
|
2192
|
+
return value;
|
|
2193
|
+
}
|
|
2194
|
+
groupOrAtom(style, preserveWhitespace = false) {
|
|
2195
|
+
this.skipWhitespace();
|
|
2196
|
+
if (this.done() || ["}", "_", "^", "&"].includes(this.peek())) throw new Error("argument expected");
|
|
2197
|
+
if (this.peek() == "{") {
|
|
2198
|
+
this.take();
|
|
2199
|
+
return this.sequence("}", style, preserveWhitespace);
|
|
2200
|
+
}
|
|
2201
|
+
return [this.atom(style)];
|
|
2202
|
+
}
|
|
2203
|
+
atom(style, preserveWhitespace = false) {
|
|
2204
|
+
const ch = this.take();
|
|
2205
|
+
if (!ch || ["}", "_", "^", "&"].includes(ch)) throw new Error(`unexpected "${ch}"`);
|
|
2206
|
+
if (ch == "{") return new Omml("m:box", [arg(this.sequence("}", style, preserveWhitespace))]);
|
|
2207
|
+
if (ch != "\\") return run(ch, style);
|
|
2208
|
+
let name = "";
|
|
2209
|
+
while (/[A-Za-z]/.test(this.peek())) name += this.take();
|
|
2210
|
+
if (!name) {
|
|
2211
|
+
const escaped = this.take();
|
|
2212
|
+
if (!escaped) throw new Error("command expected");
|
|
2213
|
+
const spaces = { ",": "\u2009", ";": "\u2004", ":": "\u2005", " ": " " };
|
|
2214
|
+
if (spaces[escaped]) return run(spaces[escaped], style);
|
|
2215
|
+
if (escaped == "!") return run("", style);
|
|
2216
|
+
return run(escaped, style);
|
|
2217
|
+
}
|
|
2218
|
+
this.skipWhitespace();
|
|
2219
|
+
if (name == "frac" || name == "dfrac" || name == "tfrac") return new Omml("m:f", [new Omml("m:num", this.groupOrAtom(style)), new Omml("m:den", this.groupOrAtom(style))]);
|
|
2220
|
+
if (name == "sqrt") {
|
|
2221
|
+
let degree;
|
|
2222
|
+
if (this.peek() == "[") {
|
|
2223
|
+
this.take();
|
|
2224
|
+
degree = this.sequence("]", style);
|
|
2225
|
+
}
|
|
2226
|
+
return new Omml("m:rad", [new Omml("m:radPr", degree ? [] : [new Omml("m:degHide", [], { "m:val": "1" })]), new Omml("m:deg", degree || []), arg(this.groupOrAtom(style))]);
|
|
2227
|
+
}
|
|
2228
|
+
if (name == "text") return new Omml("m:box", [arg(this.groupOrAtom("plain", true))]);
|
|
2229
|
+
if (name == "mathrm" || name == "operatorname") return new Omml("m:box", [arg(this.groupOrAtom("plain"))]);
|
|
2230
|
+
if (name == "mathbf") return new Omml("m:box", [arg(this.groupOrAtom("bold"))]);
|
|
2231
|
+
if (name == "mathit") return new Omml("m:box", [arg(this.groupOrAtom("italic"))]);
|
|
2232
|
+
if (["mathbb", "mathcal"].includes(name)) return new Omml("m:box", [arg(this.groupOrAtom(name == "mathbb" ? "double-struck" : "script"))]);
|
|
2233
|
+
if (["hat", "widehat", "vec"].includes(name)) {
|
|
2234
|
+
const chr = name == "vec" ? "\u20D7" : "\u0302";
|
|
2235
|
+
return new Omml("m:acc", [new Omml("m:accPr", [new Omml("m:chr", [], { "m:val": chr })]), arg(this.groupOrAtom(style))]);
|
|
2236
|
+
}
|
|
2237
|
+
if (["overline", "bar", "underline"].includes(name)) {
|
|
2238
|
+
const pos = name == "underline" ? "bot" : "top";
|
|
2239
|
+
return new Omml("m:bar", [new Omml("m:barPr", [new Omml("m:pos", [], { "m:val": pos })]), arg(this.groupOrAtom(style))]);
|
|
2240
|
+
}
|
|
2241
|
+
if (name == "binom" || name == "dbinom") return delimiter([new Omml("m:f", [new Omml("m:fPr", [new Omml("m:type", [], { "m:val": "noBar" })]), new Omml("m:num", this.groupOrAtom(style)), new Omml("m:den", this.groupOrAtom(style))])], "(", ")");
|
|
2242
|
+
if (name == "lim") {
|
|
2243
|
+
let below = true;
|
|
2244
|
+
if (this.command("limits") || this.command("nolimits")) {
|
|
2245
|
+
below = this.command("limits");
|
|
2246
|
+
this.i += below ? 7 : 9;
|
|
2247
|
+
this.skipWhitespace();
|
|
2248
|
+
}
|
|
2249
|
+
let sub = [];
|
|
2250
|
+
if (this.peek() == "_") {
|
|
2251
|
+
this.take();
|
|
2252
|
+
sub = this.groupOrAtom(style);
|
|
2253
|
+
}
|
|
2254
|
+
if (!sub.length) return run("lim", "plain");
|
|
2255
|
+
return new Omml(below ? "m:limLow" : "m:sSub", [arg([run("lim", "plain")]), new Omml(below ? "m:lim" : "m:sub", sub)]);
|
|
2256
|
+
}
|
|
2257
|
+
if (["sum", "prod", "int", "iint", "iiint"].includes(name)) {
|
|
2258
|
+
const chr = name == "sum" ? "\u2211" : name == "prod" ? "\u220F" : name == "iint" ? "\u222C" : name == "iiint" ? "\u222D" : "\u222B";
|
|
2259
|
+
let sub = [], sup = [];
|
|
2260
|
+
let limitLocation;
|
|
2261
|
+
while (true) {
|
|
2262
|
+
this.skipWhitespace();
|
|
2263
|
+
if (this.command("limits") || this.command("nolimits")) {
|
|
2264
|
+
const limits = this.command("limits");
|
|
2265
|
+
this.i += limits ? 7 : 9;
|
|
2266
|
+
limitLocation = limits ? "undOvr" : "subSup";
|
|
2267
|
+
continue;
|
|
2268
|
+
}
|
|
2269
|
+
if (this.peek() != "_" && this.peek() != "^") break;
|
|
2270
|
+
const marker = this.take();
|
|
2271
|
+
const script = this.groupOrAtom(style);
|
|
2272
|
+
if (marker == "_" ? sub.length : sup.length) throw new Error("duplicate script");
|
|
2273
|
+
if (marker == "_") sub = script;
|
|
2274
|
+
else sup = script;
|
|
2275
|
+
this.skipWhitespace();
|
|
2276
|
+
}
|
|
2277
|
+
while (/\s/.test(this.peek())) this.take();
|
|
2278
|
+
const operand = this.done() || ["}", "]", ")", "&", "=", "<", ">"].includes(this.peek()) || this.command("right") || this.command("end") || this.source.startsWith("\\\\", this.i) ? [] : [this.decoratedAtom(style)];
|
|
2279
|
+
return new Omml("m:nary", [new Omml("m:naryPr", [
|
|
2280
|
+
new Omml("m:chr", [], { "m:val": chr }),
|
|
2281
|
+
...limitLocation ? [new Omml("m:limLoc", [], { "m:val": limitLocation })] : [],
|
|
2282
|
+
new Omml("m:subHide", [], { "m:val": sub.length ? "0" : "1" }),
|
|
2283
|
+
new Omml("m:supHide", [], { "m:val": sup.length ? "0" : "1" })
|
|
2284
|
+
]), new Omml("m:sub", sub), new Omml("m:sup", sup), arg(operand)]);
|
|
2285
|
+
}
|
|
2286
|
+
if (name == "left") {
|
|
2287
|
+
const begin = this.readDelimiter();
|
|
2288
|
+
const body = this.sequence(void 0, style, false, () => this.command("right"));
|
|
2289
|
+
if (!this.command("right")) throw new Error("missing \\right");
|
|
2290
|
+
this.i += 6;
|
|
2291
|
+
return delimiter(body, begin, this.readDelimiter());
|
|
2292
|
+
}
|
|
2293
|
+
if (name == "begin") return this.matrix(style);
|
|
2294
|
+
if (symbols[name]) return run(symbols[name], style);
|
|
2295
|
+
if (operators[name]) return run(operators[name], "plain");
|
|
2296
|
+
if (["quad", "qquad"].includes(name)) return run(name == "quad" ? "\u2003" : "\u2003\u2003", style);
|
|
2297
|
+
throw new Error(`unsupported command \\${name}`);
|
|
2298
|
+
}
|
|
2299
|
+
readDelimiter() {
|
|
2300
|
+
this.skipWhitespace();
|
|
2301
|
+
const ch = this.take();
|
|
2302
|
+
if (ch == ".") return "";
|
|
2303
|
+
if (ch && "()[]|{}".includes(ch)) return ch;
|
|
2304
|
+
if (ch == "\\") {
|
|
2305
|
+
let name = "";
|
|
2306
|
+
while (/[A-Za-z]/.test(this.peek())) name += this.take();
|
|
2307
|
+
if (!name) name = this.take();
|
|
2308
|
+
const delimiters = { "{": "{", "}": "}", "|": "\u2016", lbrace: "{", rbrace: "}", langle: "\u27E8", rangle: "\u27E9", lvert: "|", rvert: "|", vert: "|", lVert: "\u2016", rVert: "\u2016", Vert: "\u2016" };
|
|
2309
|
+
if (delimiters[name]) return delimiters[name];
|
|
2310
|
+
}
|
|
2311
|
+
throw new Error("delimiter expected");
|
|
2312
|
+
}
|
|
2313
|
+
matrix(style) {
|
|
2314
|
+
if (this.peek() != "{") throw new Error("environment name expected");
|
|
2315
|
+
this.take();
|
|
2316
|
+
let env = "";
|
|
2317
|
+
while (!this.done() && this.peek() != "}") env += this.take();
|
|
2318
|
+
this.take();
|
|
2319
|
+
if (!["matrix", "pmatrix", "bmatrix", "cases"].includes(env)) throw new Error(`unsupported environment ${env}`);
|
|
2320
|
+
const rows = [];
|
|
2321
|
+
let cells = [];
|
|
2322
|
+
while (true) {
|
|
2323
|
+
this.skipWhitespace();
|
|
2324
|
+
if (this.done()) throw new Error(`environment ${env} is not closed`);
|
|
2325
|
+
if (this.command("end")) {
|
|
2326
|
+
this.i += 4;
|
|
2327
|
+
this.skipWhitespace();
|
|
2328
|
+
const closing = `{${env}}`;
|
|
2329
|
+
if (!this.source.startsWith(closing, this.i)) throw new Error(`mismatched environment ${env}`);
|
|
2330
|
+
this.i += closing.length;
|
|
2331
|
+
if (cells.length) rows.push(new Omml("m:mr", cells));
|
|
2332
|
+
break;
|
|
2333
|
+
}
|
|
2334
|
+
cells.push(arg(this.sequence(void 0, style, false, () => this.peek() == "&" || this.source.startsWith("\\\\", this.i) || this.command("end"))));
|
|
2335
|
+
if (this.peek() == "&") {
|
|
2336
|
+
this.take();
|
|
2337
|
+
continue;
|
|
2338
|
+
}
|
|
2339
|
+
if (this.source.startsWith("\\\\", this.i)) {
|
|
2340
|
+
this.i += 2;
|
|
2341
|
+
rows.push(new Omml("m:mr", cells));
|
|
2342
|
+
cells = [];
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
const matrix = new Omml("m:m", rows);
|
|
2346
|
+
if (env == "pmatrix") return delimiter([matrix], "(", ")");
|
|
2347
|
+
if (env == "bmatrix") return delimiter([matrix], "[", "]");
|
|
2348
|
+
if (env == "cases") return delimiter([matrix], "{", "");
|
|
2349
|
+
return matrix;
|
|
853
2350
|
}
|
|
854
2351
|
};
|
|
855
2352
|
|
|
856
2353
|
// src/serializer.ts
|
|
2354
|
+
var import_prismjs = __toESM(require("prismjs"));
|
|
2355
|
+
var import_prism_clike = require("prismjs/components/prism-clike.js");
|
|
2356
|
+
var import_prism_markup = require("prismjs/components/prism-markup.js");
|
|
2357
|
+
var import_prism_css = require("prismjs/components/prism-css.js");
|
|
2358
|
+
var import_prism_javascript = require("prismjs/components/prism-javascript.js");
|
|
2359
|
+
var import_prism_typescript = require("prismjs/components/prism-typescript.js");
|
|
2360
|
+
var import_prism_jsx = require("prismjs/components/prism-jsx.js");
|
|
2361
|
+
var import_prism_tsx = require("prismjs/components/prism-tsx.js");
|
|
2362
|
+
var import_prism_json = require("prismjs/components/prism-json.js");
|
|
2363
|
+
var import_prism_bash = require("prismjs/components/prism-bash.js");
|
|
2364
|
+
var import_prism_powershell = require("prismjs/components/prism-powershell.js");
|
|
2365
|
+
var import_prism_python = require("prismjs/components/prism-python.js");
|
|
2366
|
+
var import_prism_java = require("prismjs/components/prism-java.js");
|
|
2367
|
+
var import_prism_c = require("prismjs/components/prism-c.js");
|
|
2368
|
+
var import_prism_cpp = require("prismjs/components/prism-cpp.js");
|
|
2369
|
+
var import_prism_csharp = require("prismjs/components/prism-csharp.js");
|
|
2370
|
+
var import_prism_go = require("prismjs/components/prism-go.js");
|
|
2371
|
+
var import_prism_rust = require("prismjs/components/prism-rust.js");
|
|
2372
|
+
var import_prism_php = require("prismjs/components/prism-php.js");
|
|
2373
|
+
var import_prism_ruby = require("prismjs/components/prism-ruby.js");
|
|
2374
|
+
var import_prism_swift = require("prismjs/components/prism-swift.js");
|
|
2375
|
+
var import_prism_kotlin = require("prismjs/components/prism-kotlin.js");
|
|
2376
|
+
var import_prism_sql = require("prismjs/components/prism-sql.js");
|
|
2377
|
+
var import_prism_yaml = require("prismjs/components/prism-yaml.js");
|
|
2378
|
+
var import_prism_markdown = require("prismjs/components/prism-markdown.js");
|
|
2379
|
+
var import_prism_docker = require("prismjs/components/prism-docker.js");
|
|
2380
|
+
var import_prism_nginx = require("prismjs/components/prism-nginx.js");
|
|
857
2381
|
var STYLE_list = "afc";
|
|
858
2382
|
var STYLE_table_title = "TableCaption";
|
|
859
2383
|
var STYLE_code_title = "ListingCaption";
|
|
860
2384
|
var STYLE_code = "ListingCode";
|
|
861
|
-
async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath) {
|
|
862
|
-
const
|
|
863
|
-
const getPath = (fname) => {
|
|
864
|
-
if (process.platform != "win32") fname = fname.replaceAll("\\", "/");
|
|
865
|
-
if (process.platform == "win32" && fname.startsWith("/")) fname = "." + fname;
|
|
866
|
-
const targetPath = import_path2.default.isAbsolute(fname) ? import_path2.default.resolve(fname) : import_path2.default.resolve(workdir, fname);
|
|
867
|
-
if (allowedRealPath !== false) {
|
|
868
|
-
const targetRealPath = realpathAllowMissing(targetPath);
|
|
869
|
-
const relative = import_path2.default.relative(allowedRealPath, targetRealPath);
|
|
870
|
-
const isOutside = relative === ".." || relative.startsWith(`..${import_path2.default.sep}`) || import_path2.default.isAbsolute(relative);
|
|
871
|
-
if (isOutside) throw new UserInputError(`Access denied: "${fname}" resolves outside the working directory.`);
|
|
872
|
-
}
|
|
873
|
-
return targetPath;
|
|
874
|
-
};
|
|
2385
|
+
async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath, logwarn = console.warn) {
|
|
2386
|
+
const { resolvePath: getPath } = getSafePathResolver(workdir, checkFilesIsInsidePath);
|
|
875
2387
|
const sections = [];
|
|
876
2388
|
const numbering = [];
|
|
877
2389
|
const docSections = (function splitSections() {
|
|
878
|
-
|
|
2390
|
+
let header = doc.header;
|
|
2391
|
+
let footer = doc.footer;
|
|
2392
|
+
const sections2 = [{ displayPageNum: true, orientation: "portrait", pageStart: 1, header, footer, nodes: [] }];
|
|
879
2393
|
for (let j = 0; j < doc.nodes.length; j++) {
|
|
880
2394
|
const node = doc.nodes[j];
|
|
881
|
-
if (node.type == "sectionBreak")
|
|
2395
|
+
if (node.type == "sectionBreak") {
|
|
2396
|
+
header = node.header ?? header;
|
|
2397
|
+
footer = node.footer ?? footer;
|
|
882
2398
|
sections2.push({
|
|
883
|
-
displayPageNum: node.pageStart == null ? sections2.at(-1)?.displayPageNum
|
|
2399
|
+
displayPageNum: node.pageStart == null ? sections2.at(-1)?.displayPageNum ?? true : node.pageStart >= 0,
|
|
884
2400
|
orientation: node.orientation || sections2.at(-1)?.orientation || "portrait",
|
|
885
2401
|
pageStart: node.pageStart,
|
|
2402
|
+
header,
|
|
2403
|
+
footer,
|
|
886
2404
|
nodes: []
|
|
887
2405
|
});
|
|
888
|
-
else
|
|
2406
|
+
} else
|
|
889
2407
|
sections2.at(-1)?.nodes.push(node);
|
|
890
2408
|
}
|
|
891
2409
|
return sections2.filter((s) => s.nodes.length > 0);
|
|
892
2410
|
})();
|
|
893
2411
|
for (const section of docSections) {
|
|
894
|
-
let
|
|
2412
|
+
let renderHeaderFooter2 = function(value, isFooter) {
|
|
2413
|
+
if (value.type == "none") return void 0;
|
|
2414
|
+
if (value.type == "auto") {
|
|
2415
|
+
if (!isFooter) return void 0;
|
|
2416
|
+
return new import_docx2.Footer({
|
|
2417
|
+
children: [new import_docx2.Paragraph({
|
|
2418
|
+
alignment: import_docx2.AlignmentType.CENTER,
|
|
2419
|
+
children: [new import_docx2.TextRun({ children: section.displayPageNum ? [import_docx2.PageNumber.CURRENT] : [] })],
|
|
2420
|
+
indent: { firstLine: 0 },
|
|
2421
|
+
spacing: { after: 0, line: 240 }
|
|
2422
|
+
})]
|
|
2423
|
+
});
|
|
2424
|
+
}
|
|
2425
|
+
const contentValue = value;
|
|
2426
|
+
const content = contentValue.nodes.flatMap((node) => {
|
|
2427
|
+
if (node.type == "text") return [new import_docx2.Paragraph({
|
|
2428
|
+
children: renderText(node.text, node.sourceLine),
|
|
2429
|
+
alignment: contentValue.align,
|
|
2430
|
+
indent: { firstLine: 0 },
|
|
2431
|
+
spacing: isFooter ? { after: 0, line: 240 } : { line: 240 }
|
|
2432
|
+
})];
|
|
2433
|
+
return [renderHeaderFooterTable2(node)];
|
|
2434
|
+
});
|
|
2435
|
+
return isFooter ? new import_docx2.Footer({ children: content }) : new import_docx2.Header({ children: content });
|
|
2436
|
+
}, renderHeaderFooterTable2 = function(node) {
|
|
2437
|
+
return new import_docx2.Table({
|
|
2438
|
+
width: { type: "dxa", size: (0, import_docx2.convertMillimetersToTwip)(section.orientation == "landscape" ? 255.8 : 168.8) },
|
|
2439
|
+
borders: {
|
|
2440
|
+
top: { style: import_docx2.BorderStyle.SINGLE, size: 4, color: "808080" },
|
|
2441
|
+
bottom: { style: import_docx2.BorderStyle.SINGLE, size: 4, color: "808080" },
|
|
2442
|
+
left: { style: import_docx2.BorderStyle.SINGLE, size: 4, color: "808080" },
|
|
2443
|
+
right: { style: import_docx2.BorderStyle.SINGLE, size: 4, color: "808080" },
|
|
2444
|
+
insideHorizontal: { style: import_docx2.BorderStyle.SINGLE, size: 4, color: "808080" },
|
|
2445
|
+
insideVertical: { style: import_docx2.BorderStyle.SINGLE, size: 4, color: "808080" }
|
|
2446
|
+
},
|
|
2447
|
+
rows: node.rows.map((row) => new import_docx2.TableRow({
|
|
2448
|
+
children: row.map((cell, colI) => new import_docx2.TableCell({
|
|
2449
|
+
children: cell.type == "text" ? [new import_docx2.Paragraph({
|
|
2450
|
+
children: renderText(cell.text, node.sourceLine),
|
|
2451
|
+
alignment: node.align[colI] == "c" ? "center" : node.align[colI] == "r" ? "right" : "left",
|
|
2452
|
+
indent: { firstLine: 0 },
|
|
2453
|
+
spacing: { after: 0, line: 240 * 1.25 }
|
|
2454
|
+
})] : []
|
|
2455
|
+
}))
|
|
2456
|
+
}))
|
|
2457
|
+
});
|
|
2458
|
+
}, renderNode2 = function(node, prevChild, prevNode) {
|
|
895
2459
|
switch (node.type) {
|
|
896
2460
|
case "text":
|
|
897
|
-
return new
|
|
898
|
-
children: renderText(node.text),
|
|
2461
|
+
return new import_docx2.Paragraph({
|
|
2462
|
+
children: renderText(node.text, node.sourceLine),
|
|
899
2463
|
indent: node.noIndent ? { firstLine: 0 } : {},
|
|
900
2464
|
...node.center ? { alignment: "center", indent: { firstLine: 0 } } : {},
|
|
901
2465
|
spacing: {
|
|
902
2466
|
...node.noMargin ? { after: 0 } : {},
|
|
903
|
-
...prevChild instanceof
|
|
2467
|
+
...prevChild instanceof import_docx2.Table ? { before: (doc.table.spacing.after ?? doc.text.spacing.after) * 20 } : {}
|
|
904
2468
|
}
|
|
905
2469
|
});
|
|
906
2470
|
case "title":
|
|
@@ -908,20 +2472,20 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
908
2472
|
const level = node.level == 0 ? 1 : node.level;
|
|
909
2473
|
const styles = doc.headings[`h${level}`];
|
|
910
2474
|
if (styles.uppercase) node.text.forEach((r) => r.text = r.text.toUpperCase());
|
|
911
|
-
return new
|
|
912
|
-
children: renderText(node.text, styles.size),
|
|
2475
|
+
return new import_docx2.Paragraph({
|
|
2476
|
+
children: renderText(node.text, node.sourceLine, styles.size),
|
|
913
2477
|
style: `${node.level}`,
|
|
914
2478
|
...node.center ? { alignment: "center", indent: { firstLine: 0 } } : {},
|
|
915
|
-
...!node.center && node.level != 0 && styles.indent_full ? { indent: { firstLine: 0, left: (0,
|
|
2479
|
+
...!node.center && node.level != 0 && styles.indent_full ? { indent: { firstLine: 0, left: (0, import_docx2.convertMillimetersToTwip)(12.5) } } : {},
|
|
916
2480
|
spacing: {
|
|
917
2481
|
before: styles.spacing.before * 20,
|
|
918
2482
|
after: styles.spacing.after * 20
|
|
919
2483
|
}
|
|
920
2484
|
});
|
|
921
2485
|
case "pageBreak":
|
|
922
|
-
if (!(prevChild instanceof
|
|
923
|
-
return new
|
|
924
|
-
prevChild.addChildElement(new
|
|
2486
|
+
if (!(prevChild instanceof import_docx2.Paragraph))
|
|
2487
|
+
return new import_docx2.Paragraph({ children: [new import_docx2.PageBreak()] });
|
|
2488
|
+
prevChild.addChildElement(new import_docx2.PageBreak());
|
|
925
2489
|
return [];
|
|
926
2490
|
case "list":
|
|
927
2491
|
let renderList2 = function(node2, level2 = 0) {
|
|
@@ -932,8 +2496,8 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
932
2496
|
for (const item of node2.items) {
|
|
933
2497
|
if (item.type == "list") renderList2({ ...item, alternativeStyle: !!node2.alternativeStyle }, level2 + 1);
|
|
934
2498
|
else {
|
|
935
|
-
items.push(new
|
|
936
|
-
children: renderText(item.text),
|
|
2499
|
+
items.push(new import_docx2.Paragraph({
|
|
2500
|
+
children: renderText(item.text, item.sourceLine ?? node2.sourceLine),
|
|
937
2501
|
style: STYLE_list,
|
|
938
2502
|
numbering: { reference: id, level: level2 },
|
|
939
2503
|
spacing: {
|
|
@@ -953,39 +2517,39 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
953
2517
|
};
|
|
954
2518
|
numbering.push(list);
|
|
955
2519
|
const items = [];
|
|
956
|
-
let addMargin = prevChild instanceof
|
|
2520
|
+
let addMargin = prevChild instanceof import_docx2.Table;
|
|
957
2521
|
renderList2(node);
|
|
958
2522
|
return items;
|
|
959
2523
|
case "table":
|
|
960
|
-
if (node.rows[0] && doc.table.heading.style == "italic") node.rows[0].forEach((n) => n.type == "text" ? n.text.forEach((r) => r.italic = true) : 0);
|
|
961
|
-
if (node.rows[0] && doc.table.heading.style == "bold") node.rows[0].forEach((n) => n.type == "text" ? n.text.forEach((r) => r.bold = true) : 0);
|
|
2524
|
+
if (node.header !== false && node.rows[0] && doc.table.heading.style == "italic") node.rows[0].forEach((n) => n.type == "text" ? n.text.forEach((r) => r.italic = true) : 0);
|
|
2525
|
+
if (node.header !== false && node.rows[0] && doc.table.heading.style == "bold") node.rows[0].forEach((n) => n.type == "text" ? n.text.forEach((r) => r.bold = true) : 0);
|
|
962
2526
|
const spacingInner = Math.max(doc.table.spacing.after ?? doc.text.spacing.after, doc.table.spacing.before ?? 0);
|
|
963
2527
|
return [
|
|
964
2528
|
...node.title ? [
|
|
965
|
-
new
|
|
966
|
-
children: renderText(node.title, doc.table.title.size),
|
|
2529
|
+
new import_docx2.Paragraph({
|
|
2530
|
+
children: renderText(node.title, node.sourceLine, doc.table.title.size),
|
|
967
2531
|
style: STYLE_table_title,
|
|
968
|
-
...prevChild instanceof
|
|
2532
|
+
...prevChild instanceof import_docx2.Table ? { spacing: { before: spacingInner * 20 } } : doc.table.spacing.before ? { spacing: { before: doc.table.spacing.before * 20 } } : {}
|
|
969
2533
|
})
|
|
970
|
-
] : prevChild instanceof
|
|
971
|
-
new
|
|
2534
|
+
] : prevChild instanceof import_docx2.Table ? [
|
|
2535
|
+
new import_docx2.Paragraph({ spacing: { line: 20, before: spacingInner * 20, after: 0 } })
|
|
972
2536
|
] : doc.table.spacing.before ? [
|
|
973
|
-
new
|
|
2537
|
+
new import_docx2.Paragraph({ spacing: { line: 20, before: doc.table.spacing.before * 20, after: 0 } })
|
|
974
2538
|
] : [],
|
|
975
|
-
new
|
|
2539
|
+
new import_docx2.Table({
|
|
976
2540
|
// width: { type: "pct", size: 100 },
|
|
977
2541
|
width: {
|
|
978
2542
|
type: "dxa",
|
|
979
|
-
size: (0,
|
|
2543
|
+
size: (0, import_docx2.convertMillimetersToTwip)(section.orientation === "landscape" ? 255.8 : 168.8)
|
|
980
2544
|
},
|
|
981
|
-
rows: node.rows.map((row, rowI) => new
|
|
982
|
-
tableHeader: rowI == 0,
|
|
2545
|
+
rows: node.rows.map((row, rowI) => new import_docx2.TableRow({
|
|
2546
|
+
tableHeader: node.header !== false && rowI == 0,
|
|
983
2547
|
cantSplit: true,
|
|
984
|
-
children: row.map((item, colI) => new
|
|
2548
|
+
children: row.map((item, colI) => new import_docx2.TableCell({
|
|
985
2549
|
children: item.type != "text" ? renderNodeL2(item) : [
|
|
986
|
-
new
|
|
987
|
-
children: renderText(item.text, node.normalFontSize ? void 0 : doc.table.text.size),
|
|
988
|
-
alignment: rowI == 0 ? doc.table.heading.align : node.align[colI] == "c" ? "center" : node.align[colI] == "r" ? "right" : "left",
|
|
2550
|
+
new import_docx2.Paragraph({
|
|
2551
|
+
children: renderText(item.text, node.sourceLine, node.normalFontSize ? void 0 : doc.table.text.size),
|
|
2552
|
+
alignment: node.header !== false && rowI == 0 ? doc.table.heading.align : node.align[colI] == "c" ? "center" : node.align[colI] == "r" ? "right" : "left",
|
|
989
2553
|
indent: { firstLine: 0 },
|
|
990
2554
|
spacing: { after: 0, line: 240 * doc.table.text.line_spacing }
|
|
991
2555
|
})
|
|
@@ -996,20 +2560,20 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
996
2560
|
];
|
|
997
2561
|
case "tableOfContents":
|
|
998
2562
|
return [
|
|
999
|
-
new
|
|
2563
|
+
new import_docx2.TableOfContents("\u041E\u0433\u043B\u0430\u0432\u043B\u0435\u043D\u0438\u0435", {
|
|
1000
2564
|
hyperlink: true,
|
|
1001
2565
|
headingStyleRange: "1-3"
|
|
1002
2566
|
}),
|
|
1003
|
-
new
|
|
2567
|
+
new import_docx2.Paragraph({ children: [new import_docx2.PageBreak()] })
|
|
1004
2568
|
];
|
|
1005
2569
|
case "image":
|
|
1006
|
-
const type = node.src.split(".").at(-1) || "";
|
|
2570
|
+
const type = (node.src.split(".").at(-1) || "").toLowerCase();
|
|
1007
2571
|
if (!["jpg", "png", "gif", "bmp", "svg"].includes(type))
|
|
1008
2572
|
throw new UserInputError(`Unsupported image format: "${type}", file: ${node.src}`);
|
|
1009
2573
|
const img_path = getPath(node.src);
|
|
1010
|
-
if (!
|
|
2574
|
+
if (!fs4.existsSync(img_path))
|
|
1011
2575
|
throw new UserInputError(`File not exist: ${node.src}`);
|
|
1012
|
-
const data =
|
|
2576
|
+
const data = fs4.readFileSync(img_path);
|
|
1013
2577
|
const dimensions = (0, import_image_size.imageSize)(data);
|
|
1014
2578
|
const [MaxW, MaxH] = section.orientation == "landscape" ? [950, 600] : [600, 900];
|
|
1015
2579
|
let [width, height] = [dimensions.width, dimensions.height];
|
|
@@ -1019,19 +2583,19 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1019
2583
|
if (height > MaxH) [width, height] = [width / height * MaxH, MaxH];
|
|
1020
2584
|
if (width > MaxW) [width, height] = [MaxW, height / width * MaxW];
|
|
1021
2585
|
return [
|
|
1022
|
-
new
|
|
2586
|
+
new import_docx2.Paragraph({
|
|
1023
2587
|
alignment: "center",
|
|
1024
2588
|
indent: { firstLine: 0 },
|
|
1025
2589
|
spacing: {
|
|
1026
2590
|
line: 240,
|
|
1027
|
-
lineRule:
|
|
2591
|
+
lineRule: import_docx2.LineRuleType.AT_LEAST,
|
|
1028
2592
|
// For LibreOffice to not shrink image
|
|
1029
2593
|
...!node.text && doc.img.spacing.after !== void 0 ? { after: doc.img.spacing.after * 20 } : {},
|
|
1030
2594
|
...doc.img.spacing.before !== void 0 ? { before: doc.img.spacing.before * 20 } : {}
|
|
1031
2595
|
},
|
|
1032
2596
|
keepNext: !!node.text,
|
|
1033
2597
|
children: [
|
|
1034
|
-
new
|
|
2598
|
+
new import_docx2.ImageRun({
|
|
1035
2599
|
type,
|
|
1036
2600
|
data,
|
|
1037
2601
|
transformation: { width, height },
|
|
@@ -1040,8 +2604,8 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1040
2604
|
]
|
|
1041
2605
|
}),
|
|
1042
2606
|
...node.text ? [
|
|
1043
|
-
new
|
|
1044
|
-
children: renderText(node.text, doc.img.text.size),
|
|
2607
|
+
new import_docx2.Paragraph({
|
|
2608
|
+
children: renderText(node.text, node.sourceLine, doc.img.text.size),
|
|
1045
2609
|
alignment: "center",
|
|
1046
2610
|
indent: { firstLine: 0 },
|
|
1047
2611
|
spacing: {
|
|
@@ -1055,31 +2619,97 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1055
2619
|
const code = doc.code.highlight && renderCodeHighlighting(node.code, node.lang);
|
|
1056
2620
|
return [
|
|
1057
2621
|
...node.title ? [
|
|
1058
|
-
new
|
|
1059
|
-
children: renderText(node.title, doc.code.title.size),
|
|
2622
|
+
new import_docx2.Paragraph({
|
|
2623
|
+
children: renderText(node.title, node.sourceLine, doc.code.title.size),
|
|
1060
2624
|
style: STYLE_code_title
|
|
1061
2625
|
})
|
|
1062
2626
|
] : prevNode?.type == "code" ? [
|
|
1063
|
-
new
|
|
2627
|
+
new import_docx2.Paragraph({ spacing: { line: 20 } })
|
|
1064
2628
|
] : [],
|
|
1065
|
-
...code ? code : node.code.split("\n").map((p, i) => new
|
|
1066
|
-
children: [new
|
|
2629
|
+
...code ? code : node.code.split("\n").map((p, i) => new import_docx2.Paragraph({
|
|
2630
|
+
children: [new import_docx2.TextRun(p)],
|
|
1067
2631
|
style: STYLE_code,
|
|
1068
2632
|
...i == 0 && !node.title && doc.code.spacing.before && prevNode?.type != "code" ? { spacing: { before: doc.code.spacing.before * 20 } } : {}
|
|
1069
2633
|
}))
|
|
1070
2634
|
];
|
|
2635
|
+
case "math":
|
|
2636
|
+
const formulaWidth = (0, import_docx2.convertMillimetersToTwip)(section.orientation == "landscape" ? 255.8 : 168.8);
|
|
2637
|
+
const number = node.title?.map((rune) => rune.text).join("") || "";
|
|
2638
|
+
const sideWidth = Math.max(
|
|
2639
|
+
(0, import_docx2.convertMillimetersToTwip)(5),
|
|
2640
|
+
Math.ceil(estimateNumberWidthPt(number, doc.text.size) * 20 + (0, import_docx2.convertMillimetersToTwip)(3))
|
|
2641
|
+
);
|
|
2642
|
+
return [
|
|
2643
|
+
...prevNode?.type != "math" ? [
|
|
2644
|
+
new import_docx2.Paragraph({ indent: { firstLine: 0 }, spacing: { line: 20, after: doc.formula.spacing.before * 20 } })
|
|
2645
|
+
] : [],
|
|
2646
|
+
new import_docx2.Table({
|
|
2647
|
+
width: { type: "dxa", size: formulaWidth },
|
|
2648
|
+
borders: { top: { style: import_docx2.BorderStyle.NONE }, bottom: { style: import_docx2.BorderStyle.NONE }, left: { style: import_docx2.BorderStyle.NONE }, right: { style: import_docx2.BorderStyle.NONE }, insideHorizontal: { style: import_docx2.BorderStyle.NONE }, insideVertical: { style: import_docx2.BorderStyle.NONE } },
|
|
2649
|
+
rows: [new import_docx2.TableRow({
|
|
2650
|
+
children: [
|
|
2651
|
+
new import_docx2.TableCell({ width: { type: "dxa", size: sideWidth }, children: [new import_docx2.Paragraph({ indent: { firstLine: 0 }, spacing: { after: 0 } })] }),
|
|
2652
|
+
new import_docx2.TableCell({
|
|
2653
|
+
children: [
|
|
2654
|
+
new import_docx2.Paragraph({ alignment: import_docx2.AlignmentType.CENTER, indent: { firstLine: 0 }, spacing: { after: 0 }, children: [renderMath(node.latex, true, node.sourceLine)] })
|
|
2655
|
+
]
|
|
2656
|
+
}),
|
|
2657
|
+
new import_docx2.TableCell({ width: { type: "dxa", size: sideWidth }, children: [new import_docx2.Paragraph({
|
|
2658
|
+
indent: { firstLine: 0 },
|
|
2659
|
+
spacing: { line: 240, after: 0 },
|
|
2660
|
+
alignment: import_docx2.AlignmentType.RIGHT,
|
|
2661
|
+
children: renderText(node.title || [], node.sourceLine)
|
|
2662
|
+
})], verticalAlign: import_docx2.VerticalAlignTable.CENTER })
|
|
2663
|
+
]
|
|
2664
|
+
})]
|
|
2665
|
+
}),
|
|
2666
|
+
new import_docx2.Paragraph({ indent: { firstLine: 0 }, spacing: { line: 20, after: doc.formula.spacing.after * 20 } })
|
|
2667
|
+
];
|
|
1071
2668
|
case "externalDoc":
|
|
1072
2669
|
const doc_path = getPath(node.path);
|
|
1073
|
-
if (!
|
|
2670
|
+
if (!fs4.existsSync(doc_path))
|
|
1074
2671
|
throw new UserInputError(`File not exist: ${node.path}`);
|
|
1075
|
-
const ext =
|
|
2672
|
+
const ext = import_path3.default.extname(doc_path);
|
|
1076
2673
|
if (ext != ".docx" && ext != ".pdf")
|
|
1077
2674
|
throw new UserInputError(`File not .docx or .pdf: ${node.path}`);
|
|
1078
|
-
return new
|
|
2675
|
+
return new import_docx2.Paragraph({
|
|
1079
2676
|
text: `!!(${doc_path})${JSON.stringify(node.dict)}`,
|
|
1080
2677
|
indent: { firstLine: 0 },
|
|
1081
2678
|
alignment: "left"
|
|
1082
2679
|
});
|
|
2680
|
+
case "admonition":
|
|
2681
|
+
const style = `xAdmonition${node.admonitionType == "note" ? "" : node.admonitionType}`;
|
|
2682
|
+
const admonition = doc.admonition[node.admonitionType];
|
|
2683
|
+
const color = "#" + admonition.color;
|
|
2684
|
+
const icon = admonition.icon ? [new import_docx2.ImageRun({
|
|
2685
|
+
type: "svg",
|
|
2686
|
+
data: Buffer.from(admonitionIcons[node.admonitionType](admonition.title_color ? color : "#000000")),
|
|
2687
|
+
transformation: { width: admonition.icon_size, height: admonition.icon_size },
|
|
2688
|
+
fallback: {
|
|
2689
|
+
data: Buffer.from((admonition.title_color ? admonitionIconsFallback : admonitionIconsFallbackBlack)[node.admonitionType], "base64"),
|
|
2690
|
+
type: "png"
|
|
2691
|
+
}
|
|
2692
|
+
}), new import_docx2.TextRun(" ")] : [];
|
|
2693
|
+
const prevIsSameType = prevNode?.type == "admonition" && prevNode.admonitionType == node.admonitionType;
|
|
2694
|
+
const showTitle = node.title.map((r) => r.text).join("") != "";
|
|
2695
|
+
return [
|
|
2696
|
+
...prevIsSameType ? [
|
|
2697
|
+
new import_docx2.Paragraph({ spacing: { line: 20, before: 0, after: 0 } })
|
|
2698
|
+
] : [],
|
|
2699
|
+
...showTitle ? [
|
|
2700
|
+
new import_docx2.Paragraph({
|
|
2701
|
+
style,
|
|
2702
|
+
children: [...icon, ...renderText(admonition.title_color ? node.title.map((r) => ({ ...r, color })) : node.title, node.sourceLine)],
|
|
2703
|
+
keepNext: true,
|
|
2704
|
+
...prevIsSameType ? { spacing: { before: 0 } } : {}
|
|
2705
|
+
})
|
|
2706
|
+
] : [],
|
|
2707
|
+
new import_docx2.Paragraph({
|
|
2708
|
+
style,
|
|
2709
|
+
children: renderText(node.text, node.sourceLine),
|
|
2710
|
+
...prevIsSameType && !showTitle ? { spacing: { before: 0 } } : {}
|
|
2711
|
+
})
|
|
2712
|
+
];
|
|
1083
2713
|
case "sectionBreak":
|
|
1084
2714
|
return [];
|
|
1085
2715
|
default:
|
|
@@ -1087,12 +2717,20 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1087
2717
|
throw new Error("switch default");
|
|
1088
2718
|
}
|
|
1089
2719
|
}, renderNodeL2 = function(node, prevChild, prevNode) {
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
2720
|
+
try {
|
|
2721
|
+
const r = renderNode2(node, prevChild, prevNode);
|
|
2722
|
+
if (r instanceof Array) return r;
|
|
2723
|
+
return [r];
|
|
2724
|
+
} catch (error) {
|
|
2725
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2726
|
+
if (/^Line -?\d+:/.test(message)) throw error;
|
|
2727
|
+
throw new Error(`${node.sourceLine ? `Line ${node.sourceLine}: ` : ""}${message}`, { cause: error });
|
|
2728
|
+
}
|
|
1093
2729
|
};
|
|
1094
|
-
var renderNode = renderNode2, renderNodeL = renderNodeL2;
|
|
2730
|
+
var renderHeaderFooter = renderHeaderFooter2, renderHeaderFooterTable = renderHeaderFooterTable2, renderNode = renderNode2, renderNodeL = renderNodeL2;
|
|
1095
2731
|
const children = [];
|
|
2732
|
+
const header = renderHeaderFooter2(section.header, false);
|
|
2733
|
+
const footer = renderHeaderFooter2(section.footer, true);
|
|
1096
2734
|
sections.push({
|
|
1097
2735
|
children,
|
|
1098
2736
|
properties: {
|
|
@@ -1107,38 +2745,26 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1107
2745
|
// height: convertMillimetersToTwip(297),
|
|
1108
2746
|
// },
|
|
1109
2747
|
size: {
|
|
1110
|
-
orientation: section.orientation == "landscape" ?
|
|
2748
|
+
orientation: section.orientation == "landscape" ? import_docx2.PageOrientation.LANDSCAPE : import_docx2.PageOrientation.PORTRAIT
|
|
1111
2749
|
},
|
|
1112
2750
|
pageNumbers: !section.pageStart ? {} : {
|
|
1113
2751
|
start: section.pageStart
|
|
1114
2752
|
},
|
|
1115
2753
|
margin: {
|
|
1116
|
-
top: (0,
|
|
1117
|
-
left: (0,
|
|
1118
|
-
right: (0,
|
|
1119
|
-
bottom: (0,
|
|
2754
|
+
top: (0, import_docx2.convertMillimetersToTwip)(20),
|
|
2755
|
+
left: (0, import_docx2.convertMillimetersToTwip)(30),
|
|
2756
|
+
right: (0, import_docx2.convertMillimetersToTwip)(15),
|
|
2757
|
+
bottom: (0, import_docx2.convertMillimetersToTwip)(20)
|
|
1120
2758
|
}
|
|
1121
2759
|
}
|
|
1122
2760
|
},
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
children: [
|
|
1126
|
-
new import_docx.Paragraph({
|
|
1127
|
-
alignment: import_docx.AlignmentType.CENTER,
|
|
1128
|
-
children: [new import_docx.TextRun({
|
|
1129
|
-
children: section.displayPageNum ? [import_docx.PageNumber.CURRENT] : []
|
|
1130
|
-
})],
|
|
1131
|
-
indent: { firstLine: 0 },
|
|
1132
|
-
spacing: { line: 240 }
|
|
1133
|
-
})
|
|
1134
|
-
]
|
|
1135
|
-
})
|
|
1136
|
-
}
|
|
2761
|
+
...header ? { headers: { default: header } } : {},
|
|
2762
|
+
...footer ? { footers: { default: footer } } : {}
|
|
1137
2763
|
});
|
|
1138
2764
|
for (let i = 0; i < section.nodes.length; i++)
|
|
1139
2765
|
children.push(...renderNodeL2(section.nodes[i], children.at(-1), section.nodes[i - 1]));
|
|
1140
2766
|
}
|
|
1141
|
-
const docx = new
|
|
2767
|
+
const docx = new import_docx2.Document({
|
|
1142
2768
|
sections,
|
|
1143
2769
|
externalStyles: genXml_style(assets, doc),
|
|
1144
2770
|
// features: { updateFields: true },
|
|
@@ -1156,7 +2782,7 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1156
2782
|
// creator: doc.author,
|
|
1157
2783
|
// lastModifiedBy: doc.author,
|
|
1158
2784
|
});
|
|
1159
|
-
const buffer = await
|
|
2785
|
+
const buffer = await import_docx2.Packer.toBuffer(docx, void 0, [
|
|
1160
2786
|
{ path: "docProps/app.xml", data: genXml_app({ totalTime: doc.etime }) },
|
|
1161
2787
|
{
|
|
1162
2788
|
path: "docProps/core.xml",
|
|
@@ -1168,21 +2794,25 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1168
2794
|
})
|
|
1169
2795
|
}
|
|
1170
2796
|
]);
|
|
1171
|
-
|
|
1172
|
-
function renderText(text, size) {
|
|
2797
|
+
fs4.writeFileSync(fout, buffer);
|
|
2798
|
+
function renderText(text, sourceLine, size) {
|
|
1173
2799
|
function renderRune(rune, link = false) {
|
|
1174
|
-
if (rune.anchor) return new
|
|
2800
|
+
if (rune.anchor) return new import_docx2.Bookmark({
|
|
1175
2801
|
id: rune.anchor,
|
|
1176
2802
|
children: [renderRune({ ...rune, anchor: void 0 })]
|
|
1177
2803
|
});
|
|
1178
2804
|
if (rune.link) {
|
|
1179
2805
|
const children2 = [renderRune({ ...rune, link: void 0 }, true)];
|
|
1180
|
-
return rune.link.startsWith("#") ? new
|
|
2806
|
+
return rune.link.startsWith("#") ? new import_docx2.InternalHyperlink({ children: children2, anchor: rune.link.slice(1) }) : new import_docx2.ExternalHyperlink({ children: children2, link: rune.link });
|
|
1181
2807
|
}
|
|
2808
|
+
if (rune.type == "math") return renderMath(rune.text, false, sourceLine);
|
|
1182
2809
|
let children = null;
|
|
1183
|
-
if (rune.type == "val" && rune.text == "
|
|
2810
|
+
if (rune.type == "val" && rune.text == "page") {
|
|
1184
2811
|
rune.type = "text";
|
|
1185
|
-
children =
|
|
2812
|
+
children = [import_docx2.PageNumber.CURRENT];
|
|
2813
|
+
} else if (rune.type == "val" && rune.text == "pages") {
|
|
2814
|
+
rune.type = "text";
|
|
2815
|
+
children = doc.totalPagesOffset == 0 ? [import_docx2.PageNumber.TOTAL_PAGES] : [
|
|
1186
2816
|
// Complex Formula Field: { = { NUMPAGES } + 1 }
|
|
1187
2817
|
// Outer field start: "="
|
|
1188
2818
|
new RawXml("w:fldChar", { "w:fldCharType": "begin" }),
|
|
@@ -1203,7 +2833,7 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1203
2833
|
new RawXml("w:fldChar", { "w:fldCharType": "end" })
|
|
1204
2834
|
];
|
|
1205
2835
|
}
|
|
1206
|
-
return new
|
|
2836
|
+
return new import_docx2.TextRun({
|
|
1207
2837
|
...children ? { children } : { text: rune.text },
|
|
1208
2838
|
language: { value: rune.lang == "en" ? "en-US" : "ru-RU" },
|
|
1209
2839
|
...rune.linebreak ? { break: 1 } : {},
|
|
@@ -1264,12 +2894,41 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1264
2894
|
}
|
|
1265
2895
|
}
|
|
1266
2896
|
}
|
|
2897
|
+
function renderMath(latex, para, sourceLine) {
|
|
2898
|
+
try {
|
|
2899
|
+
return latexToOmml(latex, para);
|
|
2900
|
+
} catch (error) {
|
|
2901
|
+
logwarn(`${sourceLine ? `Line ${doc.mapSourceLine(sourceLine + 1)}: ` : ""}Formula was not converted: ${error instanceof Error ? error.message : String(error)}`);
|
|
2902
|
+
return new import_docx2.TextRun(latex);
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
1267
2905
|
}
|
|
2906
|
+
var admonitionIcons = {
|
|
2907
|
+
note: (c) => `<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><style>.a{fill:${c}}</style><path class="a" d="m13 12q0-0.4-0.3-0.7-0.3-0.3-0.7-0.3-0.4 0-0.7 0.3-0.3 0.3-0.3 0.7v4q0 0.4 0.3 0.7 0.3 0.3 0.7 0.3 0.4 0 0.7-0.3 0.3-0.3 0.3-0.7zm-1-2.5c0.3 0 0.6-0.1 0.9-0.4 0.2-0.2 0.3-0.5 0.3-0.8q0-0.6-0.3-0.9c-0.3-0.3-0.6-0.4-0.9-0.4-0.3 0-0.6 0.1-0.9 0.4q-0.3 0.3-0.3 0.9c0 0.3 0.1 0.6 0.3 0.8 0.3 0.3 0.6 0.4 0.9 0.4z"/><path fill-rule="evenodd" class="a" d="m22 12c0 5.5-4.5 10-10 10-5.5 0-10-4.5-10-10 0-5.5 4.5-10 10-10 5.5 0 10 4.5 10 10zm-15.7 5.7c1.5 1.5 3.6 2.3 5.7 2.3 2.1 0 4.2-0.8 5.7-2.3 1.5-1.5 2.3-3.6 2.3-5.7 0-2.1-0.8-4.2-2.3-5.7-1.5-1.5-3.6-2.3-5.7-2.3-2.1 0-4.2 0.8-5.7 2.3-1.5 1.5-2.3 3.6-2.3 5.7 0 2.1 0.8 4.2 2.3 5.7z"/></svg>`,
|
|
2908
|
+
info: (c) => `<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><style>.a{fill:${c}}</style><path class="a" d="m13 12q0-0.4-0.3-0.7-0.3-0.3-0.7-0.3-0.4 0-0.7 0.3-0.3 0.3-0.3 0.7v4q0 0.4 0.3 0.7 0.3 0.3 0.7 0.3 0.4 0 0.7-0.3 0.3-0.3 0.3-0.7zm-1-2.5c0.3 0 0.6-0.1 0.9-0.4 0.2-0.2 0.3-0.5 0.3-0.8q0-0.6-0.3-0.9c-0.3-0.3-0.6-0.4-0.9-0.4-0.3 0-0.6 0.1-0.9 0.4-0.2 0.2-0.3 0.5-0.3 0.9 0 0.3 0.1 0.6 0.3 0.8 0.3 0.3 0.6 0.4 0.9 0.4z"/><path fill-rule="evenodd" class="a" d="m22 12c0 5.5-4.5 10-10 10-5.5 0-10-4.5-10-10 0-5.5 4.5-10 10-10 5.5 0 10 4.5 10 10zm-15.7 5.7c1.5 1.5 3.6 2.3 5.7 2.3 2.1 0 4.2-0.8 5.7-2.3 1.5-1.5 2.3-3.6 2.3-5.7 0-2.1-0.8-4.2-2.3-5.7-1.5-1.5-3.6-2.3-5.7-2.3-2.1 0-4.2 0.8-5.7 2.3-1.5 1.5-2.3 3.6-2.3 5.7 0 2.1 0.8 4.2 2.3 5.7z"/></svg>`,
|
|
2909
|
+
tip: (c) => `<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><style>.a{fill:${c}}</style><path class="a" d="m8.1 20c0.2 0.9 0.7 1.6 1.5 2.2 0.7 0.5 1.5 0.8 2.4 0.8 0.9 0 1.7-0.3 2.4-0.8 0.8-0.6 1.3-1.3 1.5-2.2q0.2 0 0.4-0.1 0.1-0.1 0.3-0.2 0.1-0.1 0.2-0.3 0.1-0.2 0.1-0.4v-2.5q0.8-0.6 1.5-1.3 0.7-0.8 1.1-1.7 0.5-0.9 0.8-1.9 0.2-1 0.2-2.1c0-4.7-3.8-8.5-8.5-8.5-4.7 0-8.5 3.8-8.5 8.5q0 1.1 0.2 2.1 0.3 1 0.8 1.9 0.4 0.9 1.1 1.7 0.7 0.7 1.5 1.3v2.5q0 0.2 0.1 0.4 0.1 0.2 0.2 0.3 0.2 0.1 0.3 0.2 0.2 0.1 0.4 0.1zm6.8-3v1h-5.8v-1zm-1.1 3q-0.3 0.5-0.7 0.7-0.5 0.3-1 0.3-0.6 0-1-0.3-0.5-0.2-0.8-0.7zm-1.7-17c3.6 0 6.5 2.9 6.5 6.5q0 0.8-0.2 1.6-0.2 0.8-0.6 1.5-0.4 0.8-0.9 1.4-0.5 0.6-1.2 1h-7.2q-0.6-0.4-1.2-1-0.5-0.6-0.9-1.4-0.4-0.7-0.6-1.5-0.2-0.8-0.2-1.6c0-3.6 2.9-6.5 6.5-6.5z"/></svg>`,
|
|
2910
|
+
warning: (c) => `<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><style>.a{fill:${c}}</style><path class="a" d="m9.4 4.3c1.2-1.9 4-1.9 5.2 0l7.4 12.1c1.2 2-0.2 4.6-2.6 4.6h-14.8c-2.4 0-3.8-2.6-2.6-4.6zm3.4 1.1c-0.4-0.7-1.3-0.7-1.7 0l-7.4 12.1c-0.4 0.7 0.1 1.6 0.8 1.6h14.9c0.8 0 1.3-0.9 0.9-1.6zm-0.9 3.7c0.6 0 1 0.5 1 1v3c0 0.6-0.4 1-1 1-0.6 0-1-0.4-1-1v-3c0-0.5 0.4-1 1-1zm-1.1 7.5c0-0.6 0.5-1.1 1.1-1.1 0.6 0 1.2 0.5 1.2 1.1 0 0.7-0.6 1.2-1.2 1.2-0.6 0-1.1-0.5-1.1-1.2z"/></svg>`,
|
|
2911
|
+
danger: (c) => `<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><style>.a{fill:${c}}.b{fill:none;stroke:${c};stroke-linecap:round;stroke-linejoin:round;stroke-width:2}</style><path class="a" d="m12 14.5c-0.3 0-0.6 0.1-0.9 0.4-0.2 0.2-0.3 0.5-0.3 0.8q0 0.6 0.3 0.9c0.3 0.3 0.6 0.4 0.9 0.4 0.3 0 0.6-0.1 0.9-0.4q0.3-0.3 0.3-0.9c0-0.3-0.1-0.6-0.3-0.8-0.3-0.3-0.6-0.4-0.9-0.4z"/><path fill-rule="evenodd" class="b" d="m12 8v4"/><path class="b" d="m15.3 2q0.2 0 0.4 0 0.2 0.1 0.4 0.2 0.2 0 0.3 0.1 0.2 0.1 0.3 0.3l4.7 4.7q0.2 0.1 0.3 0.3 0.1 0.1 0.1 0.3 0.1 0.2 0.2 0.4 0 0.2 0 0.4v6.6q0 0.2 0 0.4-0.1 0.2-0.2 0.4 0 0.2-0.1 0.3-0.1 0.2-0.3 0.3l-4.7 4.7q-0.1 0.2-0.3 0.3-0.1 0.1-0.3 0.1-0.2 0.1-0.4 0.2-0.2 0-0.4 0h-6.6q-0.2 0-0.4 0-0.2-0.1-0.4-0.2-0.2 0-0.3-0.1-0.2-0.1-0.3-0.3l-4.7-4.7q-0.2-0.1-0.3-0.3-0.1-0.1-0.1-0.3-0.1-0.2-0.2-0.4 0-0.2 0-0.4v-6.6q0-0.2 0-0.4 0.1-0.2 0.2-0.4 0-0.2 0.1-0.3 0.1-0.2 0.3-0.3l4.7-4.7q0.1-0.2 0.3-0.3 0.1-0.1 0.3-0.1 0.2-0.1 0.4-0.2 0.2 0 0.4 0z"/></svg>`
|
|
2912
|
+
};
|
|
2913
|
+
var admonitionIconsFallback = {
|
|
2914
|
+
note: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAilBMVEUAAABmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmbNZZ8EAAAALnRSTlMA//BXB/7YePSoRaAMKt3USM91y08CZiK8tUzjLxM82zjGblsPPSRhBkJzadIoQt+BPAAAAMBJREFUeJydUVcOwjAUi9Mm3YsOoLRlb7j/9RDJK22EEAL/OLKd5A3G/oVtBbnLs8CyTd2PQYj9se4IzKuubffVDMIZ6eBpfy44Xk4q3GhIRa6gVLiApThJ1M9bSF2BhUTxAZg+uZQ6yDxc9dVupfkCT7HUwQENpGIOauqWad6BkxFqAaBqyJCITKOhpzxsTGNCn/fl9ka5pHJtapDla0XHvkFWC343RnL+NkQ99vrEmF2YY/+8KLXajLv522p/wANc8QfHdtgFgwAAAABJRU5ErkJggg==`,
|
|
2915
|
+
info: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAilBMVEUAAAAZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtL9UeA9AAAALnRSTlMA//BXB/7YePSoRaAMKt3USM91y08CZiK8tUzjLxM82zjGblsPPSRhBkJzadIoQt+BPAAAAMBJREFUeJydUVcOwjAUi9Mm3YsOoLRlb7j/9RDJK22EEAL/OLKd5A3G/oVtBbnLs8CyTd2PQYj9se4IzKuubffVDMIZ6eBpfy44Xk4q3GhIRa6gVLiApThJ1M9bSF2BhUTxAZg+uZQ6yDxc9dVupfkCT7HUwQENpGIOauqWad6BkxFqAaBqyJCITKOhpzxsTGNCn/fl9ka5pHJtapDla0XHvkFWC343RnL+NkQ99vrEmF2YY/+8KLXajLv522p/wANc8QfHdtgFgwAAAABJRU5ErkJggg==`,
|
|
2916
|
+
tip: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAllBMVEUAAAAufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTLBWjFSAAAAMnRSTlMA//AF9MXq5ssCgtub1XmmJgghfRkRC+exGm0tRBX+i2c1Tt8pMHC0RwxdtsOoo64eS7UJBqAAAADPSURBVHichVLZEoIwENtU7luUUwUB79v//zkHpKWiM+Ypk+x2szsl+ofAUhlTrWAkR7cCHQonlvWwASvrR7Sc6XBXgx67sOc9vcCYCGMmdKKjijXnE4Z6aF+g4fQJ3ZQGGtj2bIernKTESYxwZCOHJzpS2bCQ9+wANiQkUrHkqbQhIVEFTUTxoEWcm4YYQTS1YWQ99eFK71bK+4QtFL5Fh1A4SijrRAm6a2XQP3VKeMfYOHPjY9cWKfarqYP7WKcFa+vZ5sugja8l/vgv/MALHToJTMdMgakAAAAASUVORK5CYII=`,
|
|
2917
|
+
warning: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAllBMVEUAAACyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagD2Sl3HAAAAMnRSTlMA/w3r5vwbWbsElS1drcsiElT5ArX2wo045GaR779hCUx02KB7BdPiPyiCaxZSMsa5W/G4KzAAAADCSURBVHictZDJEoJADES7GZBFdlABFdz39f9/zmIoCge9WeaS5HVNkmngD6F5pKd9EeYkaX3yJ+NDRJofgwQ3gEO9P2zKlQ1oOicqHxrM6hzROCrCnWvYuQekHL/zkrMQA+rAYES346FggUbAkqLb79BHK9g7Tlt+ozEEUIlH3WVk3QFIfPXG5j2AgvtKFudr81edpcwzLiQIGG9lETX73da55NQeOpeWmUx79tiWFC6i9luNPJSzx7GKYyvom/9TvACzaQfmlOkXxAAAAABJRU5ErkJggg==`,
|
|
2918
|
+
danger: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAdVBMVEUAAADTLy+/ICDSLi7SLi7GHBzPKSnSLy/SLi7MGhrTLy/TLy/SLy/RLi7SLy/SLi7TLi7KLCzRLi7SLi7KKyvTLi7TLy/EJyfTLi6/KyvTLy/TLy/SLi7TLy/MKSnMKyvTLy/SLy/TLi7OJyfSLi7TLy/TLy/z9/9QAAAAJ3RSTlMA/wi9wgklCyEK7L/UFuXA4B0c6Rj+Vg2zDMXc2LkZHre0lhqmySRpaHOvAAAAtElEQVR4nKVRRxLDIBBbGdx77yX1/0/M2EBMHE8u0QWtBCwriH6juECiKXQ9DJQOBOGuzz1YK2jLMNyU7kRghioMhmQW1Mo1ncjgiKyVTB5iTSdyObz1BdeSu0LxfbHaXXknIqCWWwFJ6o29yyPDH4Yvm38ZO/BpVNXBkOM9AUcwczNGyAHtcbEF4WiIKPUQm8dI0vMQ8y3Ek9hlK6I0wSJvMxn6bD8+DedfS5Q9lN5p+0/xArzsBn7bxA9sAAAAAElFTkSuQmCC`
|
|
2919
|
+
};
|
|
2920
|
+
var admonitionIconsFallbackBlack = {
|
|
2921
|
+
note: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAilBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATAggvAAAALnRSTlMA//AHeFjY9N2gDCpEqM3TSToi47XVR9D9Zky8L3RWPRNPxm4CUFsPJGFpKAbKjdKbTAAAAMRJREFUeJydUdkSgjAM7BbKfYjKKQp4n///e860AdpxfNC8bGY3bZINY/+GJfzE5aUvLJOPPFB4kc7bwLru+r6rN4Ct83wx5ic+K0XlhnNV6FZUFaywk5insvMZjppAIJd4BZYycSAkxrirp1mmcIuYClThFC0ciRy0VJoovICTECgCoGlIcBCaQktfxTiYwthcIDWEo0fjWrQge+0l3MYFWQP+0C3BZLBu4qCZqGxvnoxZg2k7Y8WXQ8nTltxNPk77Q7wBVQsHvaZMK8YAAAAASUVORK5CYII=`,
|
|
2922
|
+
info: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAilBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATAggvAAAALnRSTlMA//AHeFjY9N2gDCpEqM3TSToi47XVR9D9Zky8L3RWPRNPxm4CUFsPJGFpKAbKjdKbTAAAAMRJREFUeJydUdkSgjAM7BbKfYjKKQp4n///e860AdpxfNC8bGY3bZINY/+GJfzE5aUvLJOPPFB4kc7bwLru+r6rN4Ct83wx5ic+K0XlhnNV6FZUFaywk5insvMZjppAIJd4BZYycSAkxrirp1mmcIuYClThFC0ciRy0VJoovICTECgCoGlIcBCaQktfxTiYwthcIDWEo0fjWrQge+0l3MYFWQP+0C3BZLBu4qCZqGxvnoxZg2k7Y8WXQ8nTltxNPk77Q7wBVQsHvaZMK8YAAAAASUVORK5CYII=`,
|
|
2923
|
+
tip: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAh1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD3YishAAAALXRSTlMA/+gFxBrmCvAH9EXbmyd9sC+kedXxTckiEW3ryx8VZ4s1gt8rqHC1XQyDshgZvchdAAAAyUlEQVR4nIWS5w7CMAyEfR20dO/SSSdlvf/zIaokDQWJ+3XyZ8dDIfonb9BMUxu8XbiIwOQacrwZkXZZU9j+AUm7xY0EzpNZDbkqgA9H5/6W48y9aiLbyieM3GY4yA1rhMxdMMugw1W0cGVQIhYVgQzuKJmzYW4TEmmw+VTVNiFRD0WkxVAKAXLRgujooGYbHmck0ru9xW8IWHyLVSdBrJMcJ1KwvqV/HmEFTHuwcPDYAQqwtIb7eYNVYfrOT6cvQF5UKdH+L/zQCzA0CJnLbfrbAAAAAElFTkSuQmCC`,
|
|
2924
|
+
warning: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAllBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6C80qAAAAMnRSTlMA/w3mA/xUu1kb610AyyKtEi35lbXvwvZhv2aPCTnYixagayh7TDKSBeLTdII/xrk2W23LF/kAAADFSURBVHictZDJcsIwEET7WbINosDGLGELS8Kanf//OUpyucAmN4q+jOZ1aUZq6QmKMsiif4wpQHrPzxTfCbTuBjm20gDTHDZnaaXIMK/zfsyrrwnxe834YSNrMmnI+JZPGOVqY6T2C50rzx0zlYY+cdf9A7qqDPvBoeJ74r6khfv13R/4zqvL6nZheV/SjNMiHN6S8q+GSagjdgH0KI7hkJT7O1Vy9qt66DRE1mLYiMemwVg7n3ddJg+zx0UdF2mvGf5DugCExweZt6hkrgAAAABJRU5ErkJggg==`,
|
|
2925
|
+
danger: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAYFBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD6T+iNAAAAIHRSTlMA/wi9Cr8lwgwY7Bwhw7cW29Tl4OkdVh6z/bmWpsXXyQwvE4cAAACqSURBVHicpVFHEsIwEJPiFKeRXgjt/79ksNfEBA8XdNJKnpFXC/xGvlKw5r7edE4nu2bXl5ZKW6oV29LpVU8VuSFSHBZL09HTgahmn5rcgomnA3HG4vWDC7PYKkkizu18BUBKLkgh2rD3eGT8w0gk/MvYwU9jng+GrJeSlSxvjM0tqLeTJXHGFcAUqmQKlziaEgO1SxQwDTyJE/mHAvJ7+LRA+XB67b0P4gn4WAUqr4Oj0wAAAABJRU5ErkJggg==`
|
|
2926
|
+
};
|
|
1268
2927
|
function addListItemLevel(levels, level, startIndex, itemCount, ordered, alternativeStyle, mark) {
|
|
1269
2928
|
if (levels.find((v) => v.level == level)) return;
|
|
1270
2929
|
levels.sort((a, b) => a.level - b.level);
|
|
1271
2930
|
let indent = level;
|
|
1272
|
-
const format = ordered ?
|
|
2931
|
+
const format = ordered ? import_docx2.LevelFormat.DECIMAL : import_docx2.LevelFormat.BULLET;
|
|
1273
2932
|
for (let i = 1; i < levels.length + 1; i++) {
|
|
1274
2933
|
const prevprevformat = levels[i - 2]?.format;
|
|
1275
2934
|
const prevformat = levels[i - 1]?.format;
|
|
@@ -1295,15 +2954,15 @@ function addListItemLevel(levels, level, startIndex, itemCount, ordered, alterna
|
|
|
1295
2954
|
} : {},
|
|
1296
2955
|
paragraph: {
|
|
1297
2956
|
indent: {
|
|
1298
|
-
left: (0,
|
|
1299
|
-
hanging: (0,
|
|
1300
|
-
...alternativeStyle ? { firstLine: (0,
|
|
2957
|
+
left: (0, import_docx2.convertMillimetersToTwip)(left + 5 * indent),
|
|
2958
|
+
hanging: (0, import_docx2.convertMillimetersToTwip)(longList && !alternativeStyle ? 8 : 5),
|
|
2959
|
+
...alternativeStyle ? { firstLine: (0, import_docx2.convertMillimetersToTwip)(12.5) } : {}
|
|
1301
2960
|
}
|
|
1302
2961
|
}
|
|
1303
2962
|
}
|
|
1304
2963
|
});
|
|
1305
2964
|
}
|
|
1306
|
-
var RawXml = class extends
|
|
2965
|
+
var RawXml = class extends import_docx2.XmlComponent {
|
|
1307
2966
|
constructor(tag, attributes = {}, text = null) {
|
|
1308
2967
|
super(tag);
|
|
1309
2968
|
if (Object.keys(attributes).length > 0)
|
|
@@ -1313,85 +2972,227 @@ var RawXml = class extends import_docx.XmlComponent {
|
|
|
1313
2972
|
}
|
|
1314
2973
|
};
|
|
1315
2974
|
function renderCodeHighlighting(code, lang) {
|
|
1316
|
-
const
|
|
2975
|
+
const language = lang.trim().toLowerCase();
|
|
2976
|
+
const grammar = import_prismjs.default.languages[language];
|
|
1317
2977
|
if (!grammar) return null;
|
|
1318
2978
|
const themeColors = {
|
|
1319
2979
|
// shared
|
|
1320
2980
|
"plain": "000000",
|
|
1321
2981
|
"comment": "008000",
|
|
2982
|
+
"hashbang": "008000",
|
|
2983
|
+
"shebang": "008000",
|
|
1322
2984
|
"punctuation": "000000",
|
|
1323
2985
|
"operator": "000000",
|
|
2986
|
+
"script": "000000",
|
|
2987
|
+
"script-punctuation": "000000",
|
|
2988
|
+
"expression": "000000",
|
|
2989
|
+
"interpolation": "000000",
|
|
1324
2990
|
// keywords & control flow
|
|
1325
2991
|
"keyword": "0000ff",
|
|
2992
|
+
"control-flow": "af00db",
|
|
1326
2993
|
"boolean": "0000ff",
|
|
1327
|
-
"
|
|
2994
|
+
"null": "0000ff",
|
|
2995
|
+
"builtin": "267f99",
|
|
1328
2996
|
"important": "0000ff",
|
|
2997
|
+
// "instruction": "0000ff",
|
|
2998
|
+
// "directive": "0000ff",
|
|
2999
|
+
"directive-hash": "0000ff",
|
|
3000
|
+
"macro": "0000ff",
|
|
3001
|
+
"macro-name": "0000ff",
|
|
3002
|
+
"rule": "0000ff",
|
|
1329
3003
|
// literals
|
|
1330
|
-
"string":
|
|
1331
|
-
"
|
|
1332
|
-
"
|
|
1333
|
-
"
|
|
3004
|
+
"string": "a31515",
|
|
3005
|
+
"template-string": "a31515",
|
|
3006
|
+
"template-punctuation": "a31515",
|
|
3007
|
+
"string-literal": "a31515",
|
|
1334
3008
|
"char": "a31515",
|
|
3009
|
+
"number": "098658",
|
|
3010
|
+
"datetime": "098658",
|
|
3011
|
+
"constant": "0070c1",
|
|
3012
|
+
"symbol": "0070c1",
|
|
3013
|
+
"scalar": "0000ff",
|
|
3014
|
+
"conversion-option": "0000ff",
|
|
1335
3015
|
// identifiers
|
|
1336
|
-
"class-name":
|
|
1337
|
-
"
|
|
1338
|
-
"
|
|
1339
|
-
"
|
|
1340
|
-
"
|
|
3016
|
+
"class-name": "267f99",
|
|
3017
|
+
"class-name-definition": "267f99",
|
|
3018
|
+
"type-definition": "267f99",
|
|
3019
|
+
"return-type": "267f99",
|
|
3020
|
+
"attribute": "267f99",
|
|
3021
|
+
"attribute-class-name": "267f99",
|
|
3022
|
+
"annotation": "267f99",
|
|
3023
|
+
"decorator": "267f99",
|
|
3024
|
+
"namespace": "267f99",
|
|
3025
|
+
"double-colon": "267f99",
|
|
3026
|
+
"function": "795e26",
|
|
3027
|
+
"function-definition": "795e26",
|
|
3028
|
+
"method-definition": "795e26",
|
|
3029
|
+
"variable": "001080",
|
|
3030
|
+
"parameter": "001080",
|
|
3031
|
+
"environment": "001080",
|
|
3032
|
+
"assign-left": "001080",
|
|
3033
|
+
"property": language === "css" ? "e50000" : language === "json" ? "0451a5" : "001080",
|
|
3034
|
+
"key": "0451a5",
|
|
3035
|
+
"package": "000000",
|
|
3036
|
+
"lifetime-annotation": "0000ff",
|
|
1341
3037
|
// markup / HTML / XML
|
|
1342
|
-
"tag":
|
|
3038
|
+
"tag": "800000",
|
|
1343
3039
|
"selector": "800000",
|
|
1344
3040
|
"attr-name": "e50000",
|
|
1345
|
-
"attr-value": "
|
|
3041
|
+
"attr-value": "0000ff",
|
|
1346
3042
|
"entity": "800000",
|
|
1347
3043
|
"doctype": "808080",
|
|
3044
|
+
"doctype-tag": "808080",
|
|
3045
|
+
"name": "808080",
|
|
1348
3046
|
"cdata": "808080",
|
|
1349
3047
|
"prolog": "808080",
|
|
3048
|
+
"delimiter": language === "php" ? "800000" : "a31515",
|
|
1350
3049
|
// CSS specific
|
|
1351
3050
|
"atrule": "0000ff",
|
|
1352
3051
|
"url": "0451a5",
|
|
1353
3052
|
// regex
|
|
1354
|
-
"regex": "811f3f"
|
|
3053
|
+
"regex": "811f3f",
|
|
3054
|
+
"regex-delimiter": "811f3f",
|
|
3055
|
+
"regex-source": "811f3f",
|
|
3056
|
+
"regex-flags": "0000ff",
|
|
3057
|
+
"interpolation-punctuation": "0000ff",
|
|
3058
|
+
// Markdown
|
|
3059
|
+
"title": "800000",
|
|
3060
|
+
"blockquote": "0451a5",
|
|
3061
|
+
"list": "0451a5",
|
|
3062
|
+
"bold": "000080",
|
|
3063
|
+
"italic": "800080",
|
|
3064
|
+
"code": "800000",
|
|
3065
|
+
"code-snippet": "800000",
|
|
3066
|
+
"code-block": "800000",
|
|
3067
|
+
"code-language": "0000ff",
|
|
3068
|
+
"inserted": "098658",
|
|
3069
|
+
"deleted": "a31515",
|
|
3070
|
+
"strike": "000000",
|
|
3071
|
+
"hr": "800000"
|
|
1355
3072
|
};
|
|
3073
|
+
const controlFlowKeywords = /* @__PURE__ */ new Set([
|
|
3074
|
+
"if",
|
|
3075
|
+
"else",
|
|
3076
|
+
"elif",
|
|
3077
|
+
"elseif",
|
|
3078
|
+
"unless",
|
|
3079
|
+
"then",
|
|
3080
|
+
"switch",
|
|
3081
|
+
"case",
|
|
3082
|
+
"default",
|
|
3083
|
+
"when",
|
|
3084
|
+
"match",
|
|
3085
|
+
"for",
|
|
3086
|
+
"foreach",
|
|
3087
|
+
"while",
|
|
3088
|
+
"do",
|
|
3089
|
+
"until",
|
|
3090
|
+
"loop",
|
|
3091
|
+
"break",
|
|
3092
|
+
"continue",
|
|
3093
|
+
"return",
|
|
3094
|
+
"goto",
|
|
3095
|
+
"throw",
|
|
3096
|
+
"throws",
|
|
3097
|
+
"try",
|
|
3098
|
+
"catch",
|
|
3099
|
+
"except",
|
|
3100
|
+
"finally",
|
|
3101
|
+
"rescue",
|
|
3102
|
+
"ensure",
|
|
3103
|
+
"await",
|
|
3104
|
+
"yield",
|
|
3105
|
+
"new",
|
|
3106
|
+
"delete",
|
|
3107
|
+
"in",
|
|
3108
|
+
"of",
|
|
3109
|
+
"instanceof"
|
|
3110
|
+
]);
|
|
1356
3111
|
const tokens = import_prismjs.default.tokenize(code, grammar);
|
|
1357
3112
|
return tokensToParagraphs(tokens);
|
|
1358
3113
|
function tokensToParagraphs(tokens2) {
|
|
1359
3114
|
const paragraphs = [];
|
|
1360
3115
|
let currentRuns = [];
|
|
1361
|
-
|
|
3116
|
+
const plainStyle = { color: themeColors.plain, bold: false, italics: false, strike: false };
|
|
3117
|
+
function addText(text, style) {
|
|
1362
3118
|
const lines = text.split("\n");
|
|
1363
3119
|
lines.forEach((line, index) => {
|
|
1364
3120
|
currentRuns.push(
|
|
1365
|
-
new
|
|
3121
|
+
new import_docx2.TextRun({
|
|
1366
3122
|
text: line,
|
|
1367
|
-
color:
|
|
1368
|
-
bold:
|
|
1369
|
-
italics:
|
|
3123
|
+
color: style.color,
|
|
3124
|
+
bold: style.bold,
|
|
3125
|
+
italics: style.italics,
|
|
3126
|
+
strike: style.strike
|
|
1370
3127
|
})
|
|
1371
3128
|
);
|
|
1372
3129
|
if (index < lines.length - 1) {
|
|
1373
|
-
paragraphs.push(new
|
|
3130
|
+
paragraphs.push(new import_docx2.Paragraph({ children: currentRuns, style: STYLE_code }));
|
|
1374
3131
|
currentRuns = [];
|
|
1375
3132
|
}
|
|
1376
3133
|
});
|
|
1377
3134
|
}
|
|
1378
|
-
function process2(token,
|
|
3135
|
+
function process2(token, parentStyle = plainStyle) {
|
|
1379
3136
|
if (typeof token === "string")
|
|
1380
|
-
addText(token,
|
|
1381
|
-
else
|
|
1382
|
-
token.
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
3137
|
+
addText(token, parentStyle);
|
|
3138
|
+
else {
|
|
3139
|
+
const aliases = typeof token.alias == "string" ? [token.alias] : token.alias || [];
|
|
3140
|
+
const types = [token.type, ...aliases];
|
|
3141
|
+
const colorType = types.find((type) => themeColors[type]);
|
|
3142
|
+
const isControlFlow = token.type == "keyword" && typeof token.content == "string" && controlFlowKeywords.has(token.content);
|
|
3143
|
+
const inheritPunctuation = token.type == "punctuation" && parentStyle.color != themeColors.plain;
|
|
3144
|
+
const style = {
|
|
3145
|
+
color: isControlFlow ? themeColors["control-flow"] : inheritPunctuation ? parentStyle.color : colorType ? themeColors[colorType] : parentStyle.color,
|
|
3146
|
+
bold: parentStyle.bold || types.includes("bold") || types.includes("important") || types.includes("title"),
|
|
3147
|
+
italics: parentStyle.italics || types.includes("italic"),
|
|
3148
|
+
strike: parentStyle.strike || types.includes("strike")
|
|
3149
|
+
};
|
|
3150
|
+
if (Array.isArray(token.content))
|
|
3151
|
+
token.content.forEach((t) => process2(t, style));
|
|
3152
|
+
else if (typeof token.content === "string")
|
|
3153
|
+
addText(token.content, style);
|
|
3154
|
+
else
|
|
3155
|
+
process2(token.content, style);
|
|
3156
|
+
}
|
|
1387
3157
|
}
|
|
1388
3158
|
tokens2.forEach((t) => process2(t));
|
|
1389
3159
|
if (currentRuns.length > 0) {
|
|
1390
|
-
paragraphs.push(new
|
|
3160
|
+
paragraphs.push(new import_docx2.Paragraph({ children: currentRuns, style: STYLE_code }));
|
|
1391
3161
|
}
|
|
1392
3162
|
return paragraphs;
|
|
1393
3163
|
}
|
|
1394
3164
|
}
|
|
3165
|
+
function estimateNumberWidthPt(text, fontSizePt) {
|
|
3166
|
+
const charWidths = {
|
|
3167
|
+
".": 0.25,
|
|
3168
|
+
",": 0.25,
|
|
3169
|
+
"-": 0.333,
|
|
3170
|
+
"(": 0.333,
|
|
3171
|
+
")": 0.333,
|
|
3172
|
+
" ": 0.25,
|
|
3173
|
+
"a": 0.444,
|
|
3174
|
+
"b": 0.5,
|
|
3175
|
+
"c": 0.444,
|
|
3176
|
+
"d": 0.5,
|
|
3177
|
+
"e": 0.444,
|
|
3178
|
+
"A": 0.722,
|
|
3179
|
+
"B": 0.667,
|
|
3180
|
+
"C": 0.667,
|
|
3181
|
+
"D": 0.722,
|
|
3182
|
+
"E": 0.611,
|
|
3183
|
+
"\u0430": 0.5,
|
|
3184
|
+
"\u0431": 0.5,
|
|
3185
|
+
"\u0432": 0.5,
|
|
3186
|
+
"\u0433": 0.5,
|
|
3187
|
+
"\u0434": 0.5,
|
|
3188
|
+
"\u0410": 0.722,
|
|
3189
|
+
"\u0411": 0.667,
|
|
3190
|
+
"\u0412": 0.667,
|
|
3191
|
+
"\u0413": 0.611,
|
|
3192
|
+
"\u0414": 0.722
|
|
3193
|
+
};
|
|
3194
|
+
return [...text].reduce((width, char) => width + (charWidths[char] ?? 0.5) * fontSizePt, 0);
|
|
3195
|
+
}
|
|
1395
3196
|
function genXml_app({ totalTime }) {
|
|
1396
3197
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1397
3198
|
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
|
|
@@ -1399,23 +3200,32 @@ function genXml_app({ totalTime }) {
|
|
|
1399
3200
|
</Properties>
|
|
1400
3201
|
`;
|
|
1401
3202
|
}
|
|
3203
|
+
function escapeXml(value) {
|
|
3204
|
+
return value.replaceAll(/[<>&"']/g, (ch) => ({
|
|
3205
|
+
"<": "<",
|
|
3206
|
+
">": ">",
|
|
3207
|
+
"&": "&",
|
|
3208
|
+
'"': """,
|
|
3209
|
+
"'": "'"
|
|
3210
|
+
})[ch]);
|
|
3211
|
+
}
|
|
1402
3212
|
function genXml_core({ title, creator, createdAt, modifiedAt }) {
|
|
1403
3213
|
if (!createdAt) createdAt = /* @__PURE__ */ new Date();
|
|
1404
3214
|
if (!modifiedAt) modifiedAt = /* @__PURE__ */ new Date();
|
|
1405
3215
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1406
3216
|
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
1407
|
-
<dc:title>${title || "Document"}</dc:title>
|
|
1408
|
-
<dc:creator>${creator || "Student"}</dc:creator>
|
|
1409
|
-
<cp:lastModifiedBy>${creator || "Student"}</cp:lastModifiedBy>
|
|
3217
|
+
<dc:title>${escapeXml(title || "Document")}</dc:title>
|
|
3218
|
+
<dc:creator>${escapeXml(creator || "Student")}</dc:creator>
|
|
3219
|
+
<cp:lastModifiedBy>${escapeXml(creator || "Student")}</cp:lastModifiedBy>
|
|
1410
3220
|
<dcterms:created xsi:type="dcterms:W3CDTF">${createdAt.toISOString()}</dcterms:created>
|
|
1411
3221
|
<dcterms:modified xsi:type="dcterms:W3CDTF">${modifiedAt.toISOString()}</dcterms:modified>
|
|
1412
3222
|
</cp:coreProperties>`;
|
|
1413
3223
|
}
|
|
1414
3224
|
function genXml_style(assets, doc) {
|
|
1415
|
-
let xml =
|
|
3225
|
+
let xml = fs4.readFileSync(import_path3.default.join(assets, "styles.xml"), { encoding: "utf8" });
|
|
1416
3226
|
xml = editStyle(`<w:style w:type="paragraph" w:default="1" w:styleId="a2">`, (xml2) => {
|
|
1417
3227
|
xml2 = replaceTag(xml2, `<w:spacing w:line="${doc.text.line_spacing * 240}" w:lineRule="auto" w:after="${doc.text.spacing.after * 20}"/>`);
|
|
1418
|
-
xml2 = replaceTag(xml2, `<w:ind w:firstLine="${(0,
|
|
3228
|
+
xml2 = replaceTag(xml2, `<w:ind w:firstLine="${(0, import_docx2.convertMillimetersToTwip)(doc.text.indent * 10)}"/>`);
|
|
1419
3229
|
xml2 = replaceTag(xml2, `<w:sz w:val="${doc.text.size * 2}"/>`);
|
|
1420
3230
|
return xml2;
|
|
1421
3231
|
});
|
|
@@ -1437,7 +3247,33 @@ function genXml_style(assets, doc) {
|
|
|
1437
3247
|
xml2 = addrPrStyle(xml2, doc.table.title.style == "bold" ? "<w:b/>" : "<w:i/>");
|
|
1438
3248
|
return xml2;
|
|
1439
3249
|
});
|
|
3250
|
+
const admonitionTypes = ["note", "info", "tip", "warning", "danger"];
|
|
3251
|
+
for (const type of admonitionTypes) {
|
|
3252
|
+
const suffix = type == "note" ? "" : type;
|
|
3253
|
+
const styleId = `xAdmonition${suffix}`;
|
|
3254
|
+
const styleTag = `<w:style w:type="paragraph" w:customStyle="1" w:styleId="${styleId}">`;
|
|
3255
|
+
const style = doc.admonition[type];
|
|
3256
|
+
xml = editStyle(styleTag, (xml2) => {
|
|
3257
|
+
xml2 = replaceTagOrInsertAfter(xml2, "<w:pPr>", `<w:spacing w:before="${style.spacing.before * 20}" w:after="${style.spacing.after * 20}"/>`);
|
|
3258
|
+
xml2 = replaceTagOrInsertAfter(xml2, "<w:pPr>", `<w:ind w:left="${(0, import_docx2.convertMillimetersToTwip)(style.indent * 10)}" w:firstLine="0"/>`);
|
|
3259
|
+
xml2 = replaceBorder(xml2, "top", style.padding.top, "FFFFFF", 2);
|
|
3260
|
+
xml2 = replaceBorder(xml2, "right", style.padding.right, "FFFFFF", 2);
|
|
3261
|
+
xml2 = replaceBorder(xml2, "bottom", style.padding.bottom, "FFFFFF", 2);
|
|
3262
|
+
xml2 = replaceBorder(xml2, "left", style.padding.left, style.color, Math.max(1, Math.round(style.bar_width * 8)));
|
|
3263
|
+
xml2 = replaceTagOrInsertAfter(xml2, "<w:pPr>", `<w:shd w:val="clear" w:color="auto" w:fill="${style.background}"/>`);
|
|
3264
|
+
return xml2;
|
|
3265
|
+
});
|
|
3266
|
+
}
|
|
1440
3267
|
return xml;
|
|
3268
|
+
function replaceBorder(xml2, side, space, color, size) {
|
|
3269
|
+
return replaceTagOrInsertAfter(xml2, "<w:pBdr>", `<w:${side} w:val="single" w:sz="${size}" w:space="${space}" w:color="${color}"/>`);
|
|
3270
|
+
}
|
|
3271
|
+
function replaceTagOrInsertAfter(xml2, insertAfter2, tag) {
|
|
3272
|
+
const tagName = /<([^\s>]+)/.exec(tag)?.[1];
|
|
3273
|
+
if (!tagName) throw err();
|
|
3274
|
+
const regex = new RegExp(`<${tagName}\\b[^>]*/>`);
|
|
3275
|
+
return regex.test(xml2) ? xml2.replace(regex, tag) : xml2.replace(insertAfter2, `${insertAfter2}${tag}`);
|
|
3276
|
+
}
|
|
1441
3277
|
function replaceTag(xml2, tag) {
|
|
1442
3278
|
const tagName = /<([^\s]+)\b/.exec(tag)?.[1];
|
|
1443
3279
|
if (!tagName) throw err();
|
|
@@ -1477,13 +3313,14 @@ function genXml_style(assets, doc) {
|
|
|
1477
3313
|
|
|
1478
3314
|
// src/enricher.ts
|
|
1479
3315
|
function enrichDoc(doc, logwarn = console.warn) {
|
|
3316
|
+
const warnAt = (line, message) => logwarn(`Line ${doc.mapSourceLine(line)}: ${message}`);
|
|
1480
3317
|
for (let i = 0; i < doc.nodes.length; i++) {
|
|
1481
3318
|
const node = doc.nodes[i];
|
|
1482
|
-
if (node.type == "title" && node.text.toUpperCase().startsWith("\u0422\u0418\u0422\u0423\u041B\u042C\u041D\u0418\u041A")) {
|
|
1483
|
-
doc.nodes.splice(i, 1, { type: "text", text: node.text.toUpperCase() });
|
|
1484
|
-
doc.nodes.splice(i + 1, 0, { type: "pageBreak" });
|
|
3319
|
+
if (node.type == "title" && node.level == 1 && node.text.toUpperCase().startsWith("\u0422\u0418\u0422\u0423\u041B\u042C\u041D\u0418\u041A")) {
|
|
3320
|
+
doc.nodes.splice(i, 1, { type: "text", text: node.text.toUpperCase(), sourceLine: node.sourceLine });
|
|
3321
|
+
doc.nodes.splice(i + 1, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1485
3322
|
i++;
|
|
1486
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u0420\u0415\u0424\u0415\u0420\u0410\u0422") {
|
|
3323
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u0420\u0415\u0424\u0415\u0420\u0410\u0422") {
|
|
1487
3324
|
node.level = 0;
|
|
1488
3325
|
node.text = node.text.toUpperCase();
|
|
1489
3326
|
const nextNode = doc.nodes[i + 1];
|
|
@@ -1491,81 +3328,83 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1491
3328
|
nextNode.noIndent = true;
|
|
1492
3329
|
nextNode.text = trimEnd(nextNode.text, ".").toUpperCase();
|
|
1493
3330
|
}
|
|
1494
|
-
doc.nodes.splice(i + 1, 0, { type: "text", tags: ["synopsis"], text: "\u041E\u0442\u0447\u0435\u0442 [!pages] \u0441., [!imgs] \u0440\u0438\u0441., [!tables] \u0442\u0430\u0431\u043B., [!codes] \u043B\u0438\u0441\u0442., [!sources] \u0438\u0441\u0442\u043E\u0447\u043D." });
|
|
3331
|
+
doc.nodes.splice(i + 1, 0, { type: "text", tags: ["synopsis"], text: "\u041E\u0442\u0447\u0435\u0442 [!pages] \u0441., [!imgs] \u0440\u0438\u0441., [!tables] \u0442\u0430\u0431\u043B., [!codes] \u043B\u0438\u0441\u0442., [!sources] \u0438\u0441\u0442\u043E\u0447\u043D.", sourceLine: node.sourceLine });
|
|
1495
3332
|
while (i + 1 < doc.nodes.length && doc.nodes[i + 1]?.type != "title") i++;
|
|
1496
|
-
doc.nodes.splice(i + 1, 0, { type: "pageBreak" });
|
|
1497
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u041E\u0413\u041B\u0410\u0412\u041B\u0415\u041D\u0418\u0415") {
|
|
3333
|
+
doc.nodes.splice(i + 1, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
3334
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u041E\u0413\u041B\u0410\u0412\u041B\u0415\u041D\u0418\u0415") {
|
|
1498
3335
|
node.level = 0;
|
|
1499
3336
|
node.text = node.text.toUpperCase();
|
|
1500
|
-
doc.nodes.splice(i + 1, 0, { type: "tableOfContents" });
|
|
1501
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u0422\u0415\u0420\u041C\u0418\u041D\u042B \u0418 \u041E\u041F\u0420\u0415\u0414\u0415\u041B\u0415\u041D\u0418\u042F") {
|
|
3337
|
+
doc.nodes.splice(i + 1, 0, { type: "tableOfContents", sourceLine: node.sourceLine });
|
|
3338
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u0422\u0415\u0420\u041C\u0418\u041D\u042B \u0418 \u041E\u041F\u0420\u0415\u0414\u0415\u041B\u0415\u041D\u0418\u042F") {
|
|
1502
3339
|
node.center = true;
|
|
1503
3340
|
node.text = node.text.toUpperCase();
|
|
1504
|
-
const warn = () =>
|
|
3341
|
+
const warn = (ln) => warnAt(ln, "Wrong format of \u0422\u0415\u0420\u041C\u0418\u041D\u042B \u0418 \u041E\u041F\u0420\u0415\u0414\u0415\u041B\u0415\u041D\u0418\u042F");
|
|
1505
3342
|
const list = doc.nodes[i + 1];
|
|
1506
3343
|
if (list?.type != "list") {
|
|
1507
|
-
warn();
|
|
3344
|
+
warn(node.sourceLine);
|
|
1508
3345
|
continue;
|
|
1509
3346
|
}
|
|
1510
|
-
const rows = [tableRow("\u0422\u0435\u0440\u043C\u0438\u043D", "\u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0435")];
|
|
3347
|
+
const rows = [tableRow(list.sourceLine, "\u0422\u0435\u0440\u043C\u0438\u043D", "\u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0435")];
|
|
1511
3348
|
const align = ["l", "l"];
|
|
1512
3349
|
for (const item of list.items) {
|
|
1513
3350
|
if (item.type != "listItem") {
|
|
1514
|
-
warn();
|
|
3351
|
+
warn(item.sourceLine);
|
|
1515
3352
|
continue;
|
|
1516
3353
|
}
|
|
1517
3354
|
const i2 = item.text.indexOf(":");
|
|
1518
3355
|
if (i2 < 0) {
|
|
1519
|
-
warn();
|
|
3356
|
+
warn(item.sourceLine);
|
|
1520
3357
|
continue;
|
|
1521
3358
|
}
|
|
1522
|
-
rows.push(tableRow(item.text.slice(0, i2).trim(), item.text.slice(i2 + 1).trim()));
|
|
3359
|
+
rows.push(tableRow(item.sourceLine, item.text.slice(0, i2).trim(), item.text.slice(i2 + 1).trim()));
|
|
1523
3360
|
}
|
|
1524
|
-
doc.nodes.splice(i + 1, 1, { type: "text", text: "\u0412 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0435\u043C \u043E\u0442\u0447\u0435\u0442\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u044E\u0442\u0441\u044F \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0435 \u0442\u0435\u0440\u043C\u0438\u043D\u044B \u0441 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u043C\u0438 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u044F\u043C\u0438." });
|
|
1525
|
-
doc.nodes.splice(i + 2, 0, { type: "table", align, rows, normalFontSize: true, tags: ["definitions_table"] });
|
|
1526
|
-
doc.nodes.splice(i + 3, 0, { type: "pageBreak" });
|
|
1527
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u041F\u0415\u0420\u0415\u0427\u0415\u041D\u042C \u0421\u041E\u041A\u0420\u0410\u0429\u0415\u041D\u0418\u0419 \u0418 \u041E\u0411\u041E\u0417\u041D\u0410\u0427\u0415\u041D\u0418\u0419") {
|
|
3361
|
+
doc.nodes.splice(i + 1, 1, { type: "text", text: "\u0412 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0435\u043C \u043E\u0442\u0447\u0435\u0442\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u044E\u0442\u0441\u044F \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0435 \u0442\u0435\u0440\u043C\u0438\u043D\u044B \u0441 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u043C\u0438 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u044F\u043C\u0438.", sourceLine: node.sourceLine });
|
|
3362
|
+
doc.nodes.splice(i + 2, 0, { type: "table", align, rows, normalFontSize: true, tags: ["definitions_table"], sourceLine: node.sourceLine });
|
|
3363
|
+
doc.nodes.splice(i + 3, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
3364
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u041F\u0415\u0420\u0415\u0427\u0415\u041D\u042C \u0421\u041E\u041A\u0420\u0410\u0429\u0415\u041D\u0418\u0419 \u0418 \u041E\u0411\u041E\u0417\u041D\u0410\u0427\u0415\u041D\u0418\u0419") {
|
|
1528
3365
|
node.center = true;
|
|
1529
3366
|
node.text = node.text.toUpperCase();
|
|
1530
|
-
const warn = () =>
|
|
3367
|
+
const warn = (ln) => warnAt(ln, "Wrong format of \u041F\u0415\u0420\u0415\u0427\u0415\u041D\u042C \u0421\u041E\u041A\u0420\u0410\u0429\u0415\u041D\u0418\u0419 \u0418 \u041E\u0411\u041E\u0417\u041D\u0410\u0427\u0415\u041D\u0418\u0419");
|
|
1531
3368
|
const list = doc.nodes[i + 1];
|
|
1532
3369
|
if (list?.type != "list") {
|
|
1533
|
-
warn();
|
|
3370
|
+
warn(node.sourceLine);
|
|
1534
3371
|
continue;
|
|
1535
3372
|
}
|
|
1536
3373
|
const items = [];
|
|
1537
3374
|
for (const item of list.items) {
|
|
1538
3375
|
if (item.type != "listItem") {
|
|
1539
|
-
warn();
|
|
3376
|
+
warn(item.sourceLine);
|
|
1540
3377
|
continue;
|
|
1541
3378
|
}
|
|
1542
|
-
items.push({ type: "text", text: trimEnd(item.text, "."), noIndent: true, noMargin: true });
|
|
3379
|
+
items.push({ type: "text", text: trimEnd(item.text, "."), noIndent: true, noMargin: true, sourceLine: item.sourceLine });
|
|
1543
3380
|
}
|
|
1544
|
-
doc.nodes.splice(i + 1, 1, { type: "text", text: "\u0412 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0435\u043C \u043E\u0442\u0447\u0435\u0442\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u044E\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0435 \u0441\u043E\u043A\u0440\u0430\u0449\u0435\u043D\u0438\u044F \u0438 \u043E\u0431\u043E\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F." });
|
|
1545
|
-
doc.nodes.splice(i + 2, 0, { type: "pageBreak" });
|
|
3381
|
+
doc.nodes.splice(i + 1, 1, { type: "text", text: "\u0412 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0435\u043C \u043E\u0442\u0447\u0435\u0442\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u044E\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0435 \u0441\u043E\u043A\u0440\u0430\u0449\u0435\u043D\u0438\u044F \u0438 \u043E\u0431\u043E\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F.", sourceLine: node.sourceLine });
|
|
3382
|
+
doc.nodes.splice(i + 2, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1546
3383
|
doc.nodes.splice(i + 2, 0, ...items);
|
|
1547
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u0412\u0412\u0415\u0414\u0415\u041D\u0418\u0415") {
|
|
3384
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u0412\u0412\u0415\u0414\u0415\u041D\u0418\u0415") {
|
|
1548
3385
|
node.center = true;
|
|
1549
3386
|
node.text = node.text.toUpperCase();
|
|
1550
3387
|
for (let j = i + 1; j < doc.nodes.length; j++) {
|
|
1551
3388
|
const node2 = doc.nodes[j];
|
|
1552
3389
|
if (node2?.type == "title") {
|
|
1553
|
-
doc.nodes.splice(j, 0, { type: "pageBreak" });
|
|
3390
|
+
doc.nodes.splice(j, 0, { type: "pageBreak", sourceLine: -1 });
|
|
1554
3391
|
break;
|
|
1555
3392
|
}
|
|
1556
3393
|
}
|
|
1557
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u0417\u0410\u041A\u041B\u042E\u0427\u0415\u041D\u0418\u0415") {
|
|
3394
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u0417\u0410\u041A\u041B\u042E\u0427\u0415\u041D\u0418\u0415") {
|
|
1558
3395
|
node.center = true;
|
|
1559
3396
|
node.text = node.text.toUpperCase();
|
|
1560
|
-
doc.nodes.splice(i, 0, { type: "pageBreak" });
|
|
3397
|
+
doc.nodes.splice(i, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1561
3398
|
i++;
|
|
1562
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u0421\u041F\u0418\u0421\u041E\u041A \u0418\u0421\u041F\u041E\u041B\u042C\u0417\u041E\u0412\u0410\u041D\u041D\u042B\u0425 \u0418\u0421\u0422\u041E\u0427\u041D\u0418\u041A\u041E\u0412") {
|
|
3399
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u0421\u041F\u0418\u0421\u041E\u041A \u0418\u0421\u041F\u041E\u041B\u042C\u0417\u041E\u0412\u0410\u041D\u041D\u042B\u0425 \u0418\u0421\u0422\u041E\u0427\u041D\u0418\u041A\u041E\u0412") {
|
|
1563
3400
|
node.center = true;
|
|
1564
3401
|
node.text = node.text.toUpperCase();
|
|
1565
|
-
doc.nodes.splice(i++, 0, { type: "pageBreak" });
|
|
3402
|
+
doc.nodes.splice(i++, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1566
3403
|
const nextNode = doc.nodes[i + 1];
|
|
1567
3404
|
if (nextNode?.type == "list") {
|
|
1568
3405
|
nextNode.alternativeStyle = true;
|
|
3406
|
+
nextNode.tags = nextNode.tags || [];
|
|
3407
|
+
nextNode.tags.push("sources");
|
|
1569
3408
|
i++;
|
|
1570
3409
|
continue;
|
|
1571
3410
|
}
|
|
@@ -1574,35 +3413,38 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1574
3413
|
mark: ".",
|
|
1575
3414
|
startIndex: 1,
|
|
1576
3415
|
alternativeStyle: true,
|
|
3416
|
+
tags: ["sources"],
|
|
1577
3417
|
ordered: true,
|
|
1578
|
-
items: []
|
|
3418
|
+
items: [],
|
|
3419
|
+
sourceLine: node.sourceLine + 1
|
|
1579
3420
|
};
|
|
1580
3421
|
const startI = i + 1;
|
|
1581
3422
|
while (true) {
|
|
1582
3423
|
const textNode = doc.nodes[i + 1];
|
|
1583
3424
|
if (textNode?.type != "text") break;
|
|
1584
3425
|
const items = textNode.text.split(/\n\s*(?=\[[a-zA-Zа-яА-ЯёЁ_\d]+\])/);
|
|
3426
|
+
let sourceLine = textNode.sourceLine;
|
|
1585
3427
|
for (const item of items)
|
|
1586
|
-
list.items.push({ type: "listItem", text: item });
|
|
3428
|
+
list.items.push({ type: "listItem", text: item, sourceLine: sourceLine++ });
|
|
1587
3429
|
i++;
|
|
1588
3430
|
}
|
|
1589
3431
|
if (list.items.length > 0)
|
|
1590
3432
|
doc.nodes.splice(startI, i - startI + 1, list);
|
|
1591
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u041F\u0420\u0418\u041B\u041E\u0416\u0415\u041D\u0418\u042F") {
|
|
3433
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u041F\u0420\u0418\u041B\u041E\u0416\u0415\u041D\u0418\u042F") {
|
|
1592
3434
|
node.level = 0;
|
|
1593
3435
|
node.center = true;
|
|
1594
3436
|
node.text = node.text.toUpperCase();
|
|
1595
|
-
doc.nodes.splice(i++, 0, { type: "pageBreak" });
|
|
3437
|
+
doc.nodes.splice(i++, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1596
3438
|
for (let j = i + 1; j < doc.nodes.length; j++) {
|
|
1597
3439
|
const node2 = doc.nodes[j];
|
|
1598
3440
|
if (node2.type == "title") {
|
|
1599
|
-
doc.nodes.splice(i++ + 1, 0, { type: "text", text: "\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 " + node2.text.replace("]", "].") });
|
|
3441
|
+
doc.nodes.splice(i++ + 1, 0, { type: "text", text: "\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 " + node2.text.replace("]", "]."), sourceLine: node2.sourceLine });
|
|
1600
3442
|
j++;
|
|
1601
3443
|
node2.level = 1;
|
|
1602
3444
|
const splitI = node2.text.indexOf("]");
|
|
1603
|
-
doc.nodes.splice(j++, 0, { type: "pageBreak" });
|
|
3445
|
+
doc.nodes.splice(j++, 0, { type: "pageBreak", sourceLine: node2.sourceLine });
|
|
1604
3446
|
if (splitI >= 0) {
|
|
1605
|
-
doc.nodes.splice(j++ + 1, 0, { type: "text", text: node2.text.slice(splitI + 1), center: true });
|
|
3447
|
+
doc.nodes.splice(j++ + 1, 0, { type: "text", text: node2.text.slice(splitI + 1), center: true, sourceLine: node2.sourceLine });
|
|
1606
3448
|
node2.text = "\u041F\u0420\u0418\u041B\u041E\u0416\u0415\u041D\u0418\u0415 " + node2.text.slice(0, splitI + 1);
|
|
1607
3449
|
node2.center = true;
|
|
1608
3450
|
}
|
|
@@ -1638,11 +3480,11 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1638
3480
|
} else if (node.type == "externalDoc") {
|
|
1639
3481
|
const nextNode = doc.nodes[i + 1];
|
|
1640
3482
|
if (nextNode && nextNode?.type != "externalDoc" && nextNode?.type != "pageBreak" && nextNode?.type != "sectionBreak") {
|
|
1641
|
-
doc.nodes.splice(i + 1, 0, { type: "pageBreak" });
|
|
3483
|
+
doc.nodes.splice(i + 1, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1642
3484
|
}
|
|
1643
3485
|
const prevNode = doc.nodes[i - 1];
|
|
1644
3486
|
if (prevNode && prevNode?.type != "externalDoc" && prevNode?.type != "pageBreak" && prevNode?.type != "sectionBreak") {
|
|
1645
|
-
doc.nodes.splice(i, 0, { type: "pageBreak" });
|
|
3487
|
+
doc.nodes.splice(i, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1646
3488
|
i++;
|
|
1647
3489
|
}
|
|
1648
3490
|
}
|
|
@@ -1651,7 +3493,7 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1651
3493
|
|
|
1652
3494
|
// src/main.ts
|
|
1653
3495
|
var import_child_process = require("child_process");
|
|
1654
|
-
var
|
|
3496
|
+
var import_promises4 = __toESM(require("fs/promises"));
|
|
1655
3497
|
var import_fs2 = require("fs");
|
|
1656
3498
|
var import_pdf_lib = require("pdf-lib");
|
|
1657
3499
|
|
|
@@ -1662,12 +3504,15 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1662
3504
|
codesAll: 0,
|
|
1663
3505
|
imgsAll: 0,
|
|
1664
3506
|
tablesAll: 0,
|
|
3507
|
+
formulasAll: 0,
|
|
1665
3508
|
codes: 0,
|
|
1666
3509
|
imgs: 0,
|
|
1667
3510
|
tables: 0,
|
|
3511
|
+
formulas: 0,
|
|
1668
3512
|
titles: { l1: 0, l2: 0, l3: 0, l4: 0, l5: 0 }
|
|
1669
3513
|
};
|
|
1670
3514
|
const named = {};
|
|
3515
|
+
const warnAt = (line, message) => logwarn(`${line ? `Line ${doc.mapSourceLine(line)}: ` : ""}${message}`);
|
|
1671
3516
|
const vals = {};
|
|
1672
3517
|
let lastRefI = 0;
|
|
1673
3518
|
let prevNum = -1;
|
|
@@ -1684,11 +3529,13 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1684
3529
|
let codes = counter.codes;
|
|
1685
3530
|
let imgs = counter.imgs;
|
|
1686
3531
|
let tables = counter.tables;
|
|
3532
|
+
let formulas = counter.formulas;
|
|
1687
3533
|
for (let j = i; j < doc.nodes.length && nextNum < 0; j++) {
|
|
1688
3534
|
const n = doc.nodes[j];
|
|
1689
3535
|
if (n.type == "code") nextNum = codes + 1;
|
|
1690
3536
|
if (n.type == "image") nextNum = imgs + 1;
|
|
1691
3537
|
if (n.type == "table") nextNum = tables + 1;
|
|
3538
|
+
if (n.type == "math") nextNum = formulas + 1;
|
|
1692
3539
|
if (n.type == "title" && n.level == 1 && doc.numberingSections) {
|
|
1693
3540
|
const num = getTitleNum(n);
|
|
1694
3541
|
if (num) l1 = num;
|
|
@@ -1696,6 +3543,7 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1696
3543
|
codes = 0;
|
|
1697
3544
|
imgs = 0;
|
|
1698
3545
|
tables = 0;
|
|
3546
|
+
formulas = 0;
|
|
1699
3547
|
}
|
|
1700
3548
|
}
|
|
1701
3549
|
nextPrefix = l1 > 0 && doc.numberingSections ? `${l1}.` : "";
|
|
@@ -1718,13 +3566,14 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1718
3566
|
counter.codes = 0;
|
|
1719
3567
|
counter.imgs = 0;
|
|
1720
3568
|
counter.tables = 0;
|
|
3569
|
+
counter.formulas = 0;
|
|
1721
3570
|
}
|
|
1722
3571
|
prefix = doc.numberingSections ? `${counter.titles.l1}.` : "";
|
|
1723
3572
|
if (appendix) prefix = APPENDIX_NAME[counter.titles.l1 - 1] + ".";
|
|
1724
3573
|
}
|
|
1725
3574
|
}
|
|
1726
3575
|
materializeNode(node);
|
|
1727
|
-
if (node.type == "code" || node.type == "image" || node.type == "table") {
|
|
3576
|
+
if (node.type == "code" || node.type == "image" || node.type == "table" || node.type == "math") {
|
|
1728
3577
|
if (node.type == "code") {
|
|
1729
3578
|
counter.codes++;
|
|
1730
3579
|
counter.codesAll++;
|
|
@@ -1737,18 +3586,27 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1737
3586
|
counter.tables++;
|
|
1738
3587
|
counter.tablesAll++;
|
|
1739
3588
|
}
|
|
3589
|
+
if (node.type == "math") {
|
|
3590
|
+
counter.formulas++;
|
|
3591
|
+
counter.formulasAll++;
|
|
3592
|
+
}
|
|
1740
3593
|
prevNum = nextNum;
|
|
1741
3594
|
prevPrefix = prefix;
|
|
1742
3595
|
nextNum = -1;
|
|
1743
3596
|
}
|
|
1744
3597
|
});
|
|
3598
|
+
for (const value of [doc.header, doc.footer, ...doc.nodes.filter((node) => node.type == "sectionBreak").flatMap((node) => [node.header, node.footer])]) {
|
|
3599
|
+
if (value?.type != "content") continue;
|
|
3600
|
+
value.nodes.forEach(materializeNode);
|
|
3601
|
+
}
|
|
1745
3602
|
const sourcesCount = crystallizeSources();
|
|
1746
|
-
vals["codes"]?.forEach((f) => f(counter.
|
|
1747
|
-
vals["imgs"]?.forEach((f) => f(counter.
|
|
1748
|
-
vals["tables"]?.forEach((f) => f(counter.
|
|
3603
|
+
vals["codes"]?.forEach((f) => f(counter.codesAll));
|
|
3604
|
+
vals["imgs"]?.forEach((f) => f(counter.imgsAll));
|
|
3605
|
+
vals["tables"]?.forEach((f) => f(counter.tablesAll));
|
|
3606
|
+
vals["formulas"]?.forEach((f) => f(counter.formulasAll));
|
|
1749
3607
|
vals["sources"]?.forEach((f) => f(sourcesCount));
|
|
1750
3608
|
Object.entries(named).forEach(([k, v]) => {
|
|
1751
|
-
if (v instanceof Array)
|
|
3609
|
+
if (v instanceof Array) v.forEach((ref) => warnAt(ref.sourceLine, `\u041D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430\u044F \u0441\u0441\u044B\u043B\u043A\u0430 [${k}]`));
|
|
1752
3610
|
});
|
|
1753
3611
|
if (synopsis) {
|
|
1754
3612
|
synopsis.text = [
|
|
@@ -1782,7 +3640,7 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1782
3640
|
function materializeList(node) {
|
|
1783
3641
|
node.items.forEach((item) => {
|
|
1784
3642
|
if (item.type == "list") materializeList(item);
|
|
1785
|
-
else materializeRunes(item.text, node);
|
|
3643
|
+
else materializeRunes(item.text, { ...node, sourceLine: item.sourceLine ?? node.sourceLine });
|
|
1786
3644
|
});
|
|
1787
3645
|
}
|
|
1788
3646
|
function materializeTable(node) {
|
|
@@ -1830,8 +3688,9 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1830
3688
|
rune.text = repeat(node.level, (i2) => counter.titles[`l${i2 + 1}`]).join(".");
|
|
1831
3689
|
return;
|
|
1832
3690
|
}
|
|
1833
|
-
if (type == "code" || type == "image" || type == "table") {
|
|
1834
|
-
let { num, text } = type == "code" ? { num: counter.codes, text: "\u041B\u0438\u0441\u0442\u0438\u043D\u0433" } : type == "image" ? { num: counter.imgs, text: "\u0420\u0438\u0441\u0443\u043D\u043E\u043A" } : type == "table" ? { num: counter.tables, text: "\u0422\u0430\u0431\u043B\u0438\u0446\u0430" } : (() => {
|
|
3691
|
+
if (type == "code" || type == "image" || type == "table" || type == "math") {
|
|
3692
|
+
let { num, text } = type == "code" ? { num: counter.codes, text: "\u041B\u0438\u0441\u0442\u0438\u043D\u0433" } : type == "image" ? { num: counter.imgs, text: "\u0420\u0438\u0441\u0443\u043D\u043E\u043A" } : type == "table" ? { num: counter.tables, text: "\u0422\u0430\u0431\u043B\u0438\u0446\u0430" } : type == "math" ? { num: counter.formulas, text: "" } : (() => {
|
|
3693
|
+
type;
|
|
1835
3694
|
throw new Error("switch default");
|
|
1836
3695
|
})();
|
|
1837
3696
|
num++;
|
|
@@ -1848,9 +3707,10 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1848
3707
|
nextRune.text = toCapitalCase(nextRune.text);
|
|
1849
3708
|
}
|
|
1850
3709
|
text = `${text} ${prefix}${num} \u2013 `;
|
|
3710
|
+
if (type == "math") text = `${prefix}${num}`;
|
|
1851
3711
|
} else text = `${prefix}${num}`;
|
|
1852
3712
|
if (v instanceof Array) v.forEach((fn) => fn.f(num, prefix));
|
|
1853
|
-
else if (v && tag != "#")
|
|
3713
|
+
else if (v && tag != "#") warnAt(node.sourceLine, `id [${tag}] ${type == "code" ? "\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0430" : type == "image" ? "\u0440\u0438\u0441\u0443\u043D\u043A\u0430" : type == "table" ? "\u0442\u0430\u0431\u043B\u0438\u0446\u044B" : type == "math" ? "\u0444\u043E\u0440\u043C\u0443\u043B\u044B" : ""} \u0443\u0436\u0435 \u0437\u0430\u043D\u044F\u0442\u043E \u0447\u0435\u043C-\u0442\u043E \u0434\u0440\u0443\u0433\u0438\u043C`);
|
|
1854
3714
|
named[tag] = { n: num, prefix };
|
|
1855
3715
|
rune.type = "text";
|
|
1856
3716
|
rune.text = text;
|
|
@@ -1870,8 +3730,8 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1870
3730
|
rune.type = "text";
|
|
1871
3731
|
rune.text = n < 0 ? prefix2 : `${prefix2}${applyMath(n)}`;
|
|
1872
3732
|
};
|
|
1873
|
-
if (v) v.push({ f, i: lastRefI
|
|
1874
|
-
else named[tag] = [{ f, i: lastRefI
|
|
3733
|
+
if (v) v.push({ f, i: lastRefI++, sourceLine: node.sourceLine });
|
|
3734
|
+
else named[tag] = [{ f, i: lastRefI++, sourceLine: node.sourceLine }];
|
|
1875
3735
|
} else {
|
|
1876
3736
|
rune.type = "text";
|
|
1877
3737
|
rune.text = `${v.prefix}${applyMath(v.n)}`;
|
|
@@ -1879,21 +3739,22 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1879
3739
|
});
|
|
1880
3740
|
}
|
|
1881
3741
|
function crystallizeSources() {
|
|
1882
|
-
const sourcesList = doc.nodes.find((node) => node.type == "list" && node.
|
|
3742
|
+
const sourcesList = doc.nodes.find((node) => node.type == "list" && node.tags?.includes("sources"));
|
|
1883
3743
|
if (!sourcesList || sourcesList.type != "list") return 0;
|
|
1884
|
-
const items = sourcesList.items.filter((v) => v.type == "listItem").map((v
|
|
3744
|
+
const items = sourcesList.items.filter((v) => v.type == "listItem").map((v, i) => {
|
|
3745
|
+
const item = v.text;
|
|
1885
3746
|
const refI = item[0]?.text ? 0 : 1;
|
|
1886
3747
|
const ref = item[refI];
|
|
1887
3748
|
if (ref?.type != "ref") {
|
|
1888
|
-
return { text: item, ref: "", i: 99999 + i };
|
|
3749
|
+
return { text: item, ref: "", i: 99999 + i, srcI: v.sourceLine };
|
|
1889
3750
|
}
|
|
1890
3751
|
const refs = named[ref.text];
|
|
1891
3752
|
if (refs && !(refs instanceof Array)) {
|
|
1892
|
-
|
|
1893
|
-
return { text: item.slice(refI + 1), ref: "", i: 99999 + i };
|
|
3753
|
+
warnAt(sourcesList.sourceLine, `id [${ref.text}] \u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A\u0430 \u0443\u0436\u0435 \u0437\u0430\u043D\u044F\u0442\u043E \u0447\u0435\u043C-\u0442\u043E \u0434\u0440\u0443\u0433\u0438\u043C`);
|
|
3754
|
+
return { text: item.slice(refI + 1), ref: "", i: 99999 + i, srcI: v.sourceLine };
|
|
1894
3755
|
}
|
|
1895
|
-
const firstOccurrenceI = Math.min(...refs.map((
|
|
1896
|
-
return { text: item.slice(refI + 1), ref: ref.text, i: firstOccurrenceI };
|
|
3756
|
+
const firstOccurrenceI = Math.min(...refs.map((v2) => v2.i));
|
|
3757
|
+
return { text: item.slice(refI + 1), ref: ref.text, i: firstOccurrenceI, srcI: v.sourceLine };
|
|
1897
3758
|
});
|
|
1898
3759
|
items.sort((a, b) => a.i - b.i);
|
|
1899
3760
|
const hasIdAny = items.some((it) => !!it.ref);
|
|
@@ -1903,16 +3764,17 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1903
3764
|
named[key] = { prefix: "", n: i + 1 };
|
|
1904
3765
|
if (refs instanceof Array) {
|
|
1905
3766
|
if (hasIdAny && refs.length <= 1)
|
|
1906
|
-
|
|
3767
|
+
warnAt(item.srcI, `\u0412 \u0442\u0435\u043A\u0441\u0442\u0435 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0442 \u0441\u0441\u044B\u043B\u043A\u0438 \u043D\u0430 \u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A: ${item.ref}`);
|
|
1907
3768
|
refs.forEach((r) => hasIdAny ? r.f(i + 1, "") : r.f(-1, `[${i + 1}]`));
|
|
1908
3769
|
}
|
|
1909
|
-
return { type: "listItem", text: item.text };
|
|
3770
|
+
return { type: "listItem", text: item.text, sourceLine: item.srcI };
|
|
1910
3771
|
});
|
|
1911
3772
|
return sourcesList.items.length;
|
|
1912
3773
|
}
|
|
1913
3774
|
}
|
|
1914
3775
|
function addLazyNumbering(doc) {
|
|
1915
3776
|
doc.nodes.forEach((node) => {
|
|
3777
|
+
if (node.type == "math" && !node.title) node.title = [{ text: "(", type: "text" }, { text: "#", type: "ref" }, { text: ")", type: "text" }];
|
|
1916
3778
|
const runes = node.type == "code" || node.type == "table" ? node.title : node.type == "image" ? node.text : null;
|
|
1917
3779
|
if (!runes) return;
|
|
1918
3780
|
if (runes.find((rune) => rune.type == "ref")) return;
|
|
@@ -1934,6 +3796,8 @@ async function render({
|
|
|
1934
3796
|
disableMacros = false,
|
|
1935
3797
|
useLibreOffice = false,
|
|
1936
3798
|
checkFilesIsInsidePath = "",
|
|
3799
|
+
templateOptions = { allowIncludes: true, allowLoops: true },
|
|
3800
|
+
variables,
|
|
1937
3801
|
logwarn = console.warn,
|
|
1938
3802
|
loginfo = console.info,
|
|
1939
3803
|
logPS = (msg) => console.log(`PS: ${msg}`),
|
|
@@ -1941,23 +3805,23 @@ async function render({
|
|
|
1941
3805
|
signal = null
|
|
1942
3806
|
}) {
|
|
1943
3807
|
signal?.throwIfAborted();
|
|
1944
|
-
const fin =
|
|
3808
|
+
const fin = import_path4.default.resolve(file);
|
|
1945
3809
|
progress(10, "\u041F\u044B\u0442\u0430\u0435\u043C\u0441\u044F \u043F\u043E\u043D\u044F\u0442\u044C, \u0447\u0442\u043E \u0432\u044B \u0442\u0443\u0442 \u043D\u0430\u043F\u0438\u0441\u0430\u043B\u0438...");
|
|
1946
3810
|
loginfo(`[!1] Parsing file ${fin}`);
|
|
1947
|
-
const doc = await parseMD(fin, logwarn);
|
|
3811
|
+
const doc = await parseMD(fin, variables, templateOptions, checkFilesIsInsidePath, signal, logwarn);
|
|
1948
3812
|
signal?.throwIfAborted();
|
|
1949
3813
|
progress(10, "\u041A\u043E\u043B\u0434\u0443\u0435\u043C \u043D\u0430\u0434 \u0441\u0438\u043D\u0442\u0430\u043A\u0441\u0438\u0441\u043E\u043C...");
|
|
1950
3814
|
loginfo("[!2] Processing document");
|
|
1951
3815
|
enrichDoc(doc, logwarn);
|
|
1952
3816
|
const runicDoc = runifyDoc(doc);
|
|
1953
3817
|
alchemist(runicDoc, logwarn);
|
|
1954
|
-
const pout = output &&
|
|
1955
|
-
if (pout) await
|
|
1956
|
-
const p =
|
|
3818
|
+
const pout = output && import_path4.default.parse(import_path4.default.resolve(output));
|
|
3819
|
+
if (pout) await import_promises4.default.mkdir(pout.dir, { recursive: true });
|
|
3820
|
+
const p = import_path4.default.parse(fin);
|
|
1957
3821
|
const fdir = p.dir;
|
|
1958
3822
|
const fname = trimEnd(p.name, ".g");
|
|
1959
|
-
const ftmp =
|
|
1960
|
-
const fout = pout ?
|
|
3823
|
+
const ftmp = import_path4.default.join(fdir, fname + ".tmp.docx");
|
|
3824
|
+
const fout = pout ? import_path4.default.join(pout.dir, pout.name + ".docx") : import_path4.default.join(fdir, fname + ".docx");
|
|
1961
3825
|
if (await checkIfFileIsBlocked(ftmp))
|
|
1962
3826
|
throw new Error(`\u0417\u0430\u043A\u0440\u043E\u0439\u0442\u0435 docx \u0444\u0430\u0439\u043B. Output file is busy or locked: ${ftmp}`);
|
|
1963
3827
|
if (await checkIfFileIsBlocked(fout))
|
|
@@ -1965,7 +3829,7 @@ async function render({
|
|
|
1965
3829
|
signal?.throwIfAborted();
|
|
1966
3830
|
progress(10, phrase_renderDocx());
|
|
1967
3831
|
loginfo("[!3] Serializing to docx");
|
|
1968
|
-
await serializeDocx(runicDoc, ftmp, fdir, assets, checkFilesIsInsidePath);
|
|
3832
|
+
await serializeDocx(runicDoc, ftmp, fdir, assets, checkFilesIsInsidePath, logwarn);
|
|
1969
3833
|
let willRunMacros = !disableMacros && (renderPDF || hasReasonForRunningMacros(runicDoc));
|
|
1970
3834
|
let limitedRender = false;
|
|
1971
3835
|
if (process.platform != "win32" && willRunMacros && !useLibreOffice) {
|
|
@@ -1976,31 +3840,31 @@ async function render({
|
|
|
1976
3840
|
signal?.throwIfAborted();
|
|
1977
3841
|
progress(20, phrase_runMacros());
|
|
1978
3842
|
loginfo("[!4] Starting macros");
|
|
1979
|
-
const tmpfolder =
|
|
1980
|
-
await
|
|
3843
|
+
const tmpfolder = import_path4.default.join(pout ? pout.dir : fdir, ".md2gost_out");
|
|
3844
|
+
await import_promises4.default.rm(tmpfolder, { recursive: true, force: true });
|
|
1981
3845
|
try {
|
|
1982
3846
|
const macroRunner = useLibreOffice ? runUnoScript : runDocxMacro;
|
|
1983
3847
|
const ok = await macroRunner(progress, logPS, logPSError, assets, fdir, ftmp, fout, renderPDF, signal);
|
|
1984
3848
|
if (!ok) {
|
|
1985
|
-
await
|
|
3849
|
+
await import_promises4.default.rename(ftmp, fout);
|
|
1986
3850
|
return { fout, err: "inPS" };
|
|
1987
3851
|
}
|
|
1988
3852
|
} catch (x) {
|
|
1989
3853
|
if (signal?.aborted) {
|
|
1990
|
-
await
|
|
3854
|
+
await import_promises4.default.unlink(ftmp).catch(() => {
|
|
1991
3855
|
});
|
|
1992
3856
|
throw x;
|
|
1993
3857
|
}
|
|
1994
|
-
await
|
|
3858
|
+
await import_promises4.default.rename(ftmp, fout);
|
|
1995
3859
|
return { fout, err: "noPS", errS: x };
|
|
1996
3860
|
}
|
|
1997
3861
|
if ((0, import_fs2.existsSync)(ftmp))
|
|
1998
|
-
await
|
|
3862
|
+
await import_promises4.default.unlink(ftmp);
|
|
1999
3863
|
signal?.throwIfAborted();
|
|
2000
|
-
const errorTxt =
|
|
3864
|
+
const errorTxt = import_path4.default.join(tmpfolder, "error.txt");
|
|
2001
3865
|
if ((0, import_fs2.existsSync)(errorTxt)) {
|
|
2002
|
-
const err = await
|
|
2003
|
-
await
|
|
3866
|
+
const err = await import_promises4.default.readFile(errorTxt);
|
|
3867
|
+
await import_promises4.default.rm(tmpfolder, { recursive: true, force: true });
|
|
2004
3868
|
return { fout, err: "vba", errS: `${err}` };
|
|
2005
3869
|
}
|
|
2006
3870
|
loginfo("[!5] Updating metadata");
|
|
@@ -2012,11 +3876,11 @@ async function render({
|
|
|
2012
3876
|
if (!(0, import_fs2.existsSync)(tmpfolder))
|
|
2013
3877
|
return { fout, err: "pdf" };
|
|
2014
3878
|
try {
|
|
2015
|
-
const files = (await
|
|
2016
|
-
const pdf = pout ?
|
|
3879
|
+
const files = (await import_promises4.default.readdir(tmpfolder)).sort().map((f) => import_path4.default.join(tmpfolder, f));
|
|
3880
|
+
const pdf = pout ? import_path4.default.join(pout.dir, pout.name + ".pdf") : import_path4.default.join(fdir, fname + ".pdf");
|
|
2017
3881
|
await mergePDFs(files, pdf, doc, signal || void 0);
|
|
2018
|
-
await
|
|
2019
|
-
if (removeIntermediateDocx && (0, import_fs2.existsSync)(fout)) await
|
|
3882
|
+
await import_promises4.default.rm(tmpfolder, { recursive: true, force: true });
|
|
3883
|
+
if (removeIntermediateDocx && (0, import_fs2.existsSync)(fout)) await import_promises4.default.unlink(fout);
|
|
2020
3884
|
return { fout: pdf };
|
|
2021
3885
|
} catch (err) {
|
|
2022
3886
|
if (signal?.aborted) throw err;
|
|
@@ -2024,7 +3888,7 @@ async function render({
|
|
|
2024
3888
|
}
|
|
2025
3889
|
}
|
|
2026
3890
|
} else {
|
|
2027
|
-
await
|
|
3891
|
+
await import_promises4.default.rename(ftmp, fout);
|
|
2028
3892
|
}
|
|
2029
3893
|
loginfo(`[!7] Rendered to ${fout}`);
|
|
2030
3894
|
return { fout, err: limitedRender ? "noWin" : void 0 };
|
|
@@ -2058,8 +3922,8 @@ function killProcessTree(child, wordPid) {
|
|
|
2058
3922
|
}
|
|
2059
3923
|
}
|
|
2060
3924
|
function runDocxMacro(progress, log, logError, assets, cwd, fin, fout, renderPDF, signal) {
|
|
2061
|
-
const script =
|
|
2062
|
-
const templateMacro =
|
|
3925
|
+
const script = import_path4.default.join(assets, "run.ps1");
|
|
3926
|
+
const templateMacro = import_path4.default.join(assets, "template.dotm");
|
|
2063
3927
|
return new Promise((res, rej) => {
|
|
2064
3928
|
if (signal?.aborted)
|
|
2065
3929
|
return rej(signal.reason ?? new Error("Aborted before execution started"));
|
|
@@ -2135,7 +3999,7 @@ function getPythonExecutable() {
|
|
|
2135
3999
|
return process.env.UNO_PYTHON_PATH || "python3";
|
|
2136
4000
|
}
|
|
2137
4001
|
function runUnoScript(progress, log, logError, assets, cwd, fin, fout, renderPDF, signal) {
|
|
2138
|
-
const script = process.env.MACRO_ENGINE_PATH ||
|
|
4002
|
+
const script = process.env.MACRO_ENGINE_PATH || import_path4.default.join(assets, "macro_engine.py");
|
|
2139
4003
|
const pythonExec = getPythonExecutable();
|
|
2140
4004
|
return new Promise((res, rej) => {
|
|
2141
4005
|
if (signal?.aborted) {
|
|
@@ -2199,31 +4063,61 @@ function runUnoScript(progress, log, logError, assets, cwd, fin, fout, renderPDF
|
|
|
2199
4063
|
});
|
|
2200
4064
|
}
|
|
2201
4065
|
async function mergePDFs(files, fout, doc, signal) {
|
|
2202
|
-
const
|
|
4066
|
+
const pdfFiles = files.filter((file) => file.toLowerCase().endsWith(".pdf"));
|
|
4067
|
+
let mainPdfIndex = -1;
|
|
4068
|
+
for (let i = 0; i < pdfFiles.length; i++) {
|
|
4069
|
+
if (pdfFiles[i].toLowerCase().endsWith("-main.pdf"))
|
|
4070
|
+
mainPdfIndex = i;
|
|
4071
|
+
}
|
|
4072
|
+
let mergedPdf;
|
|
4073
|
+
if (mainPdfIndex >= 0) {
|
|
4074
|
+
const bytes = await import_promises4.default.readFile(pdfFiles[mainPdfIndex], { signal });
|
|
4075
|
+
mergedPdf = await import_pdf_lib.PDFDocument.load(bytes);
|
|
4076
|
+
signal?.throwIfAborted();
|
|
4077
|
+
let insertionIndex = 0;
|
|
4078
|
+
for (const file of pdfFiles.slice(0, mainPdfIndex)) {
|
|
4079
|
+
const sourceBytes = await import_promises4.default.readFile(file, { signal });
|
|
4080
|
+
const pdf = await import_pdf_lib.PDFDocument.load(sourceBytes);
|
|
4081
|
+
signal?.throwIfAborted();
|
|
4082
|
+
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
|
4083
|
+
for (const page of pages)
|
|
4084
|
+
mergedPdf.insertPage(insertionIndex++, page);
|
|
4085
|
+
signal?.throwIfAborted();
|
|
4086
|
+
}
|
|
4087
|
+
for (const file of pdfFiles.slice(mainPdfIndex + 1)) {
|
|
4088
|
+
const sourceBytes = await import_promises4.default.readFile(file, { signal });
|
|
4089
|
+
const pdf = await import_pdf_lib.PDFDocument.load(sourceBytes);
|
|
4090
|
+
signal?.throwIfAborted();
|
|
4091
|
+
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
|
4092
|
+
pages.forEach((page) => mergedPdf.addPage(page));
|
|
4093
|
+
signal?.throwIfAborted();
|
|
4094
|
+
}
|
|
4095
|
+
} else {
|
|
4096
|
+
mergedPdf = await import_pdf_lib.PDFDocument.create();
|
|
4097
|
+
for (const file of pdfFiles) {
|
|
4098
|
+
const bytes = await import_promises4.default.readFile(file, { signal });
|
|
4099
|
+
const pdf = await import_pdf_lib.PDFDocument.load(bytes);
|
|
4100
|
+
signal?.throwIfAborted();
|
|
4101
|
+
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
|
4102
|
+
pages.forEach((page) => mergedPdf.addPage(page));
|
|
4103
|
+
signal?.throwIfAborted();
|
|
4104
|
+
}
|
|
4105
|
+
}
|
|
2203
4106
|
if (doc?.title) mergedPdf.setTitle(doc.title, { showInWindowTitleBar: true });
|
|
2204
4107
|
if (doc?.author) mergedPdf.setAuthor(doc.author);
|
|
2205
4108
|
if (doc?.ctime) mergedPdf.setCreationDate(doc.ctime);
|
|
2206
4109
|
if (doc?.mtime) mergedPdf.setModificationDate(doc.mtime);
|
|
2207
|
-
for (const file of files) {
|
|
2208
|
-
if (!file.endsWith(".pdf")) continue;
|
|
2209
|
-
const bytes = await import_promises3.default.readFile(file, { signal });
|
|
2210
|
-
const pdf = await import_pdf_lib.PDFDocument.load(bytes);
|
|
2211
|
-
signal?.throwIfAborted();
|
|
2212
|
-
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
|
2213
|
-
pages.forEach((page) => mergedPdf.addPage(page));
|
|
2214
|
-
signal?.throwIfAborted();
|
|
2215
|
-
}
|
|
2216
4110
|
const pdfBytes = await mergedPdf.save();
|
|
2217
|
-
await
|
|
4111
|
+
await import_promises4.default.writeFile(fout, pdfBytes, { signal });
|
|
2218
4112
|
}
|
|
2219
4113
|
async function updateMetadata(docfile, doc) {
|
|
2220
|
-
const content = await
|
|
4114
|
+
const content = await import_promises4.default.readFile(docfile);
|
|
2221
4115
|
const zip = new import_pizzip.default(content);
|
|
2222
4116
|
function replaceTag(xml, tag, value) {
|
|
2223
4117
|
const regex = new RegExp(`(<${tag}\\b[^>]*>)(.*?)(</${tag}>)`, "gs");
|
|
2224
4118
|
return xml.replace(
|
|
2225
4119
|
regex,
|
|
2226
|
-
(m, otag, old, ctag) => `${otag}${
|
|
4120
|
+
(m, otag, old, ctag) => `${otag}${escapeXml2(value)}${ctag}`
|
|
2227
4121
|
);
|
|
2228
4122
|
}
|
|
2229
4123
|
let coreXml = zip.file("docProps/core.xml").asText();
|
|
@@ -2237,8 +4131,8 @@ async function updateMetadata(docfile, doc) {
|
|
|
2237
4131
|
appXml = replaceTag(appXml, "TotalTime", `${doc.etime || randomInt(30, 120)}`);
|
|
2238
4132
|
zip.file("docProps/app.xml", appXml);
|
|
2239
4133
|
const buffer = zip.generate({ type: "nodebuffer" });
|
|
2240
|
-
await
|
|
2241
|
-
function
|
|
4134
|
+
await import_promises4.default.writeFile(docfile, buffer);
|
|
4135
|
+
function escapeXml2(unsafe) {
|
|
2242
4136
|
return unsafe.replaceAll(/[<>&"']/g, (ch) => {
|
|
2243
4137
|
switch (ch) {
|
|
2244
4138
|
case "<":
|
|
@@ -2309,11 +4203,26 @@ async function renderMarkdown(config) {
|
|
|
2309
4203
|
assert(typeof config.useLibreOffice == "boolean" || typeof config.useLibreOffice == "undefined", "useLibreOffice must be a boolean or undefined.");
|
|
2310
4204
|
assert(typeof config.checkFilesIsInsidePath == "string" || config.checkFilesIsInsidePath === false || typeof config.checkFilesIsInsidePath == "undefined", "checkFilesIsInsidePath must be a string, false or undefined.");
|
|
2311
4205
|
assert(typeof config.progress == "function" || typeof config.progress == "undefined", "Progress callback must be a function or undefined.");
|
|
4206
|
+
assert(typeof config.variables == "object" && config.variables !== null && !Array.isArray(config.variables) || typeof config.variables == "undefined", "variables must be an object or undefined.");
|
|
4207
|
+
assert(typeof config.templateOptions == "object" && config.templateOptions !== null && !Array.isArray(config.templateOptions) || typeof config.templateOptions == "undefined", "templateOptions must be an object or undefined.");
|
|
4208
|
+
if (config.templateOptions) {
|
|
4209
|
+
const options = config.templateOptions;
|
|
4210
|
+
const booleanProps = ["allowLoops", "allowIncludes"];
|
|
4211
|
+
const limitProps = ["maxLoopIterations", "maxTotalIterations", "maxDepth", "maxIncludeDepth", "maxIncludes", "maxFileLength", "maxTotalInputLength", "maxGeneratedLength"];
|
|
4212
|
+
const extraProps = getExtraProperties(options, [...booleanProps, ...limitProps]);
|
|
4213
|
+
assert(extraProps.length == 0, `Found unknown properties in config.templateOptions: ${extraProps.join(", ")}`);
|
|
4214
|
+
booleanProps.forEach(
|
|
4215
|
+
(p) => assert(options[p] === void 0 || typeof options[p] == "boolean", `config.templateOptions.${p} must be a boolean or undefined.`)
|
|
4216
|
+
);
|
|
4217
|
+
limitProps.forEach(
|
|
4218
|
+
(p) => assert(options[p] === void 0 || Number.isSafeInteger(options[p]) && options[p] >= 0, `config.templateOptions.${p} must be a non-negative safe integer or undefined.`)
|
|
4219
|
+
);
|
|
4220
|
+
}
|
|
2312
4221
|
assert(config.abortSignal instanceof AbortSignal || typeof config.abortSignal == "undefined", "abortSignal must be an AbortSignal or undefined.");
|
|
2313
4222
|
{
|
|
2314
4223
|
const extraProps = getExtraProperties(
|
|
2315
4224
|
config,
|
|
2316
|
-
["input", "output", "format", "keepIntermediateDocx", "disableMacros", "useLibreOffice", "progress", "logger", "abortSignal", "checkFilesIsInsidePath"]
|
|
4225
|
+
["input", "output", "format", "keepIntermediateDocx", "disableMacros", "useLibreOffice", "progress", "logger", "abortSignal", "checkFilesIsInsidePath", "variables", "templateOptions"]
|
|
2317
4226
|
);
|
|
2318
4227
|
assert(extraProps.length == 0, `Found unknown properties in config: ${extraProps.join(", ")}`);
|
|
2319
4228
|
}
|
|
@@ -2327,20 +4236,25 @@ async function renderMarkdown(config) {
|
|
|
2327
4236
|
(p) => assert(typeof logger[p] == "function", `config.logger.${p} must be a function.`)
|
|
2328
4237
|
);
|
|
2329
4238
|
}
|
|
2330
|
-
const
|
|
4239
|
+
const outputExtension = config.output && import_path5.default.extname(config.output).toLowerCase();
|
|
4240
|
+
const renderPDF = config.format === "pdf" || !config.format && outputExtension === ".pdf";
|
|
2331
4241
|
const removeIntermediateDocx = !config.keepIntermediateDocx;
|
|
2332
4242
|
const disableMacros = !!config.disableMacros;
|
|
2333
4243
|
const useLibreOffice = !!config.useLibreOffice;
|
|
2334
4244
|
const checkFilesIsInsidePath = config.checkFilesIsInsidePath === void 0 ? "" : config.checkFilesIsInsidePath;
|
|
2335
4245
|
assert(!(disableMacros && renderPDF), "Macros must be enabled to render PDF output.");
|
|
2336
|
-
|
|
4246
|
+
if (outputExtension && outputExtension !== ".docx" && outputExtension !== ".pdf")
|
|
4247
|
+
throw new TypeError("Output file extension must be either '.docx' or '.pdf'.");
|
|
4248
|
+
if (outputExtension && outputExtension !== (renderPDF ? ".pdf" : ".docx"))
|
|
4249
|
+
throw new TypeError(`Output file extension must match the selected '${renderPDF ? "pdf" : "docx"}' format.`);
|
|
4250
|
+
const pin = import_path5.default.parse(import_path5.default.resolve(config.input));
|
|
2337
4251
|
const fname = pin.name.endsWith(".g") ? pin.name.slice(0, -2) : pin.name;
|
|
2338
4252
|
let resolvedOutput = config.output;
|
|
2339
4253
|
if (resolvedOutput) {
|
|
2340
|
-
const isDir = resolvedOutput.endsWith("/") || resolvedOutput.endsWith("\\") || !
|
|
4254
|
+
const isDir = resolvedOutput.endsWith("/") || resolvedOutput.endsWith("\\") || !import_path5.default.extname(resolvedOutput);
|
|
2341
4255
|
if (isDir) {
|
|
2342
4256
|
const ext = renderPDF ? ".pdf" : ".docx";
|
|
2343
|
-
resolvedOutput =
|
|
4257
|
+
resolvedOutput = import_path5.default.join(resolvedOutput, fname + ext);
|
|
2344
4258
|
}
|
|
2345
4259
|
}
|
|
2346
4260
|
const warnings = [];
|
|
@@ -2365,6 +4279,8 @@ async function renderMarkdown(config) {
|
|
|
2365
4279
|
disableMacros,
|
|
2366
4280
|
useLibreOffice,
|
|
2367
4281
|
checkFilesIsInsidePath,
|
|
4282
|
+
variables: config.variables,
|
|
4283
|
+
templateOptions: config.templateOptions,
|
|
2368
4284
|
loginfo: logger?.info,
|
|
2369
4285
|
logwarn: (msg) => {
|
|
2370
4286
|
logger?.warn(msg);
|
|
@@ -2380,7 +4296,6 @@ async function renderMarkdown(config) {
|
|
|
2380
4296
|
},
|
|
2381
4297
|
signal: config.abortSignal
|
|
2382
4298
|
});
|
|
2383
|
-
config.progress?.(100, "Done!");
|
|
2384
4299
|
if (useLibreOffice) {
|
|
2385
4300
|
if (err == "noPS") throwMDE2("noPS", `Failed to run the LibreOffice binary (soffice). Ensure LibreOffice is installed and added to your system's PATH environment variable.`);
|
|
2386
4301
|
if (err == "inPS") throwMDE2("inPS", `An unexpected error occurred executing the LibreOffice conversion command. Verify that LibreOffice is properly installed, up to date, and has permission to access the target files.`);
|
|
@@ -2391,10 +4306,11 @@ async function renderMarkdown(config) {
|
|
|
2391
4306
|
if (err == "vba") throwMDE2("vba", `Microsoft Word VBA Macro execution failed: ${errS}`);
|
|
2392
4307
|
if (err == "pdf") throwMDE2("pdf", `Failed to export document to PDF.`);
|
|
2393
4308
|
if (err == "noWin") throwMDE2("noWin", `Platform restriction: Extended formatting macros and PDF rendering are only supported natively on Windows systems.`);
|
|
4309
|
+
config.progress?.(100, "Done!");
|
|
2394
4310
|
let intermediateDocxPath;
|
|
2395
4311
|
if (renderPDF && !removeIntermediateDocx) {
|
|
2396
|
-
const pout = resolvedOutput ?
|
|
2397
|
-
intermediateDocxPath = pout ?
|
|
4312
|
+
const pout = resolvedOutput ? import_path5.default.parse(import_path5.default.resolve(resolvedOutput)) : void 0;
|
|
4313
|
+
intermediateDocxPath = pout ? import_path5.default.join(pout.dir, pout.name + ".docx") : import_path5.default.join(pin.dir, fname + ".docx");
|
|
2398
4314
|
}
|
|
2399
4315
|
return {
|
|
2400
4316
|
filePath: fout,
|
|
@@ -2452,10 +4368,7 @@ var MDRenderError = class _MDRenderError extends Error {
|
|
|
2452
4368
|
}
|
|
2453
4369
|
};
|
|
2454
4370
|
function getAssetsDir() {
|
|
2455
|
-
|
|
2456
|
-
return import_path4.default.resolve(__dirname, "../assets");
|
|
2457
|
-
const metaUrl = new Function("return import.meta.url")();
|
|
2458
|
-
return import_path4.default.resolve(import_path4.default.dirname((0, import_url.fileURLToPath)(metaUrl)), "../assets");
|
|
4371
|
+
return import_path5.default.resolve(__MD2GOST_DIRNAME__, "../assets");
|
|
2459
4372
|
}
|
|
2460
4373
|
function getExtraProperties(configObj, allowedConfigKeys) {
|
|
2461
4374
|
return Object.keys(configObj).filter(
|
|
@@ -2464,6 +4377,7 @@ function getExtraProperties(configObj, allowedConfigKeys) {
|
|
|
2464
4377
|
}
|
|
2465
4378
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2466
4379
|
0 && (module.exports = {
|
|
2467
|
-
MDRenderError
|
|
4380
|
+
MDRenderError,
|
|
4381
|
+
UserInputError
|
|
2468
4382
|
});
|
|
2469
4383
|
//# sourceMappingURL=index.js.map
|