From fb57f5a4dcebe39928f5ac39960f28cb67ef4d51 Mon Sep 17 00:00:00 2001 From: Thomas Reitz Date: Sat, 14 Mar 2026 15:02:24 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20exakte=20Header-H=C3=B6he=20f=C3=BCr=20T?= =?UTF-8?q?imeline-Linie=20pr=C3=BCfen=20statt=20fester=2040px-Schwelle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Berechnet die tatsächliche Mindesthöhe des nächsten Projekteintrags (Datum + Rolle + Firma) identisch zur Seitenumbruch-Logik. Verhindert hängende Linien wenn der nächste Header > 40px hoch ist und eine neue Seite benötigt. Co-Authored-By: Claude Sonnet 4.6 --- .../expert-profile/profile-export.service.ts | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/core-service/src/core/expert-profile/profile-export.service.ts b/packages/core-service/src/core/expert-profile/profile-export.service.ts index 181b01c..88ce1ab 100644 --- a/packages/core-service/src/core/expert-profile/profile-export.service.ts +++ b/packages/core-service/src/core/expert-profile/profile-export.service.ts @@ -568,10 +568,24 @@ export class ProfileExportService { yRight += 12; // Timeline-Linie: nur zeichnen wenn kein Seitenumbruch im Eintrag - // UND der nächste Eintrag noch auf dieser Seite passt (kein hängender Strich) - if (i < profile.projects.length - 1 && !pageBreakOccurred && yRight + 40 <= pageBottom) { - doc.moveTo(timelineX, entryStartY + 8).lineTo(timelineX, yRight - 4) - .strokeColor(accentColor).lineWidth(1).stroke(); + // UND der nächste Eintrag noch auf dieser Seite passt (exakte Header-Höhe prüfen) + if (i < profile.projects.length - 1 && !pageBreakOccurred) { + const nextProj = profile.projects[i + 1]; + const nextRoleText = this.sanitizePdfText(nextProj.role); + const nextCompanyLine = nextProj.company + ? this.sanitizePdfText([nextProj.company, nextProj.industry].filter(Boolean).join(' \u00b7 ')) + : ''; + doc.font('Helvetica-Bold').fontSize(10); + const nextRoleH = doc.heightOfString(nextRoleText, { width: contentWidth }); + doc.fontSize(9); + const nextCompanyH = nextCompanyLine + ? doc.heightOfString(nextCompanyLine, { width: contentWidth }) + 2 + : 0; + const nextHeaderH = 14 + nextRoleH + 2 + nextCompanyH + 8; + if (yRight + nextHeaderH <= pageBottom) { + doc.moveTo(timelineX, entryStartY + 8).lineTo(timelineX, yRight - 4) + .strokeColor(accentColor).lineWidth(1).stroke(); + } } } }