fix: PDF FreePro 1 page A4 portrait - calcul précis hauteur cellule

This commit is contained in:
Manus
2026-06-05 10:50:00 -04:00
parent b5705b5209
commit 44186a056b

View File

@@ -100,31 +100,33 @@ function buildPdfDoc(importRecord: ImportRecord, lines: VentilationLine[]): jsPD
const pageW = 210; const pageW = 210;
const pageH = 297; const pageH = 297;
const margin = 14; const margin = 14;
const headerEndY = 33; // Y après l'en-tête
const footerH = 8; // marge basse
// En-tête // En-tête compact
doc.setFontSize(14); doc.setFontSize(13);
doc.setFont("helvetica", "bold"); doc.setFont("helvetica", "bold");
doc.text("Ventilation facture FREE PRO", pageW / 2, 14, { align: "center" }); doc.text("Ventilation facture FREE PRO", pageW / 2, 12, { align: "center" });
// Date du mois // Date du mois
const [moisStr, anneeStr] = importRecord.moisLabel.split("/"); const [moisStr, anneeStr] = importRecord.moisLabel.split("/");
const dateLabel = `01/${moisStr}/${anneeStr}`; const dateLabel = `01/${moisStr}/${anneeStr}`;
doc.setFontSize(11); doc.setFontSize(10);
doc.text(dateLabel, pageW / 2, 21, { align: "center" }); doc.text(dateLabel, pageW / 2, 19, { align: "center" });
// Date d'édition (haut droite) // Date d'édition (haut droite)
const now = new Date(); const now = new Date();
const editDate = `Edite le ${now.toLocaleDateString("fr-FR")} - ${now.toLocaleTimeString("fr-FR", { hour: "2-digit", minute: "2-digit" })}`; const editDate = `Edite le ${now.toLocaleDateString("fr-FR")} - ${now.toLocaleTimeString("fr-FR", { hour: "2-digit", minute: "2-digit" })}`;
doc.setFontSize(8); doc.setFontSize(7);
doc.setFont("helvetica", "normal"); doc.setFont("helvetica", "normal");
doc.text(editDate, pageW - margin, 9, { align: "right" }); doc.text(editDate, pageW - margin, 8, { align: "right" });
// Référence pièce // Référence pièce
doc.setFontSize(9); doc.setFontSize(8);
doc.setFont("helvetica", "bold"); doc.setFont("helvetica", "bold");
doc.text("ref_piece :", margin, 29); doc.text("ref_piece :", margin, 26);
doc.setFont("helvetica", "normal"); doc.setFont("helvetica", "normal");
doc.text(importRecord.refPiece ?? "", margin + 25, 29); doc.text(importRecord.refPiece ?? "", margin + 22, 26);
// Données tableau // Données tableau
const tableData = lines.map((l) => [ const tableData = lines.map((l) => [
@@ -140,37 +142,48 @@ function buildPdfDoc(importRecord: ImportRecord, lines: VentilationLine[]): jsPD
const colType = usableW * 0.32; const colType = usableW * 0.32;
const colMontant = usableW * 0.23; const colMontant = usableW * 0.23;
// Taille de police dynamique pour tenir sur 1 page // Calcul précis pour tenir sur 1 page :
// nbRows = lignes données + 1 header + 1 footer
const nbRows = lines.length + 2; const nbRows = lines.length + 2;
const availH = pageH - 38 - 15; // Hauteur disponible entre fin d'en-tête et bas de page
const rowH = Math.min(8, Math.floor(availH / nbRows)); const availH = pageH - headerEndY - footerH;
const fontSize = rowH >= 7 ? 9 : rowH >= 6 ? 8 : rowH >= 5 ? 7 : 6; // Hauteur max par cellule (en mm) pour tenir en 1 page
const cellPad = rowH >= 7 ? 2 : 1.2; const maxCellH = availH / nbRows;
// Padding : 1/4 de la hauteur de cellule, min 0.8, max 2.5
const cellPad = Math.max(0.8, Math.min(2.5, maxCellH * 0.25));
// Font size : basé sur hauteur de cellule disponible pour le texte
const textH = maxCellH - cellPad * 2;
// 1pt ≈ 0.353mm ; on vise textH mm de hauteur de texte
const fontSize = Math.max(5, Math.min(9, Math.floor(textH / 0.353)));
autoTable(doc, { autoTable(doc, {
startY: 33, startY: headerEndY,
head: [["Structure", "Type", "Montant TTC"]], head: [["Structure", "Type", "Montant TTC"]],
body: tableData, body: tableData,
foot: [["Total general", "", formatMontantPdf(totalCentimes)]], foot: [["Total general", "", formatMontantPdf(totalCentimes)]],
styles: { fontSize, cellPadding: cellPad, overflow: "linebreak" }, styles: { fontSize, cellPadding: cellPad, overflow: "ellipsize", minCellHeight: 0 },
headStyles: { headStyles: {
fillColor: [255, 255, 255], textColor: [0, 0, 0], fillColor: [255, 255, 255], textColor: [0, 0, 0],
fontStyle: "bold", lineWidth: 0.3, lineColor: [0, 0, 0], fontStyle: "bold", lineWidth: 0.3, lineColor: [0, 0, 0],
minCellHeight: 0,
}, },
footStyles: { footStyles: {
fillColor: [255, 255, 255], textColor: [0, 0, 0], fillColor: [255, 255, 255], textColor: [0, 0, 0],
fontStyle: "bold", lineWidth: 0.3, lineColor: [0, 0, 0], fontStyle: "bold", lineWidth: 0.3, lineColor: [0, 0, 0],
minCellHeight: 0,
}, },
bodyStyles: { lineWidth: 0.1, lineColor: [180, 180, 180] }, bodyStyles: { lineWidth: 0.1, lineColor: [180, 180, 180], minCellHeight: 0 },
columnStyles: { columnStyles: {
0: { cellWidth: colStructure }, 0: { cellWidth: colStructure },
1: { cellWidth: colType }, 1: { cellWidth: colType },
2: { cellWidth: colMontant, halign: "right" }, 2: { cellWidth: colMontant, halign: "right" },
}, },
alternateRowStyles: { fillColor: [248, 248, 248] }, alternateRowStyles: { fillColor: [248, 248, 248] },
// Ne pas autoriser de saut de page
pageBreak: "avoid", pageBreak: "avoid",
rowPageBreak: "avoid", rowPageBreak: "avoid",
margin: { left: margin, right: margin }, showFoot: "lastPage",
margin: { left: margin, right: margin, bottom: footerH },
tableWidth: usableW, tableWidth: usableW,
}); });