"fix-pdf-guests-same-page"
This commit is contained in:
+80
-28
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// @ts-ignore — pdf-lib est complet lors d'un npm install propre sur le VPS
|
||||
const { PDFDocument, rgb, StandardFonts, PageSizes } = require('pdf-lib');
|
||||
// @ts-ignore
|
||||
const { PDFDocument, rgb, StandardFonts } = require('pdf-lib');
|
||||
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
@@ -10,6 +10,12 @@ export interface PdfGuest {
|
||||
company?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère le PDF de la facture.
|
||||
* - Une page par image (ticket/reçu)
|
||||
* - Si des invités sont présents, la liste est ajoutée EN BAS de la dernière
|
||||
* page d'image (même page, sous le ticket), sans page séparée.
|
||||
*/
|
||||
export async function generateInvoicePdf(
|
||||
imagePaths: string[],
|
||||
guests: PdfGuest[],
|
||||
@@ -17,46 +23,92 @@ export async function generateInvoicePdf(
|
||||
): Promise<void> {
|
||||
const pdfDoc = await PDFDocument.create();
|
||||
|
||||
for (const imgPath of imagePaths) {
|
||||
const font = guests.length > 0 ? await pdfDoc.embedFont(StandardFonts.Helvetica) : null;
|
||||
const fontBold = guests.length > 0 ? await pdfDoc.embedFont(StandardFonts.HelveticaBold) : null;
|
||||
|
||||
for (let idx = 0; idx < imagePaths.length; idx++) {
|
||||
const imgPath = imagePaths[idx];
|
||||
const imgBytes = await fs.readFile(imgPath);
|
||||
const ext = path.extname(imgPath).toLowerCase();
|
||||
const image = ext === '.png'
|
||||
? await pdfDoc.embedPng(imgBytes)
|
||||
: await pdfDoc.embedJpg(imgBytes);
|
||||
const dims = image.scale(1);
|
||||
const page = pdfDoc.addPage([dims.width, dims.height]);
|
||||
page.drawImage(image, { x: 0, y: 0, width: dims.width, height: dims.height });
|
||||
}
|
||||
|
||||
if (guests.length > 0) {
|
||||
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
|
||||
const fontBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
|
||||
const [W, H] = PageSizes.A4;
|
||||
const M = 50;
|
||||
const LH = 24;
|
||||
const page = pdfDoc.addPage([W, H]);
|
||||
let y = H - M;
|
||||
const imgW = image.width;
|
||||
const imgH = image.height;
|
||||
|
||||
page.drawText('Liste des invites', { x: M, y, size: 18, font: fontBold, color: rgb(0.1, 0.1, 0.1) });
|
||||
y -= LH * 1.5;
|
||||
const isLast = idx === imagePaths.length - 1;
|
||||
const addList = isLast && guests.length > 0;
|
||||
|
||||
page.drawLine({ start: { x: M, y }, end: { x: W - M, y }, thickness: 1, color: rgb(0.2, 0.2, 0.2) });
|
||||
y -= LH;
|
||||
// Hauteur supplémentaire pour la liste d'invités (sous le ticket)
|
||||
const PADDING = 20; // marge entre ticket et liste
|
||||
const LINE_H = 22; // hauteur par ligne d'invité
|
||||
const HEADER_H = 50; // titre + séparateur
|
||||
const listH = addList
|
||||
? HEADER_H + guests.length * LINE_H + PADDING * 2
|
||||
: 0;
|
||||
|
||||
page.drawText('Nom', { x: M, y, size: 11, font: fontBold, color: rgb(0.3, 0.3, 0.3) });
|
||||
page.drawText('Entreprise', { x: M + 230, y, size: 11, font: fontBold, color: rgb(0.3, 0.3, 0.3) });
|
||||
y -= 6;
|
||||
const pageW = imgW;
|
||||
const pageH = imgH + listH;
|
||||
|
||||
page.drawLine({ start: { x: M, y }, end: { x: W - M, y }, thickness: 0.5, color: rgb(0.7, 0.7, 0.7) });
|
||||
y -= LH;
|
||||
const page = pdfDoc.addPage([pageW, pageH]);
|
||||
|
||||
// ── Image (placée en haut de la page) ──────────────────────
|
||||
page.drawImage(image, { x: 0, y: listH, width: imgW, height: imgH });
|
||||
|
||||
// ── Liste des invités (en bas, sous l'image) ───────────────
|
||||
if (addList && font && fontBold) {
|
||||
const M = 30; // marge gauche/droite
|
||||
let y = listH - PADDING;
|
||||
|
||||
// Ligne de séparation entre ticket et liste
|
||||
page.drawLine({
|
||||
start: { x: M, y: listH - 1 },
|
||||
end: { x: pageW - M, y: listH - 1 },
|
||||
thickness: 1,
|
||||
color: rgb(0.8, 0.8, 0.8),
|
||||
});
|
||||
|
||||
// Titre
|
||||
page.drawText('Invités', {
|
||||
x: M, y,
|
||||
size: 13,
|
||||
font: fontBold,
|
||||
color: rgb(0.15, 0.15, 0.15),
|
||||
});
|
||||
y -= HEADER_H - PADDING - 8;
|
||||
|
||||
// Séparateur sous le titre
|
||||
page.drawLine({
|
||||
start: { x: M, y },
|
||||
end: { x: pageW - M, y },
|
||||
thickness: 0.5,
|
||||
color: rgb(0.75, 0.75, 0.75),
|
||||
});
|
||||
y -= 16;
|
||||
|
||||
// Lignes invités
|
||||
for (const guest of guests) {
|
||||
if (y < M + LH) break;
|
||||
page.drawText(guest.name, { x: M, y, size: 11, font, color: rgb(0.1, 0.1, 0.1), maxWidth: 220 });
|
||||
if (y < 4) break;
|
||||
page.drawText(guest.name, {
|
||||
x: M, y,
|
||||
size: 11,
|
||||
font: fontBold,
|
||||
color: rgb(0.1, 0.1, 0.1),
|
||||
maxWidth: guest.company ? (pageW - M * 2) / 2 - 10 : pageW - M * 2,
|
||||
});
|
||||
if (guest.company) {
|
||||
page.drawText(guest.company, { x: M + 230, y, size: 11, font, color: rgb(0.1, 0.1, 0.1), maxWidth: W - M - 230 - M });
|
||||
page.drawText(guest.company, {
|
||||
x: M + (pageW - M * 2) / 2,
|
||||
y,
|
||||
size: 11,
|
||||
font,
|
||||
color: rgb(0.35, 0.35, 0.35),
|
||||
maxWidth: (pageW - M * 2) / 2,
|
||||
});
|
||||
}
|
||||
y -= LINE_H;
|
||||
}
|
||||
y -= LH;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user