#!/usr/bin/env node /** * Erzeugt aus DSGVO-Dokumentation.md eine PDF-Datei. * Verwendung: node generate-dsgvo-pdf.js */ const PDFDocument = require('pdfkit'); const fs = require('fs'); const path = require('path'); const MARGIN = 50; const PAGE_WIDTH = 595; // A4 const CONTENT_WIDTH = PAGE_WIDTH - 2 * MARGIN; function stripBold(text) { return text.replace(/\*\*([^*]+)\*\*/g, '$1'); } function isTableRow(line) { return /^\|.+\|$/.test(line.trim()) && !/^[\s|:-]+$/.test(line.replace(/\s/g, '')); } function parseTableRows(lines, startIndex) { const rows = []; let i = startIndex; while (i < lines.length && isTableRow(lines[i])) { const line = lines[i]; if (/^[\s|:-]+$/.test(line.replace(/\s/g, ''))) { i++; continue; // separator line } const cells = line.split('|').slice(1, -1).map(c => c.trim()); if (cells.some(c => c)) rows.push(cells); i++; } return { rows, nextIndex: i }; } function writeText(doc, text, options = {}) { const opts = { width: CONTENT_WIDTH, ...options }; doc.text(text, opts); } function addParagraph(doc, line, fontSize = 10) { doc.fontSize(fontSize).font('Helvetica'); const text = stripBold(line.trim()); if (!text) return; writeText(doc, text); doc.moveDown(0.5); } function addBullet(doc, line, fontSize = 10) { doc.fontSize(fontSize).font('Helvetica'); const text = stripBold(line.replace(/^[-*]\s*/, '').trim()); if (!text) return; doc.text('• ', { continued: true }); doc.text(text, { width: CONTENT_WIDTH - 20 }); doc.moveDown(0.4); } function addTable(doc, rows, fontSize = 9) { if (rows.length === 0) return; const colCount = rows[0].length; const colWidth = CONTENT_WIDTH / colCount; const rowHeight = fontSize * 1.4; const startY = doc.y; doc.fontSize(fontSize); rows.forEach((row, rowIndex) => { const isHeader = rowIndex === 0; if (isHeader) doc.font('Helvetica-Bold'); if (doc.y > 750) { doc.addPage(); doc.y = MARGIN; } let x = MARGIN; row.forEach((cell, cellIndex) => { doc.text(cell, x, doc.y, { width: colWidth - 4, align: 'left' }); x += colWidth; }); doc.y += rowHeight; if (isHeader) doc.font('Helvetica'); }); doc.moveDown(0.5); } function generateDSGVOPdf() { const mdPath = path.join(__dirname, 'DSGVO-Dokumentation.md'); const outPath = path.join(__dirname, 'DSGVO-Dokumentation.pdf'); if (!fs.existsSync(mdPath)) { console.error('Datei nicht gefunden: DSGVO-Dokumentation.md'); process.exit(1); } const content = fs.readFileSync(mdPath, 'utf8'); const lines = content.split(/\r?\n/); const doc = new PDFDocument({ margin: MARGIN, size: 'A4' }); const stream = fs.createWriteStream(outPath); doc.pipe(stream); let i = 0; while (i < lines.length) { const line = lines[i]; const trimmed = line.trim(); // Neue Seite wenn nötig if (doc.y > 750) doc.addPage(); if (trimmed.startsWith('# ')) { doc.fontSize(22).font('Helvetica-Bold'); writeText(doc, trimmed.slice(2).trim(), { align: 'center' }); doc.moveDown(0.5); doc.fontSize(12).text('Stundenerfassungssystem', { align: 'center' }); doc.moveDown(1); doc.font('Helvetica'); i++; continue; } if (trimmed.startsWith('## ')) { doc.fontSize(16).font('Helvetica-Bold'); writeText(doc, trimmed.slice(3).trim()); doc.moveDown(0.5); doc.font('Helvetica').fontSize(10); i++; continue; } if (trimmed.startsWith('### ')) { doc.fontSize(13).font('Helvetica-Bold'); writeText(doc, trimmed.slice(4).trim()); doc.moveDown(0.4); doc.font('Helvetica').fontSize(10); i++; continue; } if (trimmed === '---') { doc.moveDown(0.5); i++; continue; } if (isTableRow(line)) { const { rows, nextIndex } = parseTableRows(lines, i); addTable(doc, rows); i = nextIndex; continue; } if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) { addBullet(doc, trimmed); i++; continue; } if (trimmed) { addParagraph(doc, trimmed); } else { doc.moveDown(0.3); } i++; } doc.end(); stream.on('finish', () => { console.log('PDF erstellt: ' + outPath); }); stream.on('error', (err) => { console.error('Fehler beim Schreiben der PDF:', err); process.exit(1); }); } generateDSGVOPdf();