md2gost 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +35 -9
- package/assets/run.ps1 +15 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +30 -2
- package/dist/index.js +324 -56
- package/dist/index.js.map +3 -3
- package/dist/index.mjs +326 -58
- package/dist/index.mjs.map +3 -3
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -19,6 +19,7 @@ var Doc = class {
|
|
|
19
19
|
ctime;
|
|
20
20
|
mtime;
|
|
21
21
|
hyphenation = false;
|
|
22
|
+
totalPagesOffset = 0;
|
|
22
23
|
headings = {
|
|
23
24
|
h1: { size: 14, spacing: { before: 18, after: 4 }, indent_full: false, uppercase: false },
|
|
24
25
|
h3: { size: 14, spacing: { before: 8, after: 4 }, indent_full: false, uppercase: false },
|
|
@@ -234,7 +235,7 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
234
235
|
const node = { type: "code", lang: header[0], title, code: "" };
|
|
235
236
|
text = "";
|
|
236
237
|
while (lineI < lines.length) {
|
|
237
|
-
|
|
238
|
+
const ln = lines[lineI++];
|
|
238
239
|
if (ln.startsWith("```")) break;
|
|
239
240
|
text += "\n" + ln;
|
|
240
241
|
}
|
|
@@ -242,9 +243,17 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
242
243
|
return node;
|
|
243
244
|
}
|
|
244
245
|
function parseSection(text) {
|
|
246
|
+
text = text.toLowerCase();
|
|
245
247
|
const match = /from\s+(\d+)/.exec(text);
|
|
246
248
|
const num = match ? parseInt(match[1] || "") : NaN;
|
|
247
|
-
|
|
249
|
+
const unpaged = text.includes("unpaged");
|
|
250
|
+
const portrait = text.includes("portrait");
|
|
251
|
+
const landscape = text.includes("landscape");
|
|
252
|
+
return {
|
|
253
|
+
type: "sectionBreak",
|
|
254
|
+
pageStart: unpaged ? -1 : isFinite(num) ? num : null,
|
|
255
|
+
orientation: portrait ? "portrait" : landscape ? "landscape" : null
|
|
256
|
+
};
|
|
248
257
|
}
|
|
249
258
|
class RuleError extends Error {
|
|
250
259
|
}
|
|
@@ -343,7 +352,7 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
343
352
|
(prefix) => textl.startsWith(prefix) && !textl.slice(prefix.length)[0]?.trim()
|
|
344
353
|
);
|
|
345
354
|
const reRule = rulePrefix ? void 0 : reRules.map(({ re, f, n }) => ({ f, n, m: re.exec(textl) })).find(({ m }) => !!m);
|
|
346
|
-
|
|
355
|
+
const applyRule = rulePrefix ? () => rules[rulePrefix](text.slice(rulePrefix.length).trim()) : reRule ? () => reRule.f(reRule.m) : void 0;
|
|
347
356
|
if (applyRule) {
|
|
348
357
|
try {
|
|
349
358
|
applyRule();
|
|
@@ -462,7 +471,7 @@ function parseLine(line) {
|
|
|
462
471
|
if (/^\s*---+\s*$/.test(line)) return { prefix: "---", text: "", level, parts: [] };
|
|
463
472
|
const splited = line.trim().split(/\s/);
|
|
464
473
|
let prefix = splited[0].toLowerCase();
|
|
465
|
-
|
|
474
|
+
const text = splited.slice(1).join(" ");
|
|
466
475
|
let parts = [];
|
|
467
476
|
if (prefix.startsWith("!")) {
|
|
468
477
|
const m_img = /!\[(.*)\]\((.*)\)({(.*)})?/.exec(line);
|
|
@@ -501,7 +510,7 @@ function findDocs(nodes, logwarn = console.warn) {
|
|
|
501
510
|
let dict = {};
|
|
502
511
|
try {
|
|
503
512
|
dict = JSON.parse(`{${m_doc[2]}}`);
|
|
504
|
-
} catch
|
|
513
|
+
} catch {
|
|
505
514
|
logwarn(`Cant parse doc dict: {${m_doc[2].replaceAll("\n", " ")}}`);
|
|
506
515
|
}
|
|
507
516
|
nodes.splice(i, 1, {
|
|
@@ -606,8 +615,8 @@ function runifyDoc(doc) {
|
|
|
606
615
|
}
|
|
607
616
|
var rainbowI = 0;
|
|
608
617
|
function runifyText(text, rainbow = false) {
|
|
609
|
-
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(/(\[[^\]]*\]\([
|
|
610
|
-
const m = /\[([^\]]*)\]\(([
|
|
618
|
+
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) => {
|
|
619
|
+
const m = /\[([^\]]*)\]\(([^)]*)\)/.exec(p);
|
|
611
620
|
if (!m) return { text: p };
|
|
612
621
|
return { text: m[1], link: m[2] };
|
|
613
622
|
}).map((rune) => rune.text.split("\n").map((p, i) => ({
|
|
@@ -642,8 +651,8 @@ function runifyText(text, rainbow = false) {
|
|
|
642
651
|
text: p,
|
|
643
652
|
linebreak: i == 0 && rune.linebreak,
|
|
644
653
|
mono: i % 2 == 1 && i != arr.length - 1
|
|
645
|
-
}))).flat().map((rune) => rune.link ? [rune] : rune.text.split(/(\[!?[a-zA-Zа-яА-ЯёЁ_\d#]+\s*[
|
|
646
|
-
const m = /\[(!?([a-zA-Zа-яА-ЯёЁ_\d]+|#)(\s*[
|
|
654
|
+
}))).flat().map((rune) => rune.link ? [rune] : rune.text.split(/(\[!?[a-zA-Zа-яА-ЯёЁ_\d#]+\s*[+-]?\s*\d*\])/g).map((p, i) => {
|
|
655
|
+
const m = /\[(!?([a-zA-Zа-яА-ЯёЁ_\d]+|#)(\s*[-+]\s*\d+)?)\]/.exec(p);
|
|
647
656
|
const v = m && m[1];
|
|
648
657
|
const isVal = v?.at(0) == "!";
|
|
649
658
|
return {
|
|
@@ -741,7 +750,7 @@ function replaceAmpCodes(text) {
|
|
|
741
750
|
|
|
742
751
|
// src/serializer.ts
|
|
743
752
|
import * as fs3 from "fs";
|
|
744
|
-
import { AlignmentType, Document, Footer, convertMillimetersToTwip, Packer, PageBreak, PageNumber, Paragraph, TableOfContents, TextRun, LevelFormat, Table, TableRow, TableCell, ImageRun, ExternalHyperlink, InternalHyperlink, Bookmark } from "docx";
|
|
753
|
+
import { AlignmentType, Document, Footer, convertMillimetersToTwip, Packer, PageBreak, PageNumber, Paragraph, TableOfContents, TextRun, LevelFormat, Table, TableRow, TableCell, ImageRun, ExternalHyperlink, InternalHyperlink, Bookmark, XmlComponent, LineRuleType, PageOrientation } from "docx";
|
|
745
754
|
import { imageSize } from "image-size";
|
|
746
755
|
import path2 from "path";
|
|
747
756
|
import Prism from "prismjs";
|
|
@@ -771,23 +780,44 @@ import "prismjs/components/prism-yaml";
|
|
|
771
780
|
import "prismjs/components/prism-markdown";
|
|
772
781
|
import "prismjs/components/prism-docker";
|
|
773
782
|
import "prismjs/components/prism-nginx";
|
|
783
|
+
|
|
784
|
+
// src/errors.ts
|
|
785
|
+
var UserInputError = class extends Error {
|
|
786
|
+
constructor(message) {
|
|
787
|
+
super(message);
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
// src/serializer.ts
|
|
774
792
|
var STYLE_list = "afc";
|
|
775
793
|
var STYLE_table_title = "TableCaption";
|
|
776
794
|
var STYLE_code_title = "ListingCaption";
|
|
777
795
|
var STYLE_code = "ListingCode";
|
|
778
|
-
async function serializeDocx(doc, fout, workdir, assets) {
|
|
796
|
+
async function serializeDocx(doc, fout, workdir, assets, allowExternalFiles) {
|
|
779
797
|
const getPath = (fname) => {
|
|
798
|
+
if (process.platform != "win32") fname = fname.replaceAll("\\", "/");
|
|
780
799
|
if (process.platform == "win32" && fname[0] == "/") fname = "." + fname;
|
|
781
|
-
|
|
800
|
+
const targetPath = path2.isAbsolute(fname) ? path2.resolve(fname) : path2.resolve(workdir, fname);
|
|
801
|
+
if (!allowExternalFiles) {
|
|
802
|
+
const relative = path2.relative(workdir, targetPath);
|
|
803
|
+
const isOutside = relative.startsWith("..") || path2.isAbsolute(relative);
|
|
804
|
+
if (isOutside) throw new UserInputError(`Access denied: "${fname}" resolves outside the working directory.`);
|
|
805
|
+
}
|
|
806
|
+
return targetPath;
|
|
782
807
|
};
|
|
783
808
|
const sections = [];
|
|
784
809
|
const numbering = [];
|
|
785
810
|
const docSections = (function splitSections() {
|
|
786
|
-
const sections2 = [{ pageStart: 1, nodes: [] }];
|
|
811
|
+
const sections2 = [{ displayPageNum: true, orientation: "portrait", pageStart: 1, nodes: [] }];
|
|
787
812
|
for (let j = 0; j < doc.nodes.length; j++) {
|
|
788
813
|
const node = doc.nodes[j];
|
|
789
814
|
if (node.type == "sectionBreak")
|
|
790
|
-
sections2.push({
|
|
815
|
+
sections2.push({
|
|
816
|
+
displayPageNum: node.pageStart == null ? sections2.at(-1)?.displayPageNum || true : node.pageStart >= 0,
|
|
817
|
+
orientation: node.orientation || sections2.at(-1)?.orientation || "portrait",
|
|
818
|
+
pageStart: node.pageStart,
|
|
819
|
+
nodes: []
|
|
820
|
+
});
|
|
791
821
|
else
|
|
792
822
|
sections2.at(-1)?.nodes.push(node);
|
|
793
823
|
}
|
|
@@ -905,25 +935,29 @@ async function serializeDocx(doc, fout, workdir, assets) {
|
|
|
905
935
|
case "image":
|
|
906
936
|
const type = node.src.split(".").at(-1) || "";
|
|
907
937
|
if (!["jpg", "png", "gif", "bmp", "svg"].includes(type))
|
|
908
|
-
throw new
|
|
938
|
+
throw new UserInputError(`Unsupported image format: "${type}", file: ${node.src}`);
|
|
909
939
|
const img_path = getPath(node.src);
|
|
910
940
|
if (!fs3.existsSync(img_path))
|
|
911
|
-
throw new
|
|
941
|
+
throw new UserInputError(`File not exist: ${node.src}`);
|
|
912
942
|
const data = fs3.readFileSync(img_path);
|
|
913
943
|
const dimensions = imageSize(data);
|
|
914
|
-
const [MaxW, MaxH] = [600,
|
|
944
|
+
const [MaxW, MaxH] = section.orientation == "landscape" ? [950, 600] : [600, 900];
|
|
915
945
|
let [width, height] = [dimensions.width, dimensions.height];
|
|
916
946
|
if (node.width && node.height) [width, height] = [node.width, node.height];
|
|
917
947
|
if (node.width) [width, height] = [node.width, height / width * node.width];
|
|
918
948
|
if (node.height) [width, height] = [width / height * node.height, node.height];
|
|
919
949
|
if (height > MaxH) [width, height] = [width / height * MaxH, MaxH];
|
|
920
950
|
if (width > MaxW) [width, height] = [MaxW, height / width * MaxW];
|
|
951
|
+
width = MaxW;
|
|
952
|
+
height = MaxH;
|
|
921
953
|
return [
|
|
922
954
|
new Paragraph({
|
|
923
955
|
alignment: "center",
|
|
924
956
|
indent: { firstLine: 0 },
|
|
925
957
|
spacing: {
|
|
926
958
|
line: 240,
|
|
959
|
+
lineRule: LineRuleType.AT_LEAST,
|
|
960
|
+
// For LibreOffice to not shrink image
|
|
927
961
|
...!node.text && doc.img.spacing.after !== void 0 ? { after: doc.img.spacing.after * 20 } : {},
|
|
928
962
|
...doc.img.spacing.before !== void 0 ? { before: doc.img.spacing.before * 20 } : {}
|
|
929
963
|
},
|
|
@@ -969,10 +1003,10 @@ async function serializeDocx(doc, fout, workdir, assets) {
|
|
|
969
1003
|
case "externalDoc":
|
|
970
1004
|
const doc_path = getPath(node.path);
|
|
971
1005
|
if (!fs3.existsSync(doc_path))
|
|
972
|
-
throw new
|
|
1006
|
+
throw new UserInputError(`File not exist: ${node.path}`);
|
|
973
1007
|
const ext = path2.extname(doc_path);
|
|
974
1008
|
if (ext != ".docx" && ext != ".pdf")
|
|
975
|
-
throw new
|
|
1009
|
+
throw new UserInputError(`File not .docx or .pdf: ${node.path}`);
|
|
976
1010
|
return new Paragraph({
|
|
977
1011
|
text: `!!(${doc_path})${JSON.stringify(node.dict)}`,
|
|
978
1012
|
indent: { firstLine: 0 },
|
|
@@ -995,6 +1029,18 @@ async function serializeDocx(doc, fout, workdir, assets) {
|
|
|
995
1029
|
children,
|
|
996
1030
|
properties: {
|
|
997
1031
|
page: {
|
|
1032
|
+
// size: section.orientation == "landscape" ? {
|
|
1033
|
+
// orientation: PageOrientation.LANDSCAPE,
|
|
1034
|
+
// width: convertMillimetersToTwip(297),
|
|
1035
|
+
// height: convertMillimetersToTwip(210),
|
|
1036
|
+
// } : {
|
|
1037
|
+
// orientation: PageOrientation.PORTRAIT,
|
|
1038
|
+
// width: convertMillimetersToTwip(210),
|
|
1039
|
+
// height: convertMillimetersToTwip(297),
|
|
1040
|
+
// },
|
|
1041
|
+
size: {
|
|
1042
|
+
orientation: section.orientation == "landscape" ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
|
|
1043
|
+
},
|
|
998
1044
|
pageNumbers: !section.pageStart ? {} : {
|
|
999
1045
|
start: section.pageStart
|
|
1000
1046
|
},
|
|
@@ -1012,7 +1058,7 @@ async function serializeDocx(doc, fout, workdir, assets) {
|
|
|
1012
1058
|
new Paragraph({
|
|
1013
1059
|
alignment: AlignmentType.CENTER,
|
|
1014
1060
|
children: [new TextRun({
|
|
1015
|
-
children: section.
|
|
1061
|
+
children: section.displayPageNum ? [PageNumber.CURRENT] : []
|
|
1016
1062
|
})],
|
|
1017
1063
|
indent: { firstLine: 0 },
|
|
1018
1064
|
spacing: { line: 240 }
|
|
@@ -1068,7 +1114,26 @@ async function serializeDocx(doc, fout, workdir, assets) {
|
|
|
1068
1114
|
let children = null;
|
|
1069
1115
|
if (rune.type == "val" && rune.text == "pages") {
|
|
1070
1116
|
rune.type = "text";
|
|
1071
|
-
children = [PageNumber.TOTAL_PAGES]
|
|
1117
|
+
children = doc.totalPagesOffset == 0 ? [PageNumber.TOTAL_PAGES] : [
|
|
1118
|
+
// Complex Formula Field: { = { NUMPAGES } + 1 }
|
|
1119
|
+
// Outer field start: "="
|
|
1120
|
+
new RawXml("w:fldChar", { "w:fldCharType": "begin" }),
|
|
1121
|
+
new RawXml("w:instrText", { "xml:space": "preserve" }, " = "),
|
|
1122
|
+
// Inner field start: "NUMPAGES"
|
|
1123
|
+
new RawXml("w:fldChar", { "w:fldCharType": "begin" }),
|
|
1124
|
+
new RawXml("w:instrText", { "xml:space": "preserve" }, " NUMPAGES "),
|
|
1125
|
+
new RawXml("w:fldChar", { "w:fldCharType": "separate" }),
|
|
1126
|
+
new RawXml("w:fldChar", { "w:fldCharType": "end" }),
|
|
1127
|
+
// Operator
|
|
1128
|
+
new RawXml(
|
|
1129
|
+
"w:instrText",
|
|
1130
|
+
{ "xml:space": "preserve" },
|
|
1131
|
+
doc.totalPagesOffset > 0 ? ` + ${doc.totalPagesOffset}` : ` - ${-doc.totalPagesOffset}`
|
|
1132
|
+
),
|
|
1133
|
+
// Outer field close
|
|
1134
|
+
new RawXml("w:fldChar", { "w:fldCharType": "separate" }),
|
|
1135
|
+
new RawXml("w:fldChar", { "w:fldCharType": "end" })
|
|
1136
|
+
];
|
|
1072
1137
|
}
|
|
1073
1138
|
return new TextRun({
|
|
1074
1139
|
...children ? { children } : { text: rune.text },
|
|
@@ -1145,7 +1210,6 @@ function addListItemLevel(levels, level, startIndex, itemCount, ordered, alterna
|
|
|
1145
1210
|
}
|
|
1146
1211
|
const longList = startIndex + itemCount > 10;
|
|
1147
1212
|
const left = alternativeStyle ? 0 : longList ? 20.5 : 17.5;
|
|
1148
|
-
const hanging = longList && !alternativeStyle ? convertMillimetersToTwip(8) : null;
|
|
1149
1213
|
const text = ordered ? alternativeStyle ? `%${level + 1}.` : mark == "." ? `%${level + 1}.` : `%${level + 1})` : (
|
|
1150
1214
|
// mark == "*" ? "\u2022" : "\u2012";
|
|
1151
1215
|
mark == "*" ? "\xB7" : "\u2012"
|
|
@@ -1164,13 +1228,22 @@ function addListItemLevel(levels, level, startIndex, itemCount, ordered, alterna
|
|
|
1164
1228
|
paragraph: {
|
|
1165
1229
|
indent: {
|
|
1166
1230
|
left: convertMillimetersToTwip(left + 5 * indent),
|
|
1167
|
-
|
|
1168
|
-
...
|
|
1231
|
+
hanging: convertMillimetersToTwip(longList && !alternativeStyle ? 8 : 5),
|
|
1232
|
+
...alternativeStyle ? { firstLine: convertMillimetersToTwip(12.5) } : {}
|
|
1169
1233
|
}
|
|
1170
1234
|
}
|
|
1171
1235
|
}
|
|
1172
1236
|
});
|
|
1173
1237
|
}
|
|
1238
|
+
var RawXml = class extends XmlComponent {
|
|
1239
|
+
constructor(tag, attributes = {}, text = null) {
|
|
1240
|
+
super(tag);
|
|
1241
|
+
if (Object.keys(attributes).length > 0)
|
|
1242
|
+
this.root.push({ _attr: attributes });
|
|
1243
|
+
if (text)
|
|
1244
|
+
this.root.push(text);
|
|
1245
|
+
}
|
|
1246
|
+
};
|
|
1174
1247
|
function renderCodeHighlighting(code, lang) {
|
|
1175
1248
|
const grammar = Prism.languages[lang.trim().toLowerCase()];
|
|
1176
1249
|
if (!grammar) return null;
|
|
@@ -1509,7 +1582,7 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1509
1582
|
}
|
|
1510
1583
|
|
|
1511
1584
|
// src/main.ts
|
|
1512
|
-
import { spawn } from "child_process";
|
|
1585
|
+
import { exec, spawn } from "child_process";
|
|
1513
1586
|
import fs4 from "fs/promises";
|
|
1514
1587
|
import { existsSync as existsSync2 } from "fs";
|
|
1515
1588
|
import { PDFDocument } from "pdf-lib";
|
|
@@ -1661,7 +1734,7 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1661
1734
|
}
|
|
1662
1735
|
if (rune.type != "ref") return;
|
|
1663
1736
|
const type = node.type;
|
|
1664
|
-
const m = /^(.*?)([
|
|
1737
|
+
const m = /^(.*?)([-+]\d+)?$/.exec(rune.text.replaceAll(/\s+/g, ""));
|
|
1665
1738
|
const tag = m?.[1] || "";
|
|
1666
1739
|
const math = m?.[2] || "";
|
|
1667
1740
|
const mathSubstarct = math.at(0) == "-";
|
|
@@ -1791,14 +1864,22 @@ async function render({
|
|
|
1791
1864
|
renderPDF = false,
|
|
1792
1865
|
removeIntermediateDocx = false,
|
|
1793
1866
|
disableMacros = false,
|
|
1867
|
+
useLibreOffice = false,
|
|
1868
|
+
allowExternalFiles = false,
|
|
1794
1869
|
logwarn = console.warn,
|
|
1870
|
+
loginfo = console.info,
|
|
1795
1871
|
logPS = (msg) => console.log(`PS: ${msg}`),
|
|
1796
|
-
logPSError = (msg) => console.error(`PS: ${msg}`)
|
|
1872
|
+
logPSError = (msg) => console.error(`PS ERROR: ${msg}`),
|
|
1873
|
+
signal = null
|
|
1797
1874
|
}) {
|
|
1798
|
-
|
|
1875
|
+
signal?.throwIfAborted();
|
|
1799
1876
|
const fin = path3.resolve(file);
|
|
1877
|
+
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...");
|
|
1878
|
+
loginfo(`[!1] Parsing file ${fin}`);
|
|
1800
1879
|
const doc = await parseMD(fin, logwarn);
|
|
1880
|
+
signal?.throwIfAborted();
|
|
1801
1881
|
progress(10, "\u041A\u043E\u043B\u0434\u0443\u0435\u043C \u043D\u0430\u0434 \u0441\u0438\u043D\u0442\u0430\u043A\u0441\u0438\u0441\u043E\u043C...");
|
|
1882
|
+
loginfo("[!2] Processing document");
|
|
1802
1883
|
enrichDoc(doc, logwarn);
|
|
1803
1884
|
const runicDoc = runifyDoc(doc);
|
|
1804
1885
|
alchemist(runicDoc, logwarn);
|
|
@@ -1813,64 +1894,107 @@ async function render({
|
|
|
1813
1894
|
throw new Error(`\u0417\u0430\u043A\u0440\u043E\u0439\u0442\u0435 docx \u0444\u0430\u0439\u043B. Output file is busy or locked: ${ftmp}`);
|
|
1814
1895
|
if (await checkIfFileIsBlocked(fout))
|
|
1815
1896
|
throw new Error(`\u0417\u0430\u043A\u0440\u043E\u0439\u0442\u0435 docx \u0444\u0430\u0439\u043B. Output file is busy or locked: ${fout}`);
|
|
1897
|
+
signal?.throwIfAborted();
|
|
1816
1898
|
progress(10, phrase_renderDocx());
|
|
1817
|
-
|
|
1899
|
+
loginfo("[!3] Serializing to docx");
|
|
1900
|
+
await serializeDocx(runicDoc, ftmp, fdir, assets, allowExternalFiles);
|
|
1818
1901
|
let willRunMacros = !disableMacros && (renderPDF || hasReasonForRunningMacros(runicDoc));
|
|
1819
1902
|
let limitedRender = false;
|
|
1820
|
-
if (process.platform != "win32" && willRunMacros) {
|
|
1903
|
+
if (process.platform != "win32" && willRunMacros && !useLibreOffice) {
|
|
1821
1904
|
limitedRender = true;
|
|
1822
1905
|
willRunMacros = false;
|
|
1823
1906
|
}
|
|
1824
1907
|
if (willRunMacros) {
|
|
1908
|
+
signal?.throwIfAborted();
|
|
1825
1909
|
progress(20, phrase_runMacros());
|
|
1910
|
+
loginfo("[!4] Starting macros");
|
|
1826
1911
|
const tmpfolder = path3.join(pout ? pout.dir : fdir, ".md2gost_out");
|
|
1827
1912
|
await fs4.rm(tmpfolder, { recursive: true, force: true });
|
|
1828
1913
|
try {
|
|
1829
|
-
const
|
|
1914
|
+
const macroRunner = useLibreOffice ? runUnoScript : runDocxMacro;
|
|
1915
|
+
const ok = await macroRunner(progress, logPS, logPSError, assets, fdir, ftmp, fout, renderPDF, signal);
|
|
1830
1916
|
if (!ok) {
|
|
1831
1917
|
await fs4.rename(ftmp, fout);
|
|
1832
1918
|
return { fout, err: "inPS" };
|
|
1833
1919
|
}
|
|
1834
1920
|
} catch (x) {
|
|
1921
|
+
if (signal?.aborted) {
|
|
1922
|
+
await fs4.unlink(ftmp).catch(() => {
|
|
1923
|
+
});
|
|
1924
|
+
throw x;
|
|
1925
|
+
}
|
|
1835
1926
|
await fs4.rename(ftmp, fout);
|
|
1836
1927
|
return { fout, err: "noPS", errS: x };
|
|
1837
1928
|
}
|
|
1838
|
-
|
|
1929
|
+
if (existsSync2(ftmp))
|
|
1930
|
+
await fs4.unlink(ftmp);
|
|
1931
|
+
signal?.throwIfAborted();
|
|
1839
1932
|
const errorTxt = path3.join(tmpfolder, "error.txt");
|
|
1840
1933
|
if (existsSync2(errorTxt)) {
|
|
1841
1934
|
const err = await fs4.readFile(errorTxt);
|
|
1842
1935
|
await fs4.rm(tmpfolder, { recursive: true, force: true });
|
|
1843
1936
|
return { fout, err: "vba", errS: `${err}` };
|
|
1844
1937
|
}
|
|
1938
|
+
loginfo("[!5] Updating metadata");
|
|
1845
1939
|
await updateMetadata(fout, doc);
|
|
1846
1940
|
if (renderPDF) {
|
|
1941
|
+
signal?.throwIfAborted();
|
|
1847
1942
|
progress(10, phrase_combine());
|
|
1943
|
+
loginfo("[!6] Merging pdfs");
|
|
1848
1944
|
if (!existsSync2(tmpfolder))
|
|
1849
1945
|
return { fout, err: "pdf" };
|
|
1850
1946
|
try {
|
|
1851
1947
|
const files = (await fs4.readdir(tmpfolder)).sort().map((f) => path3.join(tmpfolder, f));
|
|
1852
1948
|
const pdf = pout ? path3.join(pout.dir, pout.base) : path3.join(fdir, fname + ".pdf");
|
|
1853
|
-
await mergePDFs(files, pdf, doc);
|
|
1949
|
+
await mergePDFs(files, pdf, doc, signal || void 0);
|
|
1854
1950
|
await fs4.rm(tmpfolder, { recursive: true, force: true });
|
|
1855
1951
|
if (removeIntermediateDocx && existsSync2(fout)) await fs4.unlink(fout);
|
|
1856
1952
|
return { fout: pdf };
|
|
1857
1953
|
} catch (err) {
|
|
1954
|
+
if (signal?.aborted) throw err;
|
|
1858
1955
|
return { fout, err: "pdf", errS: err };
|
|
1859
1956
|
}
|
|
1860
1957
|
}
|
|
1861
1958
|
} else {
|
|
1862
1959
|
await fs4.rename(ftmp, fout);
|
|
1863
1960
|
}
|
|
1961
|
+
loginfo(`[!7] Rendered to ${fout}`);
|
|
1864
1962
|
return { fout, err: limitedRender ? "noWin" : void 0 };
|
|
1865
1963
|
}
|
|
1866
1964
|
function hasReasonForRunningMacros(doc) {
|
|
1867
1965
|
if (DISABLE_MACRO) return false;
|
|
1868
1966
|
return doc.nodes.some((n) => n.type == "code" || n.type == "table");
|
|
1869
1967
|
}
|
|
1870
|
-
function
|
|
1968
|
+
function killProcessTree(child, wordPid) {
|
|
1969
|
+
if (!child.pid) return;
|
|
1970
|
+
if (process.platform === "win32") {
|
|
1971
|
+
if (wordPid) {
|
|
1972
|
+
exec(`taskkill /pid ${wordPid}`, () => {
|
|
1973
|
+
setTimeout(() => {
|
|
1974
|
+
exec(`taskkill /pid ${wordPid} /F`, () => {
|
|
1975
|
+
});
|
|
1976
|
+
setTimeout(() => {
|
|
1977
|
+
killProcessTree(child);
|
|
1978
|
+
}, 2e3);
|
|
1979
|
+
}, 2e3);
|
|
1980
|
+
});
|
|
1981
|
+
} else {
|
|
1982
|
+
exec(`taskkill /pid ${child.pid} /T /F`, () => {
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1985
|
+
} else {
|
|
1986
|
+
try {
|
|
1987
|
+
process.kill(-child.pid, "SIGKILL");
|
|
1988
|
+
} catch {
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
function runDocxMacro(progress, log, logError, assets, cwd, fin, fout, renderPDF, signal) {
|
|
1871
1993
|
const script = path3.join(assets, "run.ps1");
|
|
1872
1994
|
const templateMacro = path3.join(assets, "template.dotm");
|
|
1873
1995
|
return new Promise((res, rej) => {
|
|
1996
|
+
if (signal?.aborted)
|
|
1997
|
+
return rej(signal.reason ?? new Error("Aborted before execution started"));
|
|
1874
1998
|
const child = spawn("powershell", [
|
|
1875
1999
|
"-NoProfile",
|
|
1876
2000
|
"-ExecutionPolicy",
|
|
@@ -1884,10 +2008,28 @@ function runDocxMacro(progress, log, logError, assets, cwd, fin, fout, renderPDF
|
|
|
1884
2008
|
"-MacroTemplate",
|
|
1885
2009
|
templateMacro,
|
|
1886
2010
|
...renderPDF ? ["-RenderPDF"] : []
|
|
1887
|
-
], {
|
|
2011
|
+
], {
|
|
2012
|
+
cwd,
|
|
2013
|
+
detached: process.platform !== "win32"
|
|
2014
|
+
// Enables PGID on POSIX
|
|
2015
|
+
// detached: true,
|
|
2016
|
+
// shell: true,
|
|
2017
|
+
});
|
|
2018
|
+
let wordPid = null;
|
|
2019
|
+
let onAbort;
|
|
2020
|
+
if (signal) {
|
|
2021
|
+
onAbort = () => {
|
|
2022
|
+
logError("Operation aborted. Terminating process tree...");
|
|
2023
|
+
killProcessTree(child, wordPid);
|
|
2024
|
+
rej(signal.reason ?? new Error("Job timed out or aborted"));
|
|
2025
|
+
};
|
|
2026
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2027
|
+
}
|
|
1888
2028
|
child.stdout.on("data", (data) => {
|
|
1889
|
-
|
|
1890
|
-
const
|
|
2029
|
+
const raw = `${data}`;
|
|
2030
|
+
const pidMatch = /\[WordPID:(\d+)\]/.exec(raw);
|
|
2031
|
+
if (pidMatch) wordPid = parseInt(pidMatch[1], 10);
|
|
2032
|
+
const m = /\[\*(\d)\]/.exec(raw);
|
|
1891
2033
|
if (m) progress(12, {
|
|
1892
2034
|
// "1": "Fixing breaks (listings)",
|
|
1893
2035
|
"1": render_fixingBreaks(),
|
|
@@ -1896,31 +2038,115 @@ function runDocxMacro(progress, log, logError, assets, cwd, fin, fout, renderPDF
|
|
|
1896
2038
|
// "3": "Rendering to PDF",
|
|
1897
2039
|
"3": render_renderPDF()
|
|
1898
2040
|
}[m[1]] || "Make some work");
|
|
1899
|
-
log(
|
|
2041
|
+
log(raw);
|
|
2042
|
+
});
|
|
2043
|
+
let ok = true;
|
|
2044
|
+
child.stderr.on("data", (data) => {
|
|
2045
|
+
ok = false;
|
|
2046
|
+
logError(`${data}`);
|
|
2047
|
+
});
|
|
2048
|
+
child.on("close", (code) => {
|
|
2049
|
+
if (signal && onAbort) {
|
|
2050
|
+
signal.removeEventListener("abort", onAbort);
|
|
2051
|
+
}
|
|
2052
|
+
res(ok && code === 0);
|
|
2053
|
+
});
|
|
2054
|
+
child.on("error", (err) => {
|
|
2055
|
+
if (signal && onAbort) {
|
|
2056
|
+
signal.removeEventListener("abort", onAbort);
|
|
2057
|
+
}
|
|
2058
|
+
rej(err);
|
|
2059
|
+
});
|
|
2060
|
+
});
|
|
2061
|
+
}
|
|
2062
|
+
function getPythonExecutable() {
|
|
2063
|
+
if (process.platform === "win32") {
|
|
2064
|
+
const loPython = "C:\\Program Files\\LibreOffice\\program\\python.exe";
|
|
2065
|
+
if (existsSync2(loPython)) return loPython;
|
|
2066
|
+
}
|
|
2067
|
+
return process.env.UNO_PYTHON_PATH || "python3";
|
|
2068
|
+
}
|
|
2069
|
+
function runUnoScript(progress, log, logError, assets, cwd, fin, fout, renderPDF, signal) {
|
|
2070
|
+
const script = process.env.MACRO_ENGINE_PATH || path3.join(assets, "macro_engine.py");
|
|
2071
|
+
const pythonExec = getPythonExecutable();
|
|
2072
|
+
return new Promise((res, rej) => {
|
|
2073
|
+
if (signal?.aborted) {
|
|
2074
|
+
return rej(signal.reason ?? new Error("Aborted before execution started"));
|
|
2075
|
+
}
|
|
2076
|
+
const args = [
|
|
2077
|
+
script,
|
|
2078
|
+
"--input",
|
|
2079
|
+
fin,
|
|
2080
|
+
"--output",
|
|
2081
|
+
fout
|
|
2082
|
+
];
|
|
2083
|
+
if (renderPDF) args.push("--render-pdf");
|
|
2084
|
+
const child = spawn(pythonExec, args, {
|
|
2085
|
+
cwd,
|
|
2086
|
+
detached: process.platform !== "win32"
|
|
2087
|
+
// Enables PGID on POSIX
|
|
2088
|
+
});
|
|
2089
|
+
let onAbort;
|
|
2090
|
+
if (signal) {
|
|
2091
|
+
onAbort = () => {
|
|
2092
|
+
logError("Operation aborted. Terminating process tree...");
|
|
2093
|
+
killProcessTree(child);
|
|
2094
|
+
rej(signal.reason ?? new Error("Job timed out or aborted"));
|
|
2095
|
+
};
|
|
2096
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2097
|
+
}
|
|
2098
|
+
child.stdout.on("data", (data) => {
|
|
2099
|
+
const lines = `${data}`.split("\n");
|
|
2100
|
+
for (const line of lines) {
|
|
2101
|
+
log(line.trimEnd());
|
|
2102
|
+
if (!line.trimEnd()) continue;
|
|
2103
|
+
const m = /\[\*(\d)\]/.exec(line);
|
|
2104
|
+
if (m) {
|
|
2105
|
+
const msg = {
|
|
2106
|
+
"1": render_fixingBreaks(),
|
|
2107
|
+
"2": render_fixingBreaks(),
|
|
2108
|
+
"3": render_renderPDF()
|
|
2109
|
+
}[m[1]] || "Processing document...";
|
|
2110
|
+
progress(12, msg);
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
1900
2113
|
});
|
|
1901
2114
|
let ok = true;
|
|
1902
2115
|
child.stderr.on("data", (data) => {
|
|
1903
2116
|
ok = false;
|
|
1904
2117
|
logError(`${data}`);
|
|
1905
2118
|
});
|
|
1906
|
-
child.on("close", () =>
|
|
1907
|
-
|
|
2119
|
+
child.on("close", (code) => {
|
|
2120
|
+
if (signal && onAbort) {
|
|
2121
|
+
signal.removeEventListener("abort", onAbort);
|
|
2122
|
+
}
|
|
2123
|
+
res(ok && code === 0);
|
|
2124
|
+
});
|
|
2125
|
+
child.on("error", (err) => {
|
|
2126
|
+
if (signal && onAbort) {
|
|
2127
|
+
signal.removeEventListener("abort", onAbort);
|
|
2128
|
+
}
|
|
2129
|
+
rej(err);
|
|
2130
|
+
});
|
|
1908
2131
|
});
|
|
1909
2132
|
}
|
|
1910
|
-
async function mergePDFs(files, fout, doc) {
|
|
2133
|
+
async function mergePDFs(files, fout, doc, signal) {
|
|
1911
2134
|
const mergedPdf = await PDFDocument.create();
|
|
1912
2135
|
if (doc?.title) mergedPdf.setTitle(doc.title, { showInWindowTitleBar: true });
|
|
1913
2136
|
if (doc?.author) mergedPdf.setAuthor(doc.author);
|
|
1914
2137
|
if (doc?.ctime) mergedPdf.setCreationDate(doc.ctime);
|
|
1915
2138
|
if (doc?.mtime) mergedPdf.setModificationDate(doc.mtime);
|
|
1916
2139
|
for (const file of files) {
|
|
1917
|
-
|
|
2140
|
+
if (!file.endsWith(".pdf")) continue;
|
|
2141
|
+
const bytes = await fs4.readFile(file, { signal });
|
|
1918
2142
|
const pdf = await PDFDocument.load(bytes);
|
|
2143
|
+
signal?.throwIfAborted();
|
|
1919
2144
|
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
|
1920
2145
|
pages.forEach((page) => mergedPdf.addPage(page));
|
|
2146
|
+
signal?.throwIfAborted();
|
|
1921
2147
|
}
|
|
1922
2148
|
const pdfBytes = await mergedPdf.save();
|
|
1923
|
-
await fs4.writeFile(fout, pdfBytes);
|
|
2149
|
+
await fs4.writeFile(fout, pdfBytes, { signal });
|
|
1924
2150
|
}
|
|
1925
2151
|
async function updateMetadata(docfile, doc) {
|
|
1926
2152
|
const content = await fs4.readFile(docfile);
|
|
@@ -2012,25 +2238,50 @@ async function renderMarkdown(config) {
|
|
|
2012
2238
|
assert(config.format == "docx" || config.format == "pdf" || typeof config.format == "undefined", "Format must be either 'docx' or 'pdf', or undefined.");
|
|
2013
2239
|
assert(typeof config.keepIntermediateDocx == "boolean" || typeof config.keepIntermediateDocx == "undefined", "keepIntermediateDocx must be a boolean or undefined.");
|
|
2014
2240
|
assert(typeof config.disableMacros == "boolean" || typeof config.disableMacros == "undefined", "disableMacros must be a boolean or undefined.");
|
|
2241
|
+
assert(typeof config.useLibreOffice == "boolean" || typeof config.useLibreOffice == "undefined", "useLibreOffice must be a boolean or undefined.");
|
|
2242
|
+
assert(typeof config.allowExternalFiles == "boolean" || typeof config.allowExternalFiles == "undefined", "allowExternalFiles must be a boolean or undefined.");
|
|
2015
2243
|
assert(typeof config.progress == "function" || typeof config.progress == "undefined", "Progress callback must be a function or undefined.");
|
|
2244
|
+
assert(config.abortSignal instanceof AbortSignal || typeof config.abortSignal == "undefined", "abortSignal must be an AbortSignal or undefined.");
|
|
2016
2245
|
{
|
|
2017
2246
|
const extraProps = getExtraProperties(
|
|
2018
2247
|
config,
|
|
2019
|
-
["input", "output", "format", "keepIntermediateDocx", "disableMacros", "progress"]
|
|
2248
|
+
["input", "output", "format", "keepIntermediateDocx", "disableMacros", "useLibreOffice", "progress", "logger", "abortSignal", "allowExternalFiles"]
|
|
2020
2249
|
);
|
|
2021
2250
|
assert(extraProps.length == 0, `Found unknown properties in config: ${extraProps.join(", ")}`);
|
|
2022
2251
|
}
|
|
2252
|
+
assert(typeof config.logger == "object" && config.logger || typeof config.logger == "undefined", "Logger must be a object or undefined.");
|
|
2253
|
+
const logger = config.logger;
|
|
2254
|
+
if (logger) {
|
|
2255
|
+
const props = ["info", "warn", "error"];
|
|
2256
|
+
const extraProps = getExtraProperties(logger, props);
|
|
2257
|
+
assert(extraProps.length == 0, `Found unknown properties in config.logger: ${extraProps.join(", ")}`);
|
|
2258
|
+
props.forEach(
|
|
2259
|
+
(p) => assert(typeof logger[p] == "function", `config.logger.${p} must be a function.`)
|
|
2260
|
+
);
|
|
2261
|
+
}
|
|
2023
2262
|
const renderPDF = config.format === "pdf" || !config.format && !!config.output?.endsWith(".pdf");
|
|
2024
2263
|
const removeIntermediateDocx = !config.keepIntermediateDocx;
|
|
2025
2264
|
const disableMacros = !!config.disableMacros;
|
|
2265
|
+
const useLibreOffice = !!config.useLibreOffice;
|
|
2266
|
+
const allowExternalFiles = !!config.allowExternalFiles;
|
|
2026
2267
|
assert(!(disableMacros && renderPDF), "Macros must be enabled to render PDF output.");
|
|
2268
|
+
const pin = path4.parse(path4.resolve(config.input));
|
|
2269
|
+
const fname = pin.name.endsWith(".g") ? pin.name.slice(0, -2) : pin.name;
|
|
2270
|
+
let resolvedOutput = config.output;
|
|
2271
|
+
if (resolvedOutput) {
|
|
2272
|
+
const isDir = resolvedOutput.endsWith("/") || resolvedOutput.endsWith("\\") || !path4.extname(resolvedOutput);
|
|
2273
|
+
if (isDir) {
|
|
2274
|
+
const ext = renderPDF ? ".pdf" : ".docx";
|
|
2275
|
+
resolvedOutput = path4.join(resolvedOutput, fname + ext);
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2027
2278
|
const warnings = [];
|
|
2028
2279
|
const logPS = [];
|
|
2029
2280
|
let totalPercent = 0;
|
|
2030
|
-
const handleProgress =
|
|
2281
|
+
const handleProgress = (increment, message) => {
|
|
2031
2282
|
totalPercent = Math.min(totalPercent + increment, 99);
|
|
2032
|
-
config.progress(totalPercent, message);
|
|
2033
|
-
}
|
|
2283
|
+
config.progress?.(totalPercent, message);
|
|
2284
|
+
};
|
|
2034
2285
|
try {
|
|
2035
2286
|
let throwMDE2 = function(code, msg) {
|
|
2036
2287
|
throw new MDRenderError(code, msg, fout, warnings, logPS, errS ? { cause: errS } : void 0);
|
|
@@ -2040,26 +2291,41 @@ async function renderMarkdown(config) {
|
|
|
2040
2291
|
progress: handleProgress,
|
|
2041
2292
|
assets: getAssetsDir(),
|
|
2042
2293
|
file: config.input,
|
|
2043
|
-
output:
|
|
2294
|
+
output: resolvedOutput,
|
|
2044
2295
|
renderPDF,
|
|
2045
2296
|
removeIntermediateDocx,
|
|
2046
2297
|
disableMacros,
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2298
|
+
useLibreOffice,
|
|
2299
|
+
allowExternalFiles,
|
|
2300
|
+
loginfo: logger?.info,
|
|
2301
|
+
logwarn: (msg) => {
|
|
2302
|
+
logger?.warn(msg);
|
|
2303
|
+
warnings.push(msg);
|
|
2304
|
+
},
|
|
2305
|
+
logPS: (msg) => {
|
|
2306
|
+
logger?.info(msg);
|
|
2307
|
+
logPS.push(msg);
|
|
2308
|
+
},
|
|
2309
|
+
logPSError: (msg) => {
|
|
2310
|
+
logger?.error(msg);
|
|
2311
|
+
logPS.push(msg);
|
|
2312
|
+
},
|
|
2313
|
+
signal: config.abortSignal
|
|
2050
2314
|
});
|
|
2051
2315
|
config.progress?.(100, "Done!");
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2316
|
+
if (useLibreOffice) {
|
|
2317
|
+
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.`);
|
|
2318
|
+
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.`);
|
|
2319
|
+
} else {
|
|
2320
|
+
if (err == "noPS") throwMDE2("noPS", `Failed to initialize PowerShell. Ensure PowerShell is installed and explicitly added to your system's PATH environment variable.`);
|
|
2321
|
+
if (err == "inPS") throwMDE2("inPS", `An unexpected error occurred executing commands inside PowerShell. Verify that Microsoft Word is properly installed and licensed.`);
|
|
2322
|
+
}
|
|
2055
2323
|
if (err == "vba") throwMDE2("vba", `Microsoft Word VBA Macro execution failed: ${errS}`);
|
|
2056
2324
|
if (err == "pdf") throwMDE2("pdf", `Failed to export document to PDF.`);
|
|
2057
2325
|
if (err == "noWin") throwMDE2("noWin", `Platform restriction: Extended formatting macros and PDF rendering are only supported natively on Windows systems.`);
|
|
2058
2326
|
let intermediateDocxPath;
|
|
2059
|
-
if (renderPDF) {
|
|
2060
|
-
const
|
|
2061
|
-
const fname = pin.name.endsWith(".g") ? pin.name.slice(0, -2) : pin.name;
|
|
2062
|
-
const pout = config.output ? path4.parse(path4.resolve(config.output)) : void 0;
|
|
2327
|
+
if (renderPDF && !removeIntermediateDocx) {
|
|
2328
|
+
const pout = resolvedOutput ? path4.parse(path4.resolve(resolvedOutput)) : void 0;
|
|
2063
2329
|
intermediateDocxPath = pout ? path4.join(pout.dir, pout.name + ".docx") : path4.join(pin.dir, fname + ".docx");
|
|
2064
2330
|
}
|
|
2065
2331
|
return {
|
|
@@ -2069,6 +2335,7 @@ async function renderMarkdown(config) {
|
|
|
2069
2335
|
};
|
|
2070
2336
|
} catch (x) {
|
|
2071
2337
|
if (x instanceof MDRenderError) throw x;
|
|
2338
|
+
if (x instanceof UserInputError) throw new MDRenderError("userInput", x.message, null, warnings, logPS, { cause: x });
|
|
2072
2339
|
const message = x instanceof Error ? x.message : String(x);
|
|
2073
2340
|
throw new MDRenderError("unknown", `An unhandled internal error occurred during rendering: ${message}`, null, warnings, logPS, { cause: x });
|
|
2074
2341
|
}
|
|
@@ -2081,6 +2348,7 @@ var MDRenderError = class _MDRenderError extends Error {
|
|
|
2081
2348
|
/**
|
|
2082
2349
|
* Code indicating the underlying cause of the failure:
|
|
2083
2350
|
* - `unknown`: An unhandled or unexpected internal runtime error.
|
|
2351
|
+
* - `userInput`: Invalid user input or Markdown document structure.
|
|
2084
2352
|
* - `noPS`: PowerShell could not be started or found in the system PATH.
|
|
2085
2353
|
* - `inPS`: A failure occurred inside PowerShell; MS Word may be missing or corrupt.
|
|
2086
2354
|
* - `vba`: An error occurred during the execution of internal Word VBA macros.
|