slovo/scripts/render-doc-pdfs.js
2026-05-22 08:38:43 +02:00

160 lines
3.4 KiB
JavaScript
Executable File

#!/usr/bin/env node
const fs = require("fs");
const path = require("path");
const { execFileSync } = require("child_process");
const scriptDir = __dirname;
const repoRoot = path.resolve(scriptDir, "..");
const docsDir = path.join(repoRoot, "docs");
const homeDir = process.env.HOME || "";
function firstLine(text) {
return (
text
.split(/\r?\n/)
.map((line) => line.trim())
.find(Boolean) || ""
);
}
function findOne(root, matchArgs) {
if (!root || !fs.existsSync(root)) {
return "";
}
try {
return firstLine(
execFileSync("find", [root, ...matchArgs], {
encoding: "utf8",
}),
);
} catch {
return "";
}
}
function resolveMdToPdfPackage() {
const explicit = process.env.MD_TO_PDF_PACKAGE;
if (explicit && fs.existsSync(explicit)) {
return explicit;
}
const found = findOne(path.join(homeDir, ".npm", "_npx"), [
"-path",
"*/node_modules/md-to-pdf",
"-type",
"d",
]);
if (found) {
return found;
}
throw new Error(
"unable to find md-to-pdf package; set MD_TO_PDF_PACKAGE to a node_modules/md-to-pdf path",
);
}
function resolveChromePath() {
const envPaths = [
process.env.MD_TO_PDF_CHROME,
process.env.CHROME_HEADLESS_SHELL,
];
for (const candidate of envPaths) {
if (candidate && fs.existsSync(candidate)) {
return candidate;
}
}
return findOne(path.join(homeDir, ".cache", "puppeteer"), [
"-path",
"*/chrome-headless-shell",
"-type",
"f",
]);
}
function loadCssWithEmbeddedFont() {
const cssPath = path.join(docsDir, "pdf.css");
const fontPath = path.join(
docsDir,
"assets",
"fonts",
"NotoSansGlagolitic-Regular.ttf",
);
const fontBase64 = fs.readFileSync(fontPath).toString("base64");
const css = fs.readFileSync(cssPath, "utf8");
return css.replace(
'url("assets/fonts/NotoSansGlagolitic-Regular.ttf")',
`url("data:font/truetype;base64,${fontBase64}")`,
);
}
async function main() {
const mdToPdfPackage = resolveMdToPdfPackage();
const { mdToPdf } = require(mdToPdfPackage);
const chromePath = resolveChromePath();
const baseOptions = {
basedir: repoRoot,
css: loadCssWithEmbeddedFont(),
launch_options: {
args: ["--no-sandbox", "--disable-setuid-sandbox"],
},
pdf_options: {
format: "A4",
printBackground: true,
margin: {
top: "18mm",
right: "16mm",
bottom: "18mm",
left: "16mm",
},
},
};
if (chromePath) {
baseOptions.launch_options.executablePath = chromePath;
}
const jobs = [
{
source: "docs/papers/SLOVO_WHITEPAPER.md",
dest: "docs/papers/SLOVO_WHITEPAPER.pdf",
},
{
source: "docs/language/MANIFEST.md",
dest: "docs/papers/SLOVO_MANIFEST.pdf",
},
{
source: "docs/papers/GLAGOL_WHITEPAPER.md",
dest: "docs/papers/GLAGOL_WHITEPAPER.pdf",
},
{
source: "docs/compiler/GLAGOL_COMPILER_MANIFEST.md",
dest: "docs/papers/GLAGOL_COMPILER_MANIFEST.pdf",
},
];
for (const job of jobs) {
const result = await mdToPdf(
{
path: path.join(repoRoot, job.source),
},
{
...baseOptions,
dest: path.join(repoRoot, job.dest),
},
);
if (!result) {
throw new Error(`failed to render ${job.source}`);
}
console.log(job.dest);
}
}
main().catch((error) => {
console.error(error);
process.exit(1);
});