mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-25 00:16:41 +02:00
fix(core): PDF-Export — Footer-Leerseite behoben und Logo ueber Profilfoto
- Footer: doc.page.margins.bottom temporaer auf 0 gesetzt beim Footer-Zeichnen verhindert PDFKit Auto-Pagination (footerTextY > maxY wuerde sonst eine leere zweite Seite erzeugen) - Logo: Platform-Branding-Logo (aus Redis platform_branding_logo) wird oben in der linken Spalte ueber dem Profilfoto gerendert (fit 180x50px) - Firmendaten + Branding parallel via Promise.all aus Redis geladen Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
46ced98bf4
commit
f5d83dc1c3
1 changed files with 35 additions and 8 deletions
|
|
@ -192,12 +192,16 @@ export class ProfileExportService {
|
||||||
const data = await this.expertProfileService.getExportData(userId) as ExportData;
|
const data = await this.expertProfileService.getExportData(userId) as ExportData;
|
||||||
const profile = data.expertProfile;
|
const profile = data.expertProfile;
|
||||||
|
|
||||||
// Firmendaten für PDF-Footer laden
|
// Firmendaten + Branding-Logo parallel laden
|
||||||
let companyFooterText = '';
|
let companyFooterText = '';
|
||||||
|
let platformLogo: Buffer | null = null;
|
||||||
try {
|
try {
|
||||||
const raw = await this.redis.get('platform_company_settings');
|
const [companyRaw, brandingRaw] = await Promise.all([
|
||||||
if (raw) {
|
this.redis.get('platform_company_settings'),
|
||||||
const c = JSON.parse(raw) as Record<string, unknown>;
|
this.redis.get('platform_branding_logo'),
|
||||||
|
]);
|
||||||
|
if (companyRaw) {
|
||||||
|
const c = JSON.parse(companyRaw) as Record<string, unknown>;
|
||||||
const address = [c['street'], [c['postalCode'], c['city']].filter(Boolean).join(' ')].filter(Boolean).join(', ');
|
const address = [c['street'], [c['postalCode'], c['city']].filter(Boolean).join(' ')].filter(Boolean).join(', ');
|
||||||
companyFooterText = [
|
companyFooterText = [
|
||||||
c['name'],
|
c['name'],
|
||||||
|
|
@ -207,7 +211,13 @@ export class ProfileExportService {
|
||||||
c['website'],
|
c['website'],
|
||||||
].filter(Boolean).join(' | ');
|
].filter(Boolean).join(' | ');
|
||||||
}
|
}
|
||||||
} catch { /* Firmendaten nicht verfügbar — kein Footer */ }
|
if (brandingRaw) {
|
||||||
|
const b = JSON.parse(brandingRaw) as Record<string, unknown>;
|
||||||
|
if (typeof b['logo'] === 'string' && b['logo']) {
|
||||||
|
platformLogo = this.base64ToBuffer(b['logo']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch { /* Einstellungen nicht verfügbar */ }
|
||||||
const fullName = `${data.firstName} ${data.lastName}`;
|
const fullName = `${data.firstName} ${data.lastName}`;
|
||||||
const { firstName, lastName } = data;
|
const { firstName, lastName } = data;
|
||||||
|
|
||||||
|
|
@ -236,6 +246,19 @@ export class ProfileExportService {
|
||||||
|
|
||||||
// --- SEITE 1: Linke Spalte ---
|
// --- SEITE 1: Linke Spalte ---
|
||||||
|
|
||||||
|
// Platform-Logo (über dem Profilfoto)
|
||||||
|
if (platformLogo) {
|
||||||
|
try {
|
||||||
|
doc.image(platformLogo, leftColX, yLeft, {
|
||||||
|
fit: [leftColWidth, 50],
|
||||||
|
align: 'center',
|
||||||
|
});
|
||||||
|
yLeft += 58;
|
||||||
|
} catch (err) {
|
||||||
|
this.logger.warn('Platform-Logo konnte nicht gerendert werden', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Avatar (rundes Bild)
|
// Avatar (rundes Bild)
|
||||||
if (data.avatar) {
|
if (data.avatar) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -497,16 +520,20 @@ export class ProfileExportService {
|
||||||
|
|
||||||
// --- FOOTER: Firmendaten auf jeder Seite ---
|
// --- FOOTER: Firmendaten auf jeder Seite ---
|
||||||
if (companyFooterText) {
|
if (companyFooterText) {
|
||||||
const pageHeight = doc.page.height;
|
|
||||||
const footerLineY = pageHeight - 32;
|
|
||||||
const footerTextY = pageHeight - 26;
|
|
||||||
const range = doc.bufferedPageRange();
|
const range = doc.bufferedPageRange();
|
||||||
for (let p = range.start; p < range.start + range.count; p++) {
|
for (let p = range.start; p < range.start + range.count; p++) {
|
||||||
doc.switchToPage(p);
|
doc.switchToPage(p);
|
||||||
|
// Bottom-Margin temporär auf 0, damit PDFKit keine Leerseite erzeugt
|
||||||
|
const origBottom = doc.page.margins.bottom;
|
||||||
|
doc.page.margins.bottom = 0;
|
||||||
|
const pageH = doc.page.height;
|
||||||
|
const footerLineY = pageH - 32;
|
||||||
|
const footerTextY = pageH - 26;
|
||||||
doc.moveTo(40, footerLineY).lineTo(pageWidth - 40, footerLineY)
|
doc.moveTo(40, footerLineY).lineTo(pageWidth - 40, footerLineY)
|
||||||
.strokeColor('#cccccc').lineWidth(0.5).stroke();
|
.strokeColor('#cccccc').lineWidth(0.5).stroke();
|
||||||
doc.font('Helvetica').fontSize(7).fillColor('#999999');
|
doc.font('Helvetica').fontSize(7).fillColor('#999999');
|
||||||
doc.text(companyFooterText, 40, footerTextY, { width: pageWidth - 80, align: 'center', lineBreak: false });
|
doc.text(companyFooterText, 40, footerTextY, { width: pageWidth - 80, align: 'center', lineBreak: false });
|
||||||
|
doc.page.margins.bottom = origBottom;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue