mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-25 00:16:41 +02:00
fix: PDF export — tab chars garbled, unconditional bullets, encrypted PDF attachments
Root causes identified via DB hex-dump and server logs:
1. Tab character in budget lines: DB stores e.g. `**Budget Verantwortung:**\t750.000 EUR`
(byte 0x09 between `:**` and the amount). PDFKit can't render \t in WinAnsiEncoding,
producing garbage output like `"sSãUU`. Fix: `.replace(/\t/g, ' ')` in cleaned text.
2. Unconditional bullet: `\u2022 ${sanitize(hasBullet ? cleaned : cleaned)}` always
prepended `•` — the ternary was a no-op. Fix: only add `•` when hasBullet is true;
`**...**` header lines now render as Helvetica-Bold without a bullet.
3. ITIL4 Foundation Cert.pdf is owner-password-encrypted. pdf-lib threw
"Input document is encrypted" → cert was silently skipped.
Fix: `PdfLib.load(attBuffer, { ignoreEncryption: true })`.
Applies to both PDF and DOCX export paths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
98e7f48ce2
commit
1608f4e936
1 changed files with 21 additions and 12 deletions
|
|
@ -528,10 +528,9 @@ export class ProfileExportService {
|
|||
yRight += doc.heightOfString(companyLine, { width: contentWidth }) + 2;
|
||||
}
|
||||
|
||||
// Aufgaben (Bullet nur wenn Original-Zeile ein Aufzählungszeichen hat)
|
||||
// Aufgaben: Bold für **...**-Zeilen, Bullet nur wenn Original-Zeile ein Aufzählungszeichen hat
|
||||
if (proj.tasks) {
|
||||
const taskLines = proj.tasks.split('\n').filter((l: string) => l.trim());
|
||||
doc.font('Helvetica').fontSize(8).fillColor('#444444');
|
||||
for (const task of taskLines) {
|
||||
if (yRight > pageBottom) {
|
||||
doc.addPage();
|
||||
|
|
@ -539,12 +538,21 @@ export class ProfileExportService {
|
|||
pageBreakOccurred = true;
|
||||
}
|
||||
const raw = task.trim();
|
||||
const isBold = /^\*\*/.test(raw);
|
||||
const hasBullet = /^[•\u2022]\s/.test(raw) || /^\d+\.\s/.test(raw);
|
||||
const cleaned = this.normalizeTaskLine(raw);
|
||||
const cleaned = this.normalizeTaskLine(raw).replace(/\t/g, ' ');
|
||||
if (!cleaned) continue;
|
||||
const displayText = `\u2022 ${this.sanitizePdfText(hasBullet ? cleaned : cleaned)}`;
|
||||
doc.text(displayText, contentX + 4, yRight, { width: contentWidth - 8 });
|
||||
yRight += doc.heightOfString(displayText, { width: contentWidth - 8 }) + 1;
|
||||
const sanitized = this.sanitizePdfText(cleaned);
|
||||
if (isBold) {
|
||||
doc.font('Helvetica-Bold').fontSize(8).fillColor('#333333');
|
||||
doc.text(sanitized, contentX + 4, yRight, { width: contentWidth - 8 });
|
||||
yRight += doc.heightOfString(sanitized, { width: contentWidth - 8 }) + 1;
|
||||
} else {
|
||||
const displayText = hasBullet ? `\u2022 ${sanitized}` : sanitized;
|
||||
doc.font('Helvetica').fontSize(8).fillColor('#444444');
|
||||
doc.text(displayText, contentX + 4, yRight, { width: contentWidth - 8 });
|
||||
yRight += doc.heightOfString(displayText, { width: contentWidth - 8 }) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -609,7 +617,8 @@ export class ProfileExportService {
|
|||
for (const att of pdfAttachments) {
|
||||
try {
|
||||
const attBuffer = this.base64ToBuffer(att.data);
|
||||
const attPdf = await PdfLib.load(attBuffer);
|
||||
// ignoreEncryption: true erlaubt das Einbetten von PDFs mit Owner-Password-Schutz
|
||||
const attPdf = await PdfLib.load(attBuffer, { ignoreEncryption: true });
|
||||
const pageIndices = attPdf.getPageIndices();
|
||||
const copiedPages = await merged.copyPages(attPdf, pageIndices);
|
||||
for (const page of copiedPages) {
|
||||
|
|
@ -894,15 +903,15 @@ export class ProfileExportService {
|
|||
const taskLines = proj.tasks.split('\n').filter((l: string) => l.trim());
|
||||
for (const task of taskLines) {
|
||||
const raw = task.trim();
|
||||
const isBold = /^\*\*/.test(raw);
|
||||
const hasBullet = /^[•\u2022]\s/.test(raw) || /^\d+\.\s/.test(raw);
|
||||
const cleaned = this.normalizeTaskLine(raw);
|
||||
const cleaned = this.normalizeTaskLine(raw).replace(/\t/g, ' ');
|
||||
if (!cleaned) continue;
|
||||
const displayText = hasBullet ? `\u2022 ${cleaned}` : cleaned;
|
||||
rightParagraphs.push(
|
||||
new Paragraph({
|
||||
children: [
|
||||
new TextRun({ text: displayText, size: 16, color: '444444' }),
|
||||
],
|
||||
children: isBold
|
||||
? [new TextRun({ text: cleaned, size: 16, color: '333333', bold: true })]
|
||||
: [new TextRun({ text: hasBullet ? `\u2022 ${cleaned}` : cleaned, size: 16, color: '444444' })],
|
||||
spacing: { after: 20 },
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue