// ==================== PDF SERVICE ==================== import PDFDocument from 'pdfkit'; interface EmailData { from: string; to: string; cc?: string; subject: string; date: Date; bodyText?: string; bodyHtml?: string; } /** * Generiert ein PDF aus einer E-Mail */ export async function generateEmailPdf(email: EmailData): Promise { return new Promise((resolve, reject) => { try { const doc = new PDFDocument({ size: 'A4', margins: { top: 50, bottom: 50, left: 50, right: 50 }, }); const chunks: Buffer[] = []; doc.on('data', (chunk) => chunks.push(chunk)); doc.on('end', () => resolve(Buffer.concat(chunks))); doc.on('error', reject); // Header doc .fontSize(18) .font('Helvetica-Bold') .text('E-Mail', { align: 'center' }); doc.moveDown(1.5); // Metadaten-Tabelle doc.fontSize(10).font('Helvetica'); // Von doc .font('Helvetica-Bold') .text('Von: ', { continued: true }) .font('Helvetica') .text(email.from); // An doc .font('Helvetica-Bold') .text('An: ', { continued: true }) .font('Helvetica') .text(email.to); // CC (falls vorhanden) if (email.cc) { doc .font('Helvetica-Bold') .text('CC: ', { continued: true }) .font('Helvetica') .text(email.cc); } // Datum const formattedDate = email.date.toLocaleString('de-DE', { dateStyle: 'full', timeStyle: 'short', }); doc .font('Helvetica-Bold') .text('Datum: ', { continued: true }) .font('Helvetica') .text(formattedDate); // Betreff doc .font('Helvetica-Bold') .text('Betreff: ', { continued: true }) .font('Helvetica') .text(email.subject || '(Kein Betreff)'); doc.moveDown(1); // Trennlinie doc .moveTo(50, doc.y) .lineTo(doc.page.width - 50, doc.y) .stroke(); doc.moveDown(1); // Inhalt doc.fontSize(11); // HTML in Text konvertieren (vereinfacht) let content = email.bodyText || ''; if (!content && email.bodyHtml) { // Einfache HTML-zu-Text Konvertierung content = htmlToPlainText(email.bodyHtml); } if (!content) { content = '(Kein Inhalt)'; } // Text mit Zeilenumbrüchen ausgeben doc.text(content, { align: 'left', lineGap: 2, }); // Footer mit Erstellungsdatum const footerY = doc.page.height - 40; doc .fontSize(8) .fillColor('#666666') .text( `Exportiert am ${new Date().toLocaleString('de-DE')}`, 50, footerY, { align: 'center', width: doc.page.width - 100 } ); doc.end(); } catch (error) { reject(error); } }); } /** * Konvertiert HTML in Plain-Text (vereinfacht) */ function htmlToPlainText(html: string): string { let text = html; // Zeilenumbrüche vor Block-Elementen text = text.replace(/<(br|p|div|h[1-6]|li|tr)[^>]*>/gi, '\n'); // Listen-Elemente text = text.replace(/]*>/gi, '\n• '); // Links mit URL text = text.replace(/]*href="([^"]*)"[^>]*>([^<]*)<\/a>/gi, '$2 ($1)'); // Alle anderen Tags entfernen text = text.replace(/<[^>]+>/g, ''); // HTML-Entities dekodieren text = text.replace(/ /g, ' '); text = text.replace(/&/g, '&'); text = text.replace(/</g, '<'); text = text.replace(/>/g, '>'); text = text.replace(/"/g, '"'); text = text.replace(/'/g, "'"); text = text.replace(/ä/g, 'ä'); text = text.replace(/ö/g, 'ö'); text = text.replace(/ü/g, 'ü'); text = text.replace(/Ä/g, 'Ä'); text = text.replace(/Ö/g, 'Ö'); text = text.replace(/Ü/g, 'Ü'); text = text.replace(/ß/g, 'ß'); // Mehrfache Leerzeilen reduzieren text = text.replace(/\n{3,}/g, '\n\n'); // Führende/folgende Leerzeichen entfernen text = text.trim(); return text; }