#!/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); });