#!/usr/bin/env bash # Regenerates professional-looking A4 and A3 landscape PDFs from a Mermaid # .mmd flowchart: consistent color theme, legible fonts, a title (read from # the .mmd's own YAML frontmatter) and a footer with the generation date. # # Usage: # ./render-pdf.sh [path/to/file.mmd] # # Defaults to round-lifecycle.mmd in this same directory. # Produces -A4.pdf and -A3.pdf next to the .mmd file. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MMD_FILE="${1:-$SCRIPT_DIR/round-lifecycle.mmd}" if [[ ! -f "$MMD_FILE" ]]; then echo "Errore: file non trovato: $MMD_FILE" >&2 exit 1 fi OUT_DIR="$(cd "$(dirname "$MMD_FILE")" && pwd)" BASE="$(basename "$MMD_FILE" .mmd)" SVG_TMP="$OUT_DIR/.${BASE}.tmp.svg" CONFIG_TMP="$OUT_DIR/.${BASE}.tmp-config.json" cleanup() { rm -f "$SVG_TMP" "$CONFIG_TMP" "$OUT_DIR/.${BASE}.tmp-A4.html" "$OUT_DIR/.${BASE}.tmp-A3.html" } trap cleanup EXIT # Consistent, print-friendly color theme (indigo nodes/edges, warm amber phase # clusters, generous font size) instead of mermaid's flat default palette. cat > "$CONFIG_TMP" <<'EOF' { "theme": "base", "themeVariables": { "fontFamily": "\"Segoe UI\", Helvetica, Arial, sans-serif", "fontSize": "17px", "primaryColor": "#c9d6f7", "primaryBorderColor": "#3949ab", "primaryTextColor": "#1a1a2e", "lineColor": "#3949ab", "secondaryColor": "#fff8e1", "tertiaryColor": "#ffffff", "clusterBkg": "#fff8e1", "clusterBorder": "#c9a227", "edgeLabelBackground": "#c9d6f7", "titleColor": "#1a1a2e" }, "flowchart": { "curve": "basis", "padding": 16, "htmlLabels": true, "nodeSpacing": 100, "rankSpacing": 25 } } EOF echo "-> Rendering diagram to SVG..." npx -y @mermaid-js/mermaid-cli -i "$MMD_FILE" -o "$SVG_TMP" -b white -c "$CONFIG_TMP" echo "-> Building print-ready A4/A3 PDFs..." # mermaid-cli pulls in puppeteer as a transitive dependency; reuse that install # instead of adding a separate one just for this script. PUPPETEER_DIR="$(dirname "$(find "$HOME/.npm/_npx" -maxdepth 3 -type d -name puppeteer 2>/dev/null | head -n1)")" if [[ -z "$PUPPETEER_DIR" || ! -d "$PUPPETEER_DIR" ]]; then echo "Errore: modulo puppeteer non trovato (serve mermaid-cli gia' eseguito almeno una volta)." >&2 exit 1 fi export NODE_PATH="$PUPPETEER_DIR" GENERATED_AT="$(date '+%d/%m/%Y %H:%M')" # Human title for the header banner; falls back to a prettified filename for # any .mmd this script hasn't been told about explicitly. case "$BASE" in round-lifecycle) TITLE="Ciclo di vita di un round" ;; platform-overview) TITLE="Flusso completo della piattaforma" ;; *) TITLE="$(echo "$BASE" | tr '-' ' ' | sed 's/\b\(.\)/\u\1/g')" ;; esac node -e ' const fs = require("fs"); const path = require("path"); const puppeteer = require("puppeteer"); const outDir = process.argv[1]; const base = process.argv[2]; const svgPath = process.argv[3]; const generatedAt = process.argv[4]; const title = process.argv[5]; const svg = fs.readFileSync(svgPath, "utf-8"); function htmlFor(size) { return `
PLM Lottery ${title}
${svg}
`; } (async () => { const browser = await puppeteer.launch({ args: ["--no-sandbox"] }); const page = await browser.newPage(); for (const fmt of ["A4", "A3"]) { const htmlPath = path.join(outDir, `.${base}.tmp-${fmt}.html`); fs.writeFileSync(htmlPath, htmlFor(fmt)); await page.goto("file://" + htmlPath, { waitUntil: "networkidle0" }); const pdfPath = path.join(outDir, `${base}-${fmt}.pdf`); await page.pdf({ path: pdfPath, format: fmt, landscape: true, printBackground: true, margin: { top: "6mm", bottom: "6mm", left: "6mm", right: "6mm" }, }); console.log(" " + pdfPath); } await browser.close(); })(); ' "$OUT_DIR" "$BASE" "$SVG_TMP" "$GENERATED_AT" "$TITLE" echo "Fatto."