Initial commit - SaveTheMoment Atlas Basis-Setup
This commit is contained in:
171
lib/pdf-template-service.ts
Normal file
171
lib/pdf-template-service.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user