md2gost 1.5.0 → 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 +49 -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 +2332 -415
- package/dist/index.js.map +3 -3
- package/dist/index.mjs +2285 -366
- 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,36 +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
|
-
width: {
|
|
978
|
-
|
|
979
|
-
|
|
2541
|
+
width: {
|
|
2542
|
+
type: "dxa",
|
|
2543
|
+
size: (0, import_docx2.convertMillimetersToTwip)(section.orientation === "landscape" ? 255.8 : 168.8)
|
|
2544
|
+
},
|
|
2545
|
+
rows: node.rows.map((row, rowI) => new import_docx2.TableRow({
|
|
2546
|
+
tableHeader: node.header !== false && rowI == 0,
|
|
980
2547
|
cantSplit: true,
|
|
981
|
-
children: row.map((item, colI) => new
|
|
2548
|
+
children: row.map((item, colI) => new import_docx2.TableCell({
|
|
982
2549
|
children: item.type != "text" ? renderNodeL2(item) : [
|
|
983
|
-
new
|
|
984
|
-
children: renderText(item.text, node.normalFontSize ? void 0 : doc.table.text.size),
|
|
985
|
-
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",
|
|
986
2553
|
indent: { firstLine: 0 },
|
|
987
2554
|
spacing: { after: 0, line: 240 * doc.table.text.line_spacing }
|
|
988
2555
|
})
|
|
@@ -993,20 +2560,20 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
993
2560
|
];
|
|
994
2561
|
case "tableOfContents":
|
|
995
2562
|
return [
|
|
996
|
-
new
|
|
2563
|
+
new import_docx2.TableOfContents("\u041E\u0433\u043B\u0430\u0432\u043B\u0435\u043D\u0438\u0435", {
|
|
997
2564
|
hyperlink: true,
|
|
998
2565
|
headingStyleRange: "1-3"
|
|
999
2566
|
}),
|
|
1000
|
-
new
|
|
2567
|
+
new import_docx2.Paragraph({ children: [new import_docx2.PageBreak()] })
|
|
1001
2568
|
];
|
|
1002
2569
|
case "image":
|
|
1003
|
-
const type = node.src.split(".").at(-1) || "";
|
|
2570
|
+
const type = (node.src.split(".").at(-1) || "").toLowerCase();
|
|
1004
2571
|
if (!["jpg", "png", "gif", "bmp", "svg"].includes(type))
|
|
1005
2572
|
throw new UserInputError(`Unsupported image format: "${type}", file: ${node.src}`);
|
|
1006
2573
|
const img_path = getPath(node.src);
|
|
1007
|
-
if (!
|
|
2574
|
+
if (!fs4.existsSync(img_path))
|
|
1008
2575
|
throw new UserInputError(`File not exist: ${node.src}`);
|
|
1009
|
-
const data =
|
|
2576
|
+
const data = fs4.readFileSync(img_path);
|
|
1010
2577
|
const dimensions = (0, import_image_size.imageSize)(data);
|
|
1011
2578
|
const [MaxW, MaxH] = section.orientation == "landscape" ? [950, 600] : [600, 900];
|
|
1012
2579
|
let [width, height] = [dimensions.width, dimensions.height];
|
|
@@ -1016,19 +2583,19 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1016
2583
|
if (height > MaxH) [width, height] = [width / height * MaxH, MaxH];
|
|
1017
2584
|
if (width > MaxW) [width, height] = [MaxW, height / width * MaxW];
|
|
1018
2585
|
return [
|
|
1019
|
-
new
|
|
2586
|
+
new import_docx2.Paragraph({
|
|
1020
2587
|
alignment: "center",
|
|
1021
2588
|
indent: { firstLine: 0 },
|
|
1022
2589
|
spacing: {
|
|
1023
2590
|
line: 240,
|
|
1024
|
-
lineRule:
|
|
2591
|
+
lineRule: import_docx2.LineRuleType.AT_LEAST,
|
|
1025
2592
|
// For LibreOffice to not shrink image
|
|
1026
2593
|
...!node.text && doc.img.spacing.after !== void 0 ? { after: doc.img.spacing.after * 20 } : {},
|
|
1027
2594
|
...doc.img.spacing.before !== void 0 ? { before: doc.img.spacing.before * 20 } : {}
|
|
1028
2595
|
},
|
|
1029
2596
|
keepNext: !!node.text,
|
|
1030
2597
|
children: [
|
|
1031
|
-
new
|
|
2598
|
+
new import_docx2.ImageRun({
|
|
1032
2599
|
type,
|
|
1033
2600
|
data,
|
|
1034
2601
|
transformation: { width, height },
|
|
@@ -1037,8 +2604,8 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1037
2604
|
]
|
|
1038
2605
|
}),
|
|
1039
2606
|
...node.text ? [
|
|
1040
|
-
new
|
|
1041
|
-
children: renderText(node.text, doc.img.text.size),
|
|
2607
|
+
new import_docx2.Paragraph({
|
|
2608
|
+
children: renderText(node.text, node.sourceLine, doc.img.text.size),
|
|
1042
2609
|
alignment: "center",
|
|
1043
2610
|
indent: { firstLine: 0 },
|
|
1044
2611
|
spacing: {
|
|
@@ -1052,31 +2619,97 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1052
2619
|
const code = doc.code.highlight && renderCodeHighlighting(node.code, node.lang);
|
|
1053
2620
|
return [
|
|
1054
2621
|
...node.title ? [
|
|
1055
|
-
new
|
|
1056
|
-
children: renderText(node.title, doc.code.title.size),
|
|
2622
|
+
new import_docx2.Paragraph({
|
|
2623
|
+
children: renderText(node.title, node.sourceLine, doc.code.title.size),
|
|
1057
2624
|
style: STYLE_code_title
|
|
1058
2625
|
})
|
|
1059
2626
|
] : prevNode?.type == "code" ? [
|
|
1060
|
-
new
|
|
2627
|
+
new import_docx2.Paragraph({ spacing: { line: 20 } })
|
|
1061
2628
|
] : [],
|
|
1062
|
-
...code ? code : node.code.split("\n").map((p, i) => new
|
|
1063
|
-
children: [new
|
|
2629
|
+
...code ? code : node.code.split("\n").map((p, i) => new import_docx2.Paragraph({
|
|
2630
|
+
children: [new import_docx2.TextRun(p)],
|
|
1064
2631
|
style: STYLE_code,
|
|
1065
2632
|
...i == 0 && !node.title && doc.code.spacing.before && prevNode?.type != "code" ? { spacing: { before: doc.code.spacing.before * 20 } } : {}
|
|
1066
2633
|
}))
|
|
1067
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
|
+
];
|
|
1068
2668
|
case "externalDoc":
|
|
1069
2669
|
const doc_path = getPath(node.path);
|
|
1070
|
-
if (!
|
|
2670
|
+
if (!fs4.existsSync(doc_path))
|
|
1071
2671
|
throw new UserInputError(`File not exist: ${node.path}`);
|
|
1072
|
-
const ext =
|
|
2672
|
+
const ext = import_path3.default.extname(doc_path);
|
|
1073
2673
|
if (ext != ".docx" && ext != ".pdf")
|
|
1074
2674
|
throw new UserInputError(`File not .docx or .pdf: ${node.path}`);
|
|
1075
|
-
return new
|
|
2675
|
+
return new import_docx2.Paragraph({
|
|
1076
2676
|
text: `!!(${doc_path})${JSON.stringify(node.dict)}`,
|
|
1077
2677
|
indent: { firstLine: 0 },
|
|
1078
2678
|
alignment: "left"
|
|
1079
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
|
+
];
|
|
1080
2713
|
case "sectionBreak":
|
|
1081
2714
|
return [];
|
|
1082
2715
|
default:
|
|
@@ -1084,12 +2717,20 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1084
2717
|
throw new Error("switch default");
|
|
1085
2718
|
}
|
|
1086
2719
|
}, renderNodeL2 = function(node, prevChild, prevNode) {
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
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
|
+
}
|
|
1090
2729
|
};
|
|
1091
|
-
var renderNode = renderNode2, renderNodeL = renderNodeL2;
|
|
2730
|
+
var renderHeaderFooter = renderHeaderFooter2, renderHeaderFooterTable = renderHeaderFooterTable2, renderNode = renderNode2, renderNodeL = renderNodeL2;
|
|
1092
2731
|
const children = [];
|
|
2732
|
+
const header = renderHeaderFooter2(section.header, false);
|
|
2733
|
+
const footer = renderHeaderFooter2(section.footer, true);
|
|
1093
2734
|
sections.push({
|
|
1094
2735
|
children,
|
|
1095
2736
|
properties: {
|
|
@@ -1104,38 +2745,26 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1104
2745
|
// height: convertMillimetersToTwip(297),
|
|
1105
2746
|
// },
|
|
1106
2747
|
size: {
|
|
1107
|
-
orientation: section.orientation == "landscape" ?
|
|
2748
|
+
orientation: section.orientation == "landscape" ? import_docx2.PageOrientation.LANDSCAPE : import_docx2.PageOrientation.PORTRAIT
|
|
1108
2749
|
},
|
|
1109
2750
|
pageNumbers: !section.pageStart ? {} : {
|
|
1110
2751
|
start: section.pageStart
|
|
1111
2752
|
},
|
|
1112
2753
|
margin: {
|
|
1113
|
-
top: (0,
|
|
1114
|
-
left: (0,
|
|
1115
|
-
right: (0,
|
|
1116
|
-
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)
|
|
1117
2758
|
}
|
|
1118
2759
|
}
|
|
1119
2760
|
},
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
children: [
|
|
1123
|
-
new import_docx.Paragraph({
|
|
1124
|
-
alignment: import_docx.AlignmentType.CENTER,
|
|
1125
|
-
children: [new import_docx.TextRun({
|
|
1126
|
-
children: section.displayPageNum ? [import_docx.PageNumber.CURRENT] : []
|
|
1127
|
-
})],
|
|
1128
|
-
indent: { firstLine: 0 },
|
|
1129
|
-
spacing: { line: 240 }
|
|
1130
|
-
})
|
|
1131
|
-
]
|
|
1132
|
-
})
|
|
1133
|
-
}
|
|
2761
|
+
...header ? { headers: { default: header } } : {},
|
|
2762
|
+
...footer ? { footers: { default: footer } } : {}
|
|
1134
2763
|
});
|
|
1135
2764
|
for (let i = 0; i < section.nodes.length; i++)
|
|
1136
2765
|
children.push(...renderNodeL2(section.nodes[i], children.at(-1), section.nodes[i - 1]));
|
|
1137
2766
|
}
|
|
1138
|
-
const docx = new
|
|
2767
|
+
const docx = new import_docx2.Document({
|
|
1139
2768
|
sections,
|
|
1140
2769
|
externalStyles: genXml_style(assets, doc),
|
|
1141
2770
|
// features: { updateFields: true },
|
|
@@ -1153,7 +2782,7 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1153
2782
|
// creator: doc.author,
|
|
1154
2783
|
// lastModifiedBy: doc.author,
|
|
1155
2784
|
});
|
|
1156
|
-
const buffer = await
|
|
2785
|
+
const buffer = await import_docx2.Packer.toBuffer(docx, void 0, [
|
|
1157
2786
|
{ path: "docProps/app.xml", data: genXml_app({ totalTime: doc.etime }) },
|
|
1158
2787
|
{
|
|
1159
2788
|
path: "docProps/core.xml",
|
|
@@ -1165,21 +2794,25 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1165
2794
|
})
|
|
1166
2795
|
}
|
|
1167
2796
|
]);
|
|
1168
|
-
|
|
1169
|
-
function renderText(text, size) {
|
|
2797
|
+
fs4.writeFileSync(fout, buffer);
|
|
2798
|
+
function renderText(text, sourceLine, size) {
|
|
1170
2799
|
function renderRune(rune, link = false) {
|
|
1171
|
-
if (rune.anchor) return new
|
|
2800
|
+
if (rune.anchor) return new import_docx2.Bookmark({
|
|
1172
2801
|
id: rune.anchor,
|
|
1173
2802
|
children: [renderRune({ ...rune, anchor: void 0 })]
|
|
1174
2803
|
});
|
|
1175
2804
|
if (rune.link) {
|
|
1176
2805
|
const children2 = [renderRune({ ...rune, link: void 0 }, true)];
|
|
1177
|
-
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 });
|
|
1178
2807
|
}
|
|
2808
|
+
if (rune.type == "math") return renderMath(rune.text, false, sourceLine);
|
|
1179
2809
|
let children = null;
|
|
1180
|
-
if (rune.type == "val" && rune.text == "
|
|
2810
|
+
if (rune.type == "val" && rune.text == "page") {
|
|
1181
2811
|
rune.type = "text";
|
|
1182
|
-
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] : [
|
|
1183
2816
|
// Complex Formula Field: { = { NUMPAGES } + 1 }
|
|
1184
2817
|
// Outer field start: "="
|
|
1185
2818
|
new RawXml("w:fldChar", { "w:fldCharType": "begin" }),
|
|
@@ -1200,7 +2833,7 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1200
2833
|
new RawXml("w:fldChar", { "w:fldCharType": "end" })
|
|
1201
2834
|
];
|
|
1202
2835
|
}
|
|
1203
|
-
return new
|
|
2836
|
+
return new import_docx2.TextRun({
|
|
1204
2837
|
...children ? { children } : { text: rune.text },
|
|
1205
2838
|
language: { value: rune.lang == "en" ? "en-US" : "ru-RU" },
|
|
1206
2839
|
...rune.linebreak ? { break: 1 } : {},
|
|
@@ -1261,12 +2894,41 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1261
2894
|
}
|
|
1262
2895
|
}
|
|
1263
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
|
+
}
|
|
1264
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
|
+
};
|
|
1265
2927
|
function addListItemLevel(levels, level, startIndex, itemCount, ordered, alternativeStyle, mark) {
|
|
1266
2928
|
if (levels.find((v) => v.level == level)) return;
|
|
1267
2929
|
levels.sort((a, b) => a.level - b.level);
|
|
1268
2930
|
let indent = level;
|
|
1269
|
-
const format = ordered ?
|
|
2931
|
+
const format = ordered ? import_docx2.LevelFormat.DECIMAL : import_docx2.LevelFormat.BULLET;
|
|
1270
2932
|
for (let i = 1; i < levels.length + 1; i++) {
|
|
1271
2933
|
const prevprevformat = levels[i - 2]?.format;
|
|
1272
2934
|
const prevformat = levels[i - 1]?.format;
|
|
@@ -1292,15 +2954,15 @@ function addListItemLevel(levels, level, startIndex, itemCount, ordered, alterna
|
|
|
1292
2954
|
} : {},
|
|
1293
2955
|
paragraph: {
|
|
1294
2956
|
indent: {
|
|
1295
|
-
left: (0,
|
|
1296
|
-
hanging: (0,
|
|
1297
|
-
...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) } : {}
|
|
1298
2960
|
}
|
|
1299
2961
|
}
|
|
1300
2962
|
}
|
|
1301
2963
|
});
|
|
1302
2964
|
}
|
|
1303
|
-
var RawXml = class extends
|
|
2965
|
+
var RawXml = class extends import_docx2.XmlComponent {
|
|
1304
2966
|
constructor(tag, attributes = {}, text = null) {
|
|
1305
2967
|
super(tag);
|
|
1306
2968
|
if (Object.keys(attributes).length > 0)
|
|
@@ -1310,85 +2972,227 @@ var RawXml = class extends import_docx.XmlComponent {
|
|
|
1310
2972
|
}
|
|
1311
2973
|
};
|
|
1312
2974
|
function renderCodeHighlighting(code, lang) {
|
|
1313
|
-
const
|
|
2975
|
+
const language = lang.trim().toLowerCase();
|
|
2976
|
+
const grammar = import_prismjs.default.languages[language];
|
|
1314
2977
|
if (!grammar) return null;
|
|
1315
2978
|
const themeColors = {
|
|
1316
2979
|
// shared
|
|
1317
2980
|
"plain": "000000",
|
|
1318
2981
|
"comment": "008000",
|
|
2982
|
+
"hashbang": "008000",
|
|
2983
|
+
"shebang": "008000",
|
|
1319
2984
|
"punctuation": "000000",
|
|
1320
2985
|
"operator": "000000",
|
|
2986
|
+
"script": "000000",
|
|
2987
|
+
"script-punctuation": "000000",
|
|
2988
|
+
"expression": "000000",
|
|
2989
|
+
"interpolation": "000000",
|
|
1321
2990
|
// keywords & control flow
|
|
1322
2991
|
"keyword": "0000ff",
|
|
2992
|
+
"control-flow": "af00db",
|
|
1323
2993
|
"boolean": "0000ff",
|
|
1324
|
-
"
|
|
2994
|
+
"null": "0000ff",
|
|
2995
|
+
"builtin": "267f99",
|
|
1325
2996
|
"important": "0000ff",
|
|
2997
|
+
// "instruction": "0000ff",
|
|
2998
|
+
// "directive": "0000ff",
|
|
2999
|
+
"directive-hash": "0000ff",
|
|
3000
|
+
"macro": "0000ff",
|
|
3001
|
+
"macro-name": "0000ff",
|
|
3002
|
+
"rule": "0000ff",
|
|
1326
3003
|
// literals
|
|
1327
|
-
"string":
|
|
1328
|
-
"
|
|
1329
|
-
"
|
|
1330
|
-
"
|
|
3004
|
+
"string": "a31515",
|
|
3005
|
+
"template-string": "a31515",
|
|
3006
|
+
"template-punctuation": "a31515",
|
|
3007
|
+
"string-literal": "a31515",
|
|
1331
3008
|
"char": "a31515",
|
|
3009
|
+
"number": "098658",
|
|
3010
|
+
"datetime": "098658",
|
|
3011
|
+
"constant": "0070c1",
|
|
3012
|
+
"symbol": "0070c1",
|
|
3013
|
+
"scalar": "0000ff",
|
|
3014
|
+
"conversion-option": "0000ff",
|
|
1332
3015
|
// identifiers
|
|
1333
|
-
"class-name":
|
|
1334
|
-
"
|
|
1335
|
-
"
|
|
1336
|
-
"
|
|
1337
|
-
"
|
|
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",
|
|
1338
3037
|
// markup / HTML / XML
|
|
1339
|
-
"tag":
|
|
3038
|
+
"tag": "800000",
|
|
1340
3039
|
"selector": "800000",
|
|
1341
3040
|
"attr-name": "e50000",
|
|
1342
|
-
"attr-value": "
|
|
3041
|
+
"attr-value": "0000ff",
|
|
1343
3042
|
"entity": "800000",
|
|
1344
3043
|
"doctype": "808080",
|
|
3044
|
+
"doctype-tag": "808080",
|
|
3045
|
+
"name": "808080",
|
|
1345
3046
|
"cdata": "808080",
|
|
1346
3047
|
"prolog": "808080",
|
|
3048
|
+
"delimiter": language === "php" ? "800000" : "a31515",
|
|
1347
3049
|
// CSS specific
|
|
1348
3050
|
"atrule": "0000ff",
|
|
1349
3051
|
"url": "0451a5",
|
|
1350
3052
|
// regex
|
|
1351
|
-
"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"
|
|
1352
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
|
+
]);
|
|
1353
3111
|
const tokens = import_prismjs.default.tokenize(code, grammar);
|
|
1354
3112
|
return tokensToParagraphs(tokens);
|
|
1355
3113
|
function tokensToParagraphs(tokens2) {
|
|
1356
3114
|
const paragraphs = [];
|
|
1357
3115
|
let currentRuns = [];
|
|
1358
|
-
|
|
3116
|
+
const plainStyle = { color: themeColors.plain, bold: false, italics: false, strike: false };
|
|
3117
|
+
function addText(text, style) {
|
|
1359
3118
|
const lines = text.split("\n");
|
|
1360
3119
|
lines.forEach((line, index) => {
|
|
1361
3120
|
currentRuns.push(
|
|
1362
|
-
new
|
|
3121
|
+
new import_docx2.TextRun({
|
|
1363
3122
|
text: line,
|
|
1364
|
-
color:
|
|
1365
|
-
bold:
|
|
1366
|
-
italics:
|
|
3123
|
+
color: style.color,
|
|
3124
|
+
bold: style.bold,
|
|
3125
|
+
italics: style.italics,
|
|
3126
|
+
strike: style.strike
|
|
1367
3127
|
})
|
|
1368
3128
|
);
|
|
1369
3129
|
if (index < lines.length - 1) {
|
|
1370
|
-
paragraphs.push(new
|
|
3130
|
+
paragraphs.push(new import_docx2.Paragraph({ children: currentRuns, style: STYLE_code }));
|
|
1371
3131
|
currentRuns = [];
|
|
1372
3132
|
}
|
|
1373
3133
|
});
|
|
1374
3134
|
}
|
|
1375
|
-
function process2(token,
|
|
3135
|
+
function process2(token, parentStyle = plainStyle) {
|
|
1376
3136
|
if (typeof token === "string")
|
|
1377
|
-
addText(token,
|
|
1378
|
-
else
|
|
1379
|
-
token.
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
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
|
+
}
|
|
1384
3157
|
}
|
|
1385
3158
|
tokens2.forEach((t) => process2(t));
|
|
1386
3159
|
if (currentRuns.length > 0) {
|
|
1387
|
-
paragraphs.push(new
|
|
3160
|
+
paragraphs.push(new import_docx2.Paragraph({ children: currentRuns, style: STYLE_code }));
|
|
1388
3161
|
}
|
|
1389
3162
|
return paragraphs;
|
|
1390
3163
|
}
|
|
1391
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
|
+
}
|
|
1392
3196
|
function genXml_app({ totalTime }) {
|
|
1393
3197
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1394
3198
|
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
|
|
@@ -1396,23 +3200,32 @@ function genXml_app({ totalTime }) {
|
|
|
1396
3200
|
</Properties>
|
|
1397
3201
|
`;
|
|
1398
3202
|
}
|
|
3203
|
+
function escapeXml(value) {
|
|
3204
|
+
return value.replaceAll(/[<>&"']/g, (ch) => ({
|
|
3205
|
+
"<": "<",
|
|
3206
|
+
">": ">",
|
|
3207
|
+
"&": "&",
|
|
3208
|
+
'"': """,
|
|
3209
|
+
"'": "'"
|
|
3210
|
+
})[ch]);
|
|
3211
|
+
}
|
|
1399
3212
|
function genXml_core({ title, creator, createdAt, modifiedAt }) {
|
|
1400
3213
|
if (!createdAt) createdAt = /* @__PURE__ */ new Date();
|
|
1401
3214
|
if (!modifiedAt) modifiedAt = /* @__PURE__ */ new Date();
|
|
1402
3215
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1403
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">
|
|
1404
|
-
<dc:title>${title || "Document"}</dc:title>
|
|
1405
|
-
<dc:creator>${creator || "Student"}</dc:creator>
|
|
1406
|
-
<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>
|
|
1407
3220
|
<dcterms:created xsi:type="dcterms:W3CDTF">${createdAt.toISOString()}</dcterms:created>
|
|
1408
3221
|
<dcterms:modified xsi:type="dcterms:W3CDTF">${modifiedAt.toISOString()}</dcterms:modified>
|
|
1409
3222
|
</cp:coreProperties>`;
|
|
1410
3223
|
}
|
|
1411
3224
|
function genXml_style(assets, doc) {
|
|
1412
|
-
let xml =
|
|
3225
|
+
let xml = fs4.readFileSync(import_path3.default.join(assets, "styles.xml"), { encoding: "utf8" });
|
|
1413
3226
|
xml = editStyle(`<w:style w:type="paragraph" w:default="1" w:styleId="a2">`, (xml2) => {
|
|
1414
3227
|
xml2 = replaceTag(xml2, `<w:spacing w:line="${doc.text.line_spacing * 240}" w:lineRule="auto" w:after="${doc.text.spacing.after * 20}"/>`);
|
|
1415
|
-
xml2 = replaceTag(xml2, `<w:ind w:firstLine="${(0,
|
|
3228
|
+
xml2 = replaceTag(xml2, `<w:ind w:firstLine="${(0, import_docx2.convertMillimetersToTwip)(doc.text.indent * 10)}"/>`);
|
|
1416
3229
|
xml2 = replaceTag(xml2, `<w:sz w:val="${doc.text.size * 2}"/>`);
|
|
1417
3230
|
return xml2;
|
|
1418
3231
|
});
|
|
@@ -1434,7 +3247,33 @@ function genXml_style(assets, doc) {
|
|
|
1434
3247
|
xml2 = addrPrStyle(xml2, doc.table.title.style == "bold" ? "<w:b/>" : "<w:i/>");
|
|
1435
3248
|
return xml2;
|
|
1436
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
|
+
}
|
|
1437
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
|
+
}
|
|
1438
3277
|
function replaceTag(xml2, tag) {
|
|
1439
3278
|
const tagName = /<([^\s]+)\b/.exec(tag)?.[1];
|
|
1440
3279
|
if (!tagName) throw err();
|
|
@@ -1474,13 +3313,14 @@ function genXml_style(assets, doc) {
|
|
|
1474
3313
|
|
|
1475
3314
|
// src/enricher.ts
|
|
1476
3315
|
function enrichDoc(doc, logwarn = console.warn) {
|
|
3316
|
+
const warnAt = (line, message) => logwarn(`Line ${doc.mapSourceLine(line)}: ${message}`);
|
|
1477
3317
|
for (let i = 0; i < doc.nodes.length; i++) {
|
|
1478
3318
|
const node = doc.nodes[i];
|
|
1479
|
-
if (node.type == "title" && node.text.toUpperCase().startsWith("\u0422\u0418\u0422\u0423\u041B\u042C\u041D\u0418\u041A")) {
|
|
1480
|
-
doc.nodes.splice(i, 1, { type: "text", text: node.text.toUpperCase() });
|
|
1481
|
-
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 });
|
|
1482
3322
|
i++;
|
|
1483
|
-
} 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") {
|
|
1484
3324
|
node.level = 0;
|
|
1485
3325
|
node.text = node.text.toUpperCase();
|
|
1486
3326
|
const nextNode = doc.nodes[i + 1];
|
|
@@ -1488,81 +3328,83 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1488
3328
|
nextNode.noIndent = true;
|
|
1489
3329
|
nextNode.text = trimEnd(nextNode.text, ".").toUpperCase();
|
|
1490
3330
|
}
|
|
1491
|
-
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 });
|
|
1492
3332
|
while (i + 1 < doc.nodes.length && doc.nodes[i + 1]?.type != "title") i++;
|
|
1493
|
-
doc.nodes.splice(i + 1, 0, { type: "pageBreak" });
|
|
1494
|
-
} 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") {
|
|
1495
3335
|
node.level = 0;
|
|
1496
3336
|
node.text = node.text.toUpperCase();
|
|
1497
|
-
doc.nodes.splice(i + 1, 0, { type: "tableOfContents" });
|
|
1498
|
-
} 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") {
|
|
1499
3339
|
node.center = true;
|
|
1500
3340
|
node.text = node.text.toUpperCase();
|
|
1501
|
-
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");
|
|
1502
3342
|
const list = doc.nodes[i + 1];
|
|
1503
3343
|
if (list?.type != "list") {
|
|
1504
|
-
warn();
|
|
3344
|
+
warn(node.sourceLine);
|
|
1505
3345
|
continue;
|
|
1506
3346
|
}
|
|
1507
|
-
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")];
|
|
1508
3348
|
const align = ["l", "l"];
|
|
1509
3349
|
for (const item of list.items) {
|
|
1510
3350
|
if (item.type != "listItem") {
|
|
1511
|
-
warn();
|
|
3351
|
+
warn(item.sourceLine);
|
|
1512
3352
|
continue;
|
|
1513
3353
|
}
|
|
1514
3354
|
const i2 = item.text.indexOf(":");
|
|
1515
3355
|
if (i2 < 0) {
|
|
1516
|
-
warn();
|
|
3356
|
+
warn(item.sourceLine);
|
|
1517
3357
|
continue;
|
|
1518
3358
|
}
|
|
1519
|
-
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()));
|
|
1520
3360
|
}
|
|
1521
|
-
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." });
|
|
1522
|
-
doc.nodes.splice(i + 2, 0, { type: "table", align, rows, normalFontSize: true, tags: ["definitions_table"] });
|
|
1523
|
-
doc.nodes.splice(i + 3, 0, { type: "pageBreak" });
|
|
1524
|
-
} 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") {
|
|
1525
3365
|
node.center = true;
|
|
1526
3366
|
node.text = node.text.toUpperCase();
|
|
1527
|
-
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");
|
|
1528
3368
|
const list = doc.nodes[i + 1];
|
|
1529
3369
|
if (list?.type != "list") {
|
|
1530
|
-
warn();
|
|
3370
|
+
warn(node.sourceLine);
|
|
1531
3371
|
continue;
|
|
1532
3372
|
}
|
|
1533
3373
|
const items = [];
|
|
1534
3374
|
for (const item of list.items) {
|
|
1535
3375
|
if (item.type != "listItem") {
|
|
1536
|
-
warn();
|
|
3376
|
+
warn(item.sourceLine);
|
|
1537
3377
|
continue;
|
|
1538
3378
|
}
|
|
1539
|
-
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 });
|
|
1540
3380
|
}
|
|
1541
|
-
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." });
|
|
1542
|
-
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 });
|
|
1543
3383
|
doc.nodes.splice(i + 2, 0, ...items);
|
|
1544
|
-
} 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") {
|
|
1545
3385
|
node.center = true;
|
|
1546
3386
|
node.text = node.text.toUpperCase();
|
|
1547
3387
|
for (let j = i + 1; j < doc.nodes.length; j++) {
|
|
1548
3388
|
const node2 = doc.nodes[j];
|
|
1549
3389
|
if (node2?.type == "title") {
|
|
1550
|
-
doc.nodes.splice(j, 0, { type: "pageBreak" });
|
|
3390
|
+
doc.nodes.splice(j, 0, { type: "pageBreak", sourceLine: -1 });
|
|
1551
3391
|
break;
|
|
1552
3392
|
}
|
|
1553
3393
|
}
|
|
1554
|
-
} 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") {
|
|
1555
3395
|
node.center = true;
|
|
1556
3396
|
node.text = node.text.toUpperCase();
|
|
1557
|
-
doc.nodes.splice(i, 0, { type: "pageBreak" });
|
|
3397
|
+
doc.nodes.splice(i, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1558
3398
|
i++;
|
|
1559
|
-
} 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") {
|
|
1560
3400
|
node.center = true;
|
|
1561
3401
|
node.text = node.text.toUpperCase();
|
|
1562
|
-
doc.nodes.splice(i++, 0, { type: "pageBreak" });
|
|
3402
|
+
doc.nodes.splice(i++, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1563
3403
|
const nextNode = doc.nodes[i + 1];
|
|
1564
3404
|
if (nextNode?.type == "list") {
|
|
1565
3405
|
nextNode.alternativeStyle = true;
|
|
3406
|
+
nextNode.tags = nextNode.tags || [];
|
|
3407
|
+
nextNode.tags.push("sources");
|
|
1566
3408
|
i++;
|
|
1567
3409
|
continue;
|
|
1568
3410
|
}
|
|
@@ -1571,35 +3413,38 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1571
3413
|
mark: ".",
|
|
1572
3414
|
startIndex: 1,
|
|
1573
3415
|
alternativeStyle: true,
|
|
3416
|
+
tags: ["sources"],
|
|
1574
3417
|
ordered: true,
|
|
1575
|
-
items: []
|
|
3418
|
+
items: [],
|
|
3419
|
+
sourceLine: node.sourceLine + 1
|
|
1576
3420
|
};
|
|
1577
3421
|
const startI = i + 1;
|
|
1578
3422
|
while (true) {
|
|
1579
3423
|
const textNode = doc.nodes[i + 1];
|
|
1580
3424
|
if (textNode?.type != "text") break;
|
|
1581
3425
|
const items = textNode.text.split(/\n\s*(?=\[[a-zA-Zа-яА-ЯёЁ_\d]+\])/);
|
|
3426
|
+
let sourceLine = textNode.sourceLine;
|
|
1582
3427
|
for (const item of items)
|
|
1583
|
-
list.items.push({ type: "listItem", text: item });
|
|
3428
|
+
list.items.push({ type: "listItem", text: item, sourceLine: sourceLine++ });
|
|
1584
3429
|
i++;
|
|
1585
3430
|
}
|
|
1586
3431
|
if (list.items.length > 0)
|
|
1587
3432
|
doc.nodes.splice(startI, i - startI + 1, list);
|
|
1588
|
-
} 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") {
|
|
1589
3434
|
node.level = 0;
|
|
1590
3435
|
node.center = true;
|
|
1591
3436
|
node.text = node.text.toUpperCase();
|
|
1592
|
-
doc.nodes.splice(i++, 0, { type: "pageBreak" });
|
|
3437
|
+
doc.nodes.splice(i++, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1593
3438
|
for (let j = i + 1; j < doc.nodes.length; j++) {
|
|
1594
3439
|
const node2 = doc.nodes[j];
|
|
1595
3440
|
if (node2.type == "title") {
|
|
1596
|
-
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 });
|
|
1597
3442
|
j++;
|
|
1598
3443
|
node2.level = 1;
|
|
1599
3444
|
const splitI = node2.text.indexOf("]");
|
|
1600
|
-
doc.nodes.splice(j++, 0, { type: "pageBreak" });
|
|
3445
|
+
doc.nodes.splice(j++, 0, { type: "pageBreak", sourceLine: node2.sourceLine });
|
|
1601
3446
|
if (splitI >= 0) {
|
|
1602
|
-
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 });
|
|
1603
3448
|
node2.text = "\u041F\u0420\u0418\u041B\u041E\u0416\u0415\u041D\u0418\u0415 " + node2.text.slice(0, splitI + 1);
|
|
1604
3449
|
node2.center = true;
|
|
1605
3450
|
}
|
|
@@ -1635,11 +3480,11 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1635
3480
|
} else if (node.type == "externalDoc") {
|
|
1636
3481
|
const nextNode = doc.nodes[i + 1];
|
|
1637
3482
|
if (nextNode && nextNode?.type != "externalDoc" && nextNode?.type != "pageBreak" && nextNode?.type != "sectionBreak") {
|
|
1638
|
-
doc.nodes.splice(i + 1, 0, { type: "pageBreak" });
|
|
3483
|
+
doc.nodes.splice(i + 1, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1639
3484
|
}
|
|
1640
3485
|
const prevNode = doc.nodes[i - 1];
|
|
1641
3486
|
if (prevNode && prevNode?.type != "externalDoc" && prevNode?.type != "pageBreak" && prevNode?.type != "sectionBreak") {
|
|
1642
|
-
doc.nodes.splice(i, 0, { type: "pageBreak" });
|
|
3487
|
+
doc.nodes.splice(i, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1643
3488
|
i++;
|
|
1644
3489
|
}
|
|
1645
3490
|
}
|
|
@@ -1648,7 +3493,7 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1648
3493
|
|
|
1649
3494
|
// src/main.ts
|
|
1650
3495
|
var import_child_process = require("child_process");
|
|
1651
|
-
var
|
|
3496
|
+
var import_promises4 = __toESM(require("fs/promises"));
|
|
1652
3497
|
var import_fs2 = require("fs");
|
|
1653
3498
|
var import_pdf_lib = require("pdf-lib");
|
|
1654
3499
|
|
|
@@ -1659,12 +3504,15 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1659
3504
|
codesAll: 0,
|
|
1660
3505
|
imgsAll: 0,
|
|
1661
3506
|
tablesAll: 0,
|
|
3507
|
+
formulasAll: 0,
|
|
1662
3508
|
codes: 0,
|
|
1663
3509
|
imgs: 0,
|
|
1664
3510
|
tables: 0,
|
|
3511
|
+
formulas: 0,
|
|
1665
3512
|
titles: { l1: 0, l2: 0, l3: 0, l4: 0, l5: 0 }
|
|
1666
3513
|
};
|
|
1667
3514
|
const named = {};
|
|
3515
|
+
const warnAt = (line, message) => logwarn(`${line ? `Line ${doc.mapSourceLine(line)}: ` : ""}${message}`);
|
|
1668
3516
|
const vals = {};
|
|
1669
3517
|
let lastRefI = 0;
|
|
1670
3518
|
let prevNum = -1;
|
|
@@ -1681,11 +3529,13 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1681
3529
|
let codes = counter.codes;
|
|
1682
3530
|
let imgs = counter.imgs;
|
|
1683
3531
|
let tables = counter.tables;
|
|
3532
|
+
let formulas = counter.formulas;
|
|
1684
3533
|
for (let j = i; j < doc.nodes.length && nextNum < 0; j++) {
|
|
1685
3534
|
const n = doc.nodes[j];
|
|
1686
3535
|
if (n.type == "code") nextNum = codes + 1;
|
|
1687
3536
|
if (n.type == "image") nextNum = imgs + 1;
|
|
1688
3537
|
if (n.type == "table") nextNum = tables + 1;
|
|
3538
|
+
if (n.type == "math") nextNum = formulas + 1;
|
|
1689
3539
|
if (n.type == "title" && n.level == 1 && doc.numberingSections) {
|
|
1690
3540
|
const num = getTitleNum(n);
|
|
1691
3541
|
if (num) l1 = num;
|
|
@@ -1693,6 +3543,7 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1693
3543
|
codes = 0;
|
|
1694
3544
|
imgs = 0;
|
|
1695
3545
|
tables = 0;
|
|
3546
|
+
formulas = 0;
|
|
1696
3547
|
}
|
|
1697
3548
|
}
|
|
1698
3549
|
nextPrefix = l1 > 0 && doc.numberingSections ? `${l1}.` : "";
|
|
@@ -1715,13 +3566,14 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1715
3566
|
counter.codes = 0;
|
|
1716
3567
|
counter.imgs = 0;
|
|
1717
3568
|
counter.tables = 0;
|
|
3569
|
+
counter.formulas = 0;
|
|
1718
3570
|
}
|
|
1719
3571
|
prefix = doc.numberingSections ? `${counter.titles.l1}.` : "";
|
|
1720
3572
|
if (appendix) prefix = APPENDIX_NAME[counter.titles.l1 - 1] + ".";
|
|
1721
3573
|
}
|
|
1722
3574
|
}
|
|
1723
3575
|
materializeNode(node);
|
|
1724
|
-
if (node.type == "code" || node.type == "image" || node.type == "table") {
|
|
3576
|
+
if (node.type == "code" || node.type == "image" || node.type == "table" || node.type == "math") {
|
|
1725
3577
|
if (node.type == "code") {
|
|
1726
3578
|
counter.codes++;
|
|
1727
3579
|
counter.codesAll++;
|
|
@@ -1734,18 +3586,27 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1734
3586
|
counter.tables++;
|
|
1735
3587
|
counter.tablesAll++;
|
|
1736
3588
|
}
|
|
3589
|
+
if (node.type == "math") {
|
|
3590
|
+
counter.formulas++;
|
|
3591
|
+
counter.formulasAll++;
|
|
3592
|
+
}
|
|
1737
3593
|
prevNum = nextNum;
|
|
1738
3594
|
prevPrefix = prefix;
|
|
1739
3595
|
nextNum = -1;
|
|
1740
3596
|
}
|
|
1741
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
|
+
}
|
|
1742
3602
|
const sourcesCount = crystallizeSources();
|
|
1743
|
-
vals["codes"]?.forEach((f) => f(counter.
|
|
1744
|
-
vals["imgs"]?.forEach((f) => f(counter.
|
|
1745
|
-
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));
|
|
1746
3607
|
vals["sources"]?.forEach((f) => f(sourcesCount));
|
|
1747
3608
|
Object.entries(named).forEach(([k, v]) => {
|
|
1748
|
-
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}]`));
|
|
1749
3610
|
});
|
|
1750
3611
|
if (synopsis) {
|
|
1751
3612
|
synopsis.text = [
|
|
@@ -1779,7 +3640,7 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1779
3640
|
function materializeList(node) {
|
|
1780
3641
|
node.items.forEach((item) => {
|
|
1781
3642
|
if (item.type == "list") materializeList(item);
|
|
1782
|
-
else materializeRunes(item.text, node);
|
|
3643
|
+
else materializeRunes(item.text, { ...node, sourceLine: item.sourceLine ?? node.sourceLine });
|
|
1783
3644
|
});
|
|
1784
3645
|
}
|
|
1785
3646
|
function materializeTable(node) {
|
|
@@ -1827,8 +3688,9 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1827
3688
|
rune.text = repeat(node.level, (i2) => counter.titles[`l${i2 + 1}`]).join(".");
|
|
1828
3689
|
return;
|
|
1829
3690
|
}
|
|
1830
|
-
if (type == "code" || type == "image" || type == "table") {
|
|
1831
|
-
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;
|
|
1832
3694
|
throw new Error("switch default");
|
|
1833
3695
|
})();
|
|
1834
3696
|
num++;
|
|
@@ -1845,9 +3707,10 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1845
3707
|
nextRune.text = toCapitalCase(nextRune.text);
|
|
1846
3708
|
}
|
|
1847
3709
|
text = `${text} ${prefix}${num} \u2013 `;
|
|
3710
|
+
if (type == "math") text = `${prefix}${num}`;
|
|
1848
3711
|
} else text = `${prefix}${num}`;
|
|
1849
3712
|
if (v instanceof Array) v.forEach((fn) => fn.f(num, prefix));
|
|
1850
|
-
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`);
|
|
1851
3714
|
named[tag] = { n: num, prefix };
|
|
1852
3715
|
rune.type = "text";
|
|
1853
3716
|
rune.text = text;
|
|
@@ -1867,8 +3730,8 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1867
3730
|
rune.type = "text";
|
|
1868
3731
|
rune.text = n < 0 ? prefix2 : `${prefix2}${applyMath(n)}`;
|
|
1869
3732
|
};
|
|
1870
|
-
if (v) v.push({ f, i: lastRefI
|
|
1871
|
-
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 }];
|
|
1872
3735
|
} else {
|
|
1873
3736
|
rune.type = "text";
|
|
1874
3737
|
rune.text = `${v.prefix}${applyMath(v.n)}`;
|
|
@@ -1876,21 +3739,22 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1876
3739
|
});
|
|
1877
3740
|
}
|
|
1878
3741
|
function crystallizeSources() {
|
|
1879
|
-
const sourcesList = doc.nodes.find((node) => node.type == "list" && node.
|
|
3742
|
+
const sourcesList = doc.nodes.find((node) => node.type == "list" && node.tags?.includes("sources"));
|
|
1880
3743
|
if (!sourcesList || sourcesList.type != "list") return 0;
|
|
1881
|
-
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;
|
|
1882
3746
|
const refI = item[0]?.text ? 0 : 1;
|
|
1883
3747
|
const ref = item[refI];
|
|
1884
3748
|
if (ref?.type != "ref") {
|
|
1885
|
-
return { text: item, ref: "", i: 99999 + i };
|
|
3749
|
+
return { text: item, ref: "", i: 99999 + i, srcI: v.sourceLine };
|
|
1886
3750
|
}
|
|
1887
3751
|
const refs = named[ref.text];
|
|
1888
3752
|
if (refs && !(refs instanceof Array)) {
|
|
1889
|
-
|
|
1890
|
-
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 };
|
|
1891
3755
|
}
|
|
1892
|
-
const firstOccurrenceI = Math.min(...refs.map((
|
|
1893
|
-
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 };
|
|
1894
3758
|
});
|
|
1895
3759
|
items.sort((a, b) => a.i - b.i);
|
|
1896
3760
|
const hasIdAny = items.some((it) => !!it.ref);
|
|
@@ -1900,16 +3764,17 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1900
3764
|
named[key] = { prefix: "", n: i + 1 };
|
|
1901
3765
|
if (refs instanceof Array) {
|
|
1902
3766
|
if (hasIdAny && refs.length <= 1)
|
|
1903
|
-
|
|
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}`);
|
|
1904
3768
|
refs.forEach((r) => hasIdAny ? r.f(i + 1, "") : r.f(-1, `[${i + 1}]`));
|
|
1905
3769
|
}
|
|
1906
|
-
return { type: "listItem", text: item.text };
|
|
3770
|
+
return { type: "listItem", text: item.text, sourceLine: item.srcI };
|
|
1907
3771
|
});
|
|
1908
3772
|
return sourcesList.items.length;
|
|
1909
3773
|
}
|
|
1910
3774
|
}
|
|
1911
3775
|
function addLazyNumbering(doc) {
|
|
1912
3776
|
doc.nodes.forEach((node) => {
|
|
3777
|
+
if (node.type == "math" && !node.title) node.title = [{ text: "(", type: "text" }, { text: "#", type: "ref" }, { text: ")", type: "text" }];
|
|
1913
3778
|
const runes = node.type == "code" || node.type == "table" ? node.title : node.type == "image" ? node.text : null;
|
|
1914
3779
|
if (!runes) return;
|
|
1915
3780
|
if (runes.find((rune) => rune.type == "ref")) return;
|
|
@@ -1931,6 +3796,8 @@ async function render({
|
|
|
1931
3796
|
disableMacros = false,
|
|
1932
3797
|
useLibreOffice = false,
|
|
1933
3798
|
checkFilesIsInsidePath = "",
|
|
3799
|
+
templateOptions = { allowIncludes: true, allowLoops: true },
|
|
3800
|
+
variables,
|
|
1934
3801
|
logwarn = console.warn,
|
|
1935
3802
|
loginfo = console.info,
|
|
1936
3803
|
logPS = (msg) => console.log(`PS: ${msg}`),
|
|
@@ -1938,23 +3805,23 @@ async function render({
|
|
|
1938
3805
|
signal = null
|
|
1939
3806
|
}) {
|
|
1940
3807
|
signal?.throwIfAborted();
|
|
1941
|
-
const fin =
|
|
3808
|
+
const fin = import_path4.default.resolve(file);
|
|
1942
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...");
|
|
1943
3810
|
loginfo(`[!1] Parsing file ${fin}`);
|
|
1944
|
-
const doc = await parseMD(fin, logwarn);
|
|
3811
|
+
const doc = await parseMD(fin, variables, templateOptions, checkFilesIsInsidePath, signal, logwarn);
|
|
1945
3812
|
signal?.throwIfAborted();
|
|
1946
3813
|
progress(10, "\u041A\u043E\u043B\u0434\u0443\u0435\u043C \u043D\u0430\u0434 \u0441\u0438\u043D\u0442\u0430\u043A\u0441\u0438\u0441\u043E\u043C...");
|
|
1947
3814
|
loginfo("[!2] Processing document");
|
|
1948
3815
|
enrichDoc(doc, logwarn);
|
|
1949
3816
|
const runicDoc = runifyDoc(doc);
|
|
1950
3817
|
alchemist(runicDoc, logwarn);
|
|
1951
|
-
const pout = output &&
|
|
1952
|
-
if (pout) await
|
|
1953
|
-
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);
|
|
1954
3821
|
const fdir = p.dir;
|
|
1955
3822
|
const fname = trimEnd(p.name, ".g");
|
|
1956
|
-
const ftmp =
|
|
1957
|
-
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");
|
|
1958
3825
|
if (await checkIfFileIsBlocked(ftmp))
|
|
1959
3826
|
throw new Error(`\u0417\u0430\u043A\u0440\u043E\u0439\u0442\u0435 docx \u0444\u0430\u0439\u043B. Output file is busy or locked: ${ftmp}`);
|
|
1960
3827
|
if (await checkIfFileIsBlocked(fout))
|
|
@@ -1962,7 +3829,7 @@ async function render({
|
|
|
1962
3829
|
signal?.throwIfAborted();
|
|
1963
3830
|
progress(10, phrase_renderDocx());
|
|
1964
3831
|
loginfo("[!3] Serializing to docx");
|
|
1965
|
-
await serializeDocx(runicDoc, ftmp, fdir, assets, checkFilesIsInsidePath);
|
|
3832
|
+
await serializeDocx(runicDoc, ftmp, fdir, assets, checkFilesIsInsidePath, logwarn);
|
|
1966
3833
|
let willRunMacros = !disableMacros && (renderPDF || hasReasonForRunningMacros(runicDoc));
|
|
1967
3834
|
let limitedRender = false;
|
|
1968
3835
|
if (process.platform != "win32" && willRunMacros && !useLibreOffice) {
|
|
@@ -1973,31 +3840,31 @@ async function render({
|
|
|
1973
3840
|
signal?.throwIfAborted();
|
|
1974
3841
|
progress(20, phrase_runMacros());
|
|
1975
3842
|
loginfo("[!4] Starting macros");
|
|
1976
|
-
const tmpfolder =
|
|
1977
|
-
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 });
|
|
1978
3845
|
try {
|
|
1979
3846
|
const macroRunner = useLibreOffice ? runUnoScript : runDocxMacro;
|
|
1980
3847
|
const ok = await macroRunner(progress, logPS, logPSError, assets, fdir, ftmp, fout, renderPDF, signal);
|
|
1981
3848
|
if (!ok) {
|
|
1982
|
-
await
|
|
3849
|
+
await import_promises4.default.rename(ftmp, fout);
|
|
1983
3850
|
return { fout, err: "inPS" };
|
|
1984
3851
|
}
|
|
1985
3852
|
} catch (x) {
|
|
1986
3853
|
if (signal?.aborted) {
|
|
1987
|
-
await
|
|
3854
|
+
await import_promises4.default.unlink(ftmp).catch(() => {
|
|
1988
3855
|
});
|
|
1989
3856
|
throw x;
|
|
1990
3857
|
}
|
|
1991
|
-
await
|
|
3858
|
+
await import_promises4.default.rename(ftmp, fout);
|
|
1992
3859
|
return { fout, err: "noPS", errS: x };
|
|
1993
3860
|
}
|
|
1994
3861
|
if ((0, import_fs2.existsSync)(ftmp))
|
|
1995
|
-
await
|
|
3862
|
+
await import_promises4.default.unlink(ftmp);
|
|
1996
3863
|
signal?.throwIfAborted();
|
|
1997
|
-
const errorTxt =
|
|
3864
|
+
const errorTxt = import_path4.default.join(tmpfolder, "error.txt");
|
|
1998
3865
|
if ((0, import_fs2.existsSync)(errorTxt)) {
|
|
1999
|
-
const err = await
|
|
2000
|
-
await
|
|
3866
|
+
const err = await import_promises4.default.readFile(errorTxt);
|
|
3867
|
+
await import_promises4.default.rm(tmpfolder, { recursive: true, force: true });
|
|
2001
3868
|
return { fout, err: "vba", errS: `${err}` };
|
|
2002
3869
|
}
|
|
2003
3870
|
loginfo("[!5] Updating metadata");
|
|
@@ -2009,11 +3876,11 @@ async function render({
|
|
|
2009
3876
|
if (!(0, import_fs2.existsSync)(tmpfolder))
|
|
2010
3877
|
return { fout, err: "pdf" };
|
|
2011
3878
|
try {
|
|
2012
|
-
const files = (await
|
|
2013
|
-
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");
|
|
2014
3881
|
await mergePDFs(files, pdf, doc, signal || void 0);
|
|
2015
|
-
await
|
|
2016
|
-
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);
|
|
2017
3884
|
return { fout: pdf };
|
|
2018
3885
|
} catch (err) {
|
|
2019
3886
|
if (signal?.aborted) throw err;
|
|
@@ -2021,7 +3888,7 @@ async function render({
|
|
|
2021
3888
|
}
|
|
2022
3889
|
}
|
|
2023
3890
|
} else {
|
|
2024
|
-
await
|
|
3891
|
+
await import_promises4.default.rename(ftmp, fout);
|
|
2025
3892
|
}
|
|
2026
3893
|
loginfo(`[!7] Rendered to ${fout}`);
|
|
2027
3894
|
return { fout, err: limitedRender ? "noWin" : void 0 };
|
|
@@ -2055,8 +3922,8 @@ function killProcessTree(child, wordPid) {
|
|
|
2055
3922
|
}
|
|
2056
3923
|
}
|
|
2057
3924
|
function runDocxMacro(progress, log, logError, assets, cwd, fin, fout, renderPDF, signal) {
|
|
2058
|
-
const script =
|
|
2059
|
-
const templateMacro =
|
|
3925
|
+
const script = import_path4.default.join(assets, "run.ps1");
|
|
3926
|
+
const templateMacro = import_path4.default.join(assets, "template.dotm");
|
|
2060
3927
|
return new Promise((res, rej) => {
|
|
2061
3928
|
if (signal?.aborted)
|
|
2062
3929
|
return rej(signal.reason ?? new Error("Aborted before execution started"));
|
|
@@ -2132,7 +3999,7 @@ function getPythonExecutable() {
|
|
|
2132
3999
|
return process.env.UNO_PYTHON_PATH || "python3";
|
|
2133
4000
|
}
|
|
2134
4001
|
function runUnoScript(progress, log, logError, assets, cwd, fin, fout, renderPDF, signal) {
|
|
2135
|
-
const script = process.env.MACRO_ENGINE_PATH ||
|
|
4002
|
+
const script = process.env.MACRO_ENGINE_PATH || import_path4.default.join(assets, "macro_engine.py");
|
|
2136
4003
|
const pythonExec = getPythonExecutable();
|
|
2137
4004
|
return new Promise((res, rej) => {
|
|
2138
4005
|
if (signal?.aborted) {
|
|
@@ -2196,31 +4063,61 @@ function runUnoScript(progress, log, logError, assets, cwd, fin, fout, renderPDF
|
|
|
2196
4063
|
});
|
|
2197
4064
|
}
|
|
2198
4065
|
async function mergePDFs(files, fout, doc, signal) {
|
|
2199
|
-
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
|
+
}
|
|
2200
4106
|
if (doc?.title) mergedPdf.setTitle(doc.title, { showInWindowTitleBar: true });
|
|
2201
4107
|
if (doc?.author) mergedPdf.setAuthor(doc.author);
|
|
2202
4108
|
if (doc?.ctime) mergedPdf.setCreationDate(doc.ctime);
|
|
2203
4109
|
if (doc?.mtime) mergedPdf.setModificationDate(doc.mtime);
|
|
2204
|
-
for (const file of files) {
|
|
2205
|
-
if (!file.endsWith(".pdf")) continue;
|
|
2206
|
-
const bytes = await import_promises3.default.readFile(file, { signal });
|
|
2207
|
-
const pdf = await import_pdf_lib.PDFDocument.load(bytes);
|
|
2208
|
-
signal?.throwIfAborted();
|
|
2209
|
-
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
|
2210
|
-
pages.forEach((page) => mergedPdf.addPage(page));
|
|
2211
|
-
signal?.throwIfAborted();
|
|
2212
|
-
}
|
|
2213
4110
|
const pdfBytes = await mergedPdf.save();
|
|
2214
|
-
await
|
|
4111
|
+
await import_promises4.default.writeFile(fout, pdfBytes, { signal });
|
|
2215
4112
|
}
|
|
2216
4113
|
async function updateMetadata(docfile, doc) {
|
|
2217
|
-
const content = await
|
|
4114
|
+
const content = await import_promises4.default.readFile(docfile);
|
|
2218
4115
|
const zip = new import_pizzip.default(content);
|
|
2219
4116
|
function replaceTag(xml, tag, value) {
|
|
2220
4117
|
const regex = new RegExp(`(<${tag}\\b[^>]*>)(.*?)(</${tag}>)`, "gs");
|
|
2221
4118
|
return xml.replace(
|
|
2222
4119
|
regex,
|
|
2223
|
-
(m, otag, old, ctag) => `${otag}${
|
|
4120
|
+
(m, otag, old, ctag) => `${otag}${escapeXml2(value)}${ctag}`
|
|
2224
4121
|
);
|
|
2225
4122
|
}
|
|
2226
4123
|
let coreXml = zip.file("docProps/core.xml").asText();
|
|
@@ -2234,8 +4131,8 @@ async function updateMetadata(docfile, doc) {
|
|
|
2234
4131
|
appXml = replaceTag(appXml, "TotalTime", `${doc.etime || randomInt(30, 120)}`);
|
|
2235
4132
|
zip.file("docProps/app.xml", appXml);
|
|
2236
4133
|
const buffer = zip.generate({ type: "nodebuffer" });
|
|
2237
|
-
await
|
|
2238
|
-
function
|
|
4134
|
+
await import_promises4.default.writeFile(docfile, buffer);
|
|
4135
|
+
function escapeXml2(unsafe) {
|
|
2239
4136
|
return unsafe.replaceAll(/[<>&"']/g, (ch) => {
|
|
2240
4137
|
switch (ch) {
|
|
2241
4138
|
case "<":
|
|
@@ -2306,11 +4203,26 @@ async function renderMarkdown(config) {
|
|
|
2306
4203
|
assert(typeof config.useLibreOffice == "boolean" || typeof config.useLibreOffice == "undefined", "useLibreOffice must be a boolean or undefined.");
|
|
2307
4204
|
assert(typeof config.checkFilesIsInsidePath == "string" || config.checkFilesIsInsidePath === false || typeof config.checkFilesIsInsidePath == "undefined", "checkFilesIsInsidePath must be a string, false or undefined.");
|
|
2308
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
|
+
}
|
|
2309
4221
|
assert(config.abortSignal instanceof AbortSignal || typeof config.abortSignal == "undefined", "abortSignal must be an AbortSignal or undefined.");
|
|
2310
4222
|
{
|
|
2311
4223
|
const extraProps = getExtraProperties(
|
|
2312
4224
|
config,
|
|
2313
|
-
["input", "output", "format", "keepIntermediateDocx", "disableMacros", "useLibreOffice", "progress", "logger", "abortSignal", "checkFilesIsInsidePath"]
|
|
4225
|
+
["input", "output", "format", "keepIntermediateDocx", "disableMacros", "useLibreOffice", "progress", "logger", "abortSignal", "checkFilesIsInsidePath", "variables", "templateOptions"]
|
|
2314
4226
|
);
|
|
2315
4227
|
assert(extraProps.length == 0, `Found unknown properties in config: ${extraProps.join(", ")}`);
|
|
2316
4228
|
}
|
|
@@ -2324,20 +4236,25 @@ async function renderMarkdown(config) {
|
|
|
2324
4236
|
(p) => assert(typeof logger[p] == "function", `config.logger.${p} must be a function.`)
|
|
2325
4237
|
);
|
|
2326
4238
|
}
|
|
2327
|
-
const
|
|
4239
|
+
const outputExtension = config.output && import_path5.default.extname(config.output).toLowerCase();
|
|
4240
|
+
const renderPDF = config.format === "pdf" || !config.format && outputExtension === ".pdf";
|
|
2328
4241
|
const removeIntermediateDocx = !config.keepIntermediateDocx;
|
|
2329
4242
|
const disableMacros = !!config.disableMacros;
|
|
2330
4243
|
const useLibreOffice = !!config.useLibreOffice;
|
|
2331
4244
|
const checkFilesIsInsidePath = config.checkFilesIsInsidePath === void 0 ? "" : config.checkFilesIsInsidePath;
|
|
2332
4245
|
assert(!(disableMacros && renderPDF), "Macros must be enabled to render PDF output.");
|
|
2333
|
-
|
|
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));
|
|
2334
4251
|
const fname = pin.name.endsWith(".g") ? pin.name.slice(0, -2) : pin.name;
|
|
2335
4252
|
let resolvedOutput = config.output;
|
|
2336
4253
|
if (resolvedOutput) {
|
|
2337
|
-
const isDir = resolvedOutput.endsWith("/") || resolvedOutput.endsWith("\\") || !
|
|
4254
|
+
const isDir = resolvedOutput.endsWith("/") || resolvedOutput.endsWith("\\") || !import_path5.default.extname(resolvedOutput);
|
|
2338
4255
|
if (isDir) {
|
|
2339
4256
|
const ext = renderPDF ? ".pdf" : ".docx";
|
|
2340
|
-
resolvedOutput =
|
|
4257
|
+
resolvedOutput = import_path5.default.join(resolvedOutput, fname + ext);
|
|
2341
4258
|
}
|
|
2342
4259
|
}
|
|
2343
4260
|
const warnings = [];
|
|
@@ -2362,6 +4279,8 @@ async function renderMarkdown(config) {
|
|
|
2362
4279
|
disableMacros,
|
|
2363
4280
|
useLibreOffice,
|
|
2364
4281
|
checkFilesIsInsidePath,
|
|
4282
|
+
variables: config.variables,
|
|
4283
|
+
templateOptions: config.templateOptions,
|
|
2365
4284
|
loginfo: logger?.info,
|
|
2366
4285
|
logwarn: (msg) => {
|
|
2367
4286
|
logger?.warn(msg);
|
|
@@ -2377,7 +4296,6 @@ async function renderMarkdown(config) {
|
|
|
2377
4296
|
},
|
|
2378
4297
|
signal: config.abortSignal
|
|
2379
4298
|
});
|
|
2380
|
-
config.progress?.(100, "Done!");
|
|
2381
4299
|
if (useLibreOffice) {
|
|
2382
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.`);
|
|
2383
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.`);
|
|
@@ -2388,10 +4306,11 @@ async function renderMarkdown(config) {
|
|
|
2388
4306
|
if (err == "vba") throwMDE2("vba", `Microsoft Word VBA Macro execution failed: ${errS}`);
|
|
2389
4307
|
if (err == "pdf") throwMDE2("pdf", `Failed to export document to PDF.`);
|
|
2390
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!");
|
|
2391
4310
|
let intermediateDocxPath;
|
|
2392
4311
|
if (renderPDF && !removeIntermediateDocx) {
|
|
2393
|
-
const pout = resolvedOutput ?
|
|
2394
|
-
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");
|
|
2395
4314
|
}
|
|
2396
4315
|
return {
|
|
2397
4316
|
filePath: fout,
|
|
@@ -2449,10 +4368,7 @@ var MDRenderError = class _MDRenderError extends Error {
|
|
|
2449
4368
|
}
|
|
2450
4369
|
};
|
|
2451
4370
|
function getAssetsDir() {
|
|
2452
|
-
|
|
2453
|
-
return import_path4.default.resolve(__dirname, "../assets");
|
|
2454
|
-
const metaUrl = new Function("return import.meta.url")();
|
|
2455
|
-
return import_path4.default.resolve(import_path4.default.dirname((0, import_url.fileURLToPath)(metaUrl)), "../assets");
|
|
4371
|
+
return import_path5.default.resolve(__MD2GOST_DIRNAME__, "../assets");
|
|
2456
4372
|
}
|
|
2457
4373
|
function getExtraProperties(configObj, allowedConfigKeys) {
|
|
2458
4374
|
return Object.keys(configObj).filter(
|
|
@@ -2461,6 +4377,7 @@ function getExtraProperties(configObj, allowedConfigKeys) {
|
|
|
2461
4377
|
}
|
|
2462
4378
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2463
4379
|
0 && (module.exports = {
|
|
2464
|
-
MDRenderError
|
|
4380
|
+
MDRenderError,
|
|
4381
|
+
UserInputError
|
|
2465
4382
|
});
|
|
2466
4383
|
//# sourceMappingURL=index.js.map
|