Files
Atlas/lib/pdf-template-service.ts
2025-11-12 20:21:32 +01:00

172 lines
4.4 KiB
TypeScript

import { PDFDocument, rgb, StandardFonts } from 'pdf-lib';
import { readFile } from 'fs/promises';
import path from 'path';
import { formatDate } from './date-utils';
export async function generateContractFromTemplate(
booking: any,
location: any,
photobox: any,
signatureData?: string
) {
try {
// Load the template PDF
const templatePath = path.join(process.cwd(), 'mietvertrag-vorlage.pdf');
const templateBytes = await readFile(templatePath);
// Load PDF
const pdfDoc = await PDFDocument.load(templateBytes);
const pages = pdfDoc.getPages();
const firstPage = pages[0];
// Embed font
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
const fontBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
const { width, height } = firstPage.getSize();
// PDF coordinate system: (0,0) is bottom-left, Y increases upwards
// A4 page height is ~842 points
// Page 1: Customer Info & Table
// Customer name (under "und") - around middle of page
if (booking.customerName) {
firstPage.drawText(booking.customerName, {
x: 250,
y: 465,
size: 10,
font: font,
color: rgb(0, 0, 0),
});
}
// Customer address (next line)
if (booking.customerAddress) {
firstPage.drawText(
`${booking.customerAddress}, ${booking.customerZip} ${booking.customerCity}`,
{
x: 250,
y: 450,
size: 10,
font: font,
color: rgb(0, 0, 0),
}
);
}
// Distance (in "Lieferung inkl. Aufbau" row)
if (booking.distance) {
const distance = booking.distance || 0;
const totalKm = distance * 2;
// Fill in the km field
firstPage.drawText(`${distance.toFixed(1)}`, {
x: 735,
y: 95,
size: 9,
font: font,
color: rgb(0, 0, 0),
});
firstPage.drawText(`${totalKm.toFixed(1)}`, {
x: 895,
y: 95,
size: 9,
font: font,
color: rgb(0, 0, 0),
});
}
// Event location and date (Section 2 - MIETZWECK) - bottom of page 1
if (booking.eventLocation && booking.eventDate) {
firstPage.drawText(
`${booking.eventLocation} am ${formatDate(booking.eventDate)}`,
{
x: 100,
y: 35,
size: 10,
font: font,
color: rgb(0, 0, 0),
}
);
}
// Page 2: Mietzeit & Mietpreis
const secondPage = pages[1];
// Event date (Section 3 - MIETZEIT)
if (booking.eventDate) {
secondPage.drawText(formatDate(booking.eventDate), {
x: 210,
y: height - 85,
size: 10,
font: font,
color: rgb(0, 0, 0),
});
}
// Price (Section 4 - MIETPREIS)
if (booking.calculatedPrice) {
const price = booking.calculatedPrice.toFixed(2);
secondPage.drawText(`${price}`, {
x: 475,
y: height - 165,
size: 10,
font: font,
color: rgb(0, 0, 0),
});
}
// Signature on last page if provided
if (signatureData && pages.length >= 4) {
const lastPage = pages[pages.length - 1];
// Convert base64 signature to image
try {
const signatureImage = await pdfDoc.embedPng(signatureData);
const signatureDims = signatureImage.scale(0.25);
// Right side signature box
lastPage.drawImage(signatureImage, {
x: 420,
y: 120,
width: signatureDims.width,
height: signatureDims.height,
});
// Signature name below
if (booking.customerName) {
lastPage.drawText(booking.customerName, {
x: 420,
y: 100,
size: 10,
font: font,
color: rgb(0, 0, 0),
});
}
// Signature date and info
const sigDate = new Date();
lastPage.drawText(`Digital signiert am ${formatDate(sigDate)}`, {
x: 420,
y: 85,
size: 8,
font: font,
color: rgb(0.4, 0.4, 0.4),
});
} catch (err) {
console.error('Signature embedding error:', err);
}
}
// Serialize the PDF
const pdfBytes = await pdfDoc.save();
return Buffer.from(pdfBytes);
} catch (error) {
console.error('PDF generation from template error:', error);
throw error;
}
}