Files
fellakte/main/pdf.js
T
2026-09-14 18:57:05 +00:00

70 lines
3.7 KiB
JavaScript

// pdf.js — renders a branded, signed document (Einwilligung / Behandlungsvertrag) to
// PDF using Electron's own printToPDF, no extra PDF library needed. A hidden
// BrowserWindow loads a self-contained HTML string (logo and signature inlined as
// base64 data URIs — a data: page can't reliably load file:// resources) and prints it.
"use strict";
const { BrowserWindow } = require("electron");
function escapeHtml(s) {
return String(s ?? "").replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c]));
}
async function renderPdf({ title, ownerBlockHtml, bodyHtml, signatureDataUri, signedInfoHtml, logoDataUri }) {
const html = `<!doctype html><html><head><meta charset="utf-8"><title>${escapeHtml(title)}</title>
<style>
@page { margin: 22mm 18mm; }
/* .footer is pinned to the bottom of every printed page (see below); this padding
reserves the same space in the normal content flow so text never runs under it. */
body{ font-family: Georgia, 'Times New Roman', serif; color:#3a332c; margin:0; font-size:12px; line-height:1.55; padding-bottom:34px; }
.header{ display:flex; align-items:center; gap:16px; border-bottom:2px solid #b8975a; padding-bottom:14px; margin-bottom:22px; }
.header img{ height:56px; width:auto; }
.practice{ font-family: Georgia, serif; font-size:10.5px; color:#7a6c60; line-height:1.5; }
.practice b{ color:#3a332c; font-size:12px; letter-spacing:.03em; }
h1{ font-size:19px; margin:0 0 20px; color:#3a332c; font-weight:normal; letter-spacing:.02em; }
.owner-block{ font-size:12px; margin-bottom:20px; }
.owner-block .row{ display:flex; gap:8px; margin-bottom:3px; }
.owner-block .lbl{ width:120px; color:#7a6c60; flex-shrink:0; }
.body h2{ font-size:12.5px; margin:16px 0 5px; color:#3a332c; }
.body p{ margin:0 0 8px; }
.body ul{ padding-left:18px; margin:6px 0 12px; }
.body li{ margin-bottom:5px; }
.body li.checked::before{ content:"\\2713 "; color:#6f8f5c; font-weight:bold; }
.body li.unchecked::before{ content:"\\2717 "; color:#a89c8f; }
.sig-block{ margin-top:30px; border-top:1px solid #e2d0bd; padding-top:14px; font-size:11.5px; }
.sig-block img{ max-height:65px; display:block; margin:8px 0 4px; }
/* position:fixed repeats an element on every page when Chromium paginates for print,
which is exactly what we want for a letterhead footer instead of it just trailing
after the content wherever that happens to end. */
.footer{ position:fixed; left:0; right:0; bottom:0; padding-top:10px; border-top:1px solid #e2d0bd; font-size:8.5px; color:#a89c8f; text-align:center; background:#fff; }
</style></head>
<body>
<div class="header">
${logoDataUri ? `<img src="${logoDataUri}">` : ""}
<div class="practice"><b>TIERHEILPRAXIS IRIS HACK</b><br>
Iris Hack, Elisabethstr. 17, 84489 Burghausen, Deutschland<br>
Tel: +49 160 3408101 &middot; info@tierheilpraxis-hack.de</div>
</div>
<h1>${escapeHtml(title)}</h1>
<div class="owner-block">${ownerBlockHtml}</div>
<div class="body">${bodyHtml}</div>
${signedInfoHtml || signatureDataUri ? `<div class="sig-block">${signedInfoHtml}${signatureDataUri ? `<img src="${signatureDataUri}">` : ""}</div>` : ""}
<div class="footer">Tierheilpraxis Iris Hack &middot; Elisabethstr. 17, 84489 Burghausen &middot; Steuernummer: 106/224/10465 &middot; IBAN: DE59711600000007312210</div>
</body></html>`;
const win = new BrowserWindow({ show: false, webPreferences: { offscreen: false } });
try {
await win.loadURL("data:text/html;charset=utf-8," + encodeURIComponent(html));
const buffer = await win.webContents.printToPDF({
printBackground: true,
pageSize: "A4",
margins: { marginType: "default" },
});
return buffer;
} finally {
win.destroy();
}
}
module.exports = { renderPdf };