export const DASHBOARD_PDF_CAPTURE_ROOT_ID = 'dashboard-pdf-capture-root' /** * @param {{ scale?: number, filenameBase?: string }} [opts] */ export async function exportDashboardToPdf(opts = {}) { const scale = opts.scale ?? 2 const filenameBase = opts.filenameBase ?? 'mitai-uebersicht' const [{ default: html2canvas }, { jsPDF }] = await Promise.all([ import('html2canvas'), import('jspdf'), ]) const el = document.getElementById(DASHBOARD_PDF_CAPTURE_ROOT_ID) if (!el) throw new Error('Dashboard-Inhalt nicht gefunden (interner Fehler).') const prevScroll = window.scrollY window.scrollTo(0, 0) await new Promise((r) => requestAnimationFrame(r)) await new Promise((r) => requestAnimationFrame(r)) await new Promise((r) => setTimeout(r, 320)) try { const canvas = await html2canvas(el, { scale, useCORS: true, allowTaint: true, logging: false, backgroundColor: '#ffffff', ignoreElements: (node) => node?.getAttribute?.('data-dashboard-pdf-exclude') === 'true', scrollX: 0, scrollY: 0, width: el.scrollWidth, height: el.scrollHeight, windowWidth: el.scrollWidth, windowHeight: el.scrollHeight, }) const imgData = canvas.toDataURL('image/png', 1.0) const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4', compress: true }) const pageW = pdf.internal.pageSize.getWidth() const pageH = pdf.internal.pageSize.getHeight() const margin = 8 const innerW = pageW - 2 * margin const innerH = pageH - 2 * margin const imgW = innerW const imgH = (canvas.height * imgW) / canvas.width let position = margin pdf.addImage(imgData, 'PNG', margin, position, imgW, imgH, undefined, 'FAST') let heightLeft = imgH - innerH while (heightLeft > 0) { position = margin - (imgH - heightLeft) pdf.addPage() pdf.addImage(imgData, 'PNG', margin, position, imgW, imgH, undefined, 'FAST') heightLeft -= innerH } const safe = String(filenameBase) .replace(/[\\/:*?"<>|]+/g, '') .trim() pdf.save(`${safe || 'bericht'}.pdf`) } finally { window.scrollTo(0, prevScroll) } }