diff --git a/public/index.html b/public/index.html index 7546621..192e688 100644 --- a/public/index.html +++ b/public/index.html @@ -498,8 +498,17 @@ function closeAiModal(){ } async function downloadPdf(){ + const fallbackWin = window.open('', '_blank'); + const openPrintFallback = (msg='') => { + if (msg) alert(msg); + if (!fallbackWin) return; + fallbackWin.document.write(`Coachingcards PDF${app.innerHTML}`); + fallbackWin.document.close(); + fallbackWin.onload = () => setTimeout(() => fallbackWin.print(), 200); + }; + if (!window.html2canvas || !window.jspdf?.jsPDF) { - alert('PDF-Export-Bibliothek nicht geladen.'); + openPrintFallback('PDF-Bibliotheken fehlen – Druckdialog als Fallback.'); return; } @@ -511,18 +520,12 @@ async function downloadPdf(){ root.style.background = '#fff'; root.innerHTML = app.innerHTML; root.querySelectorAll('.card-actions,.card-action-btn,button,[data-edit-all]').forEach(el => el.remove()); - root.querySelectorAll('.card-size').forEach(el => { - el.style.boxShadow = 'none'; - el.style.margin = '0 0 12px 0'; - }); + root.querySelectorAll('.card-size').forEach(el => { el.style.boxShadow = 'none'; el.style.margin = '0 0 12px 0'; }); document.body.appendChild(root); try { const cards = [...root.querySelectorAll('.card-size')]; - if (!cards.length) { - alert('Keine Karten für den Export sichtbar.'); - return; - } + if (!cards.length) throw new Error('Keine Karten sichtbar'); const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' }); @@ -533,31 +536,31 @@ async function downloadPdf(){ const usableH = pageH - margin * 2; for (let i = 0; i < cards.length; i++) { - const canvas = await window.html2canvas(cards[i], { - scale: 2, - useCORS: true, - backgroundColor: '#ffffff', - logging: false - }); + const canvas = await Promise.race([ + window.html2canvas(cards[i], { scale: 2, useCORS: true, backgroundColor: '#ffffff', logging: false }), + new Promise((_, rej) => setTimeout(() => rej(new Error('Render timeout')), 15000)) + ]); const img = canvas.toDataURL('image/jpeg', 0.98); - const imgW = canvas.width; - const imgH = canvas.height; - const ratio = Math.min(usableW / imgW, usableH / imgH); - const drawW = imgW * ratio; - const drawH = imgH * ratio; + const ratio = Math.min(usableW / canvas.width, usableH / canvas.height); + const drawW = canvas.width * ratio; + const drawH = canvas.height * ratio; const x = margin + (usableW - drawW) / 2; const y = margin + (usableH - drawH) / 2; - if (i > 0) pdf.addPage(); pdf.addImage(img, 'JPEG', x, y, drawW, drawH, undefined, 'FAST'); } pdf.save(`coachingcards-${new Date().toISOString().slice(0,10)}.pdf`); + if (fallbackWin && !fallbackWin.closed) fallbackWin.close(); + } catch (e) { + console.error('PDF export failed', e); + openPrintFallback('PDF-Download fehlgeschlagen – nutze Druckdialog als Fallback.'); } finally { root.remove(); } } + async function syncActiveJob(){ const r = await fetch('/api/generate/active'); const j = await r.json();