diff --git a/public/js/main.js b/public/js/main.js index bd63aa8..97bf372 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -348,6 +348,24 @@ function generatePdfDocument(settings, applications, month, year) { return new Date(lv ? lv.datum : app.datum); }; + // Imported/scraped values can contain HTML entities (e.g. "GmbH & Co. KG") + // and sanitizeInput() stores "< > \" '" escaped — decode them so the PDF shows + // real characters instead of the raw entity text. + const decodeEntities = (str) => { + if (str == null) return ''; + return String(str) + .replace(/&#(\d+);/g, (_, n) => String.fromCharCode(parseInt(n, 10))) + .replace(/&#x([0-9a-fA-F]+);/g, (_, n) => String.fromCharCode(parseInt(n, 16))) + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/ /g, ' ') + .replace(/&/g, '&'); + }; + // Cleaned cell text with an em-dash fallback for missing/empty values. + const cell = (v) => decodeEntities(v).trim() || '—'; + // Determine the year shown in the title. When no year was picked in the form, // derive it from the data so the title still reads e.g. "Juni 2026". const yearsInData = [...new Set( @@ -599,11 +617,14 @@ function generatePdfDocument(settings, applications, month, year) { head: [['Datum', 'Firma', 'Stelle', 'Art', 'Status']], body: tableRows.map(({ app, date, status }) => ([ isNaN(date) ? '—' : date.toLocaleDateString('de-DE'), - app.firma || '—', - app.stelle || '—', - app.art || '—', + cell(app.firma), + cell(app.stelle), + cell(app.art), status ])), + // Never split a row across a page boundary — a split row leaves the + // single-line cells (Datum, Art, Status) blank on the continuation page. + rowPageBreak: 'avoid', styles: { font: 'helvetica', fontSize: 9, cellPadding: 2.4, textColor: [31, 41, 55], lineColor: [226, 232, 240],