From 44186a056b7b9ccb34c1c12d6ea20b1662b20a26 Mon Sep 17 00:00:00 2001 From: Manus Date: Fri, 5 Jun 2026 10:50:00 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20PDF=20FreePro=201=20page=20A4=20portrait?= =?UTF-8?q?=20-=20calcul=20pr=C3=A9cis=20hauteur=20cellule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/VentilationFreePro.tsx | 51 ++++++++++++++++--------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/client/src/pages/VentilationFreePro.tsx b/client/src/pages/VentilationFreePro.tsx index 775d7ee..5989bc4 100644 --- a/client/src/pages/VentilationFreePro.tsx +++ b/client/src/pages/VentilationFreePro.tsx @@ -100,31 +100,33 @@ function buildPdfDoc(importRecord: ImportRecord, lines: VentilationLine[]): jsPD const pageW = 210; const pageH = 297; const margin = 14; + const headerEndY = 33; // Y après l'en-tête + const footerH = 8; // marge basse - // En-tête - doc.setFontSize(14); + // En-tête compact + doc.setFontSize(13); 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 const [moisStr, anneeStr] = importRecord.moisLabel.split("/"); const dateLabel = `01/${moisStr}/${anneeStr}`; - doc.setFontSize(11); - doc.text(dateLabel, pageW / 2, 21, { align: "center" }); + doc.setFontSize(10); + doc.text(dateLabel, pageW / 2, 19, { align: "center" }); // Date d'édition (haut droite) const now = new Date(); 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.text(editDate, pageW - margin, 9, { align: "right" }); + doc.text(editDate, pageW - margin, 8, { align: "right" }); // Référence pièce - doc.setFontSize(9); + doc.setFontSize(8); doc.setFont("helvetica", "bold"); - doc.text("ref_piece :", margin, 29); + doc.text("ref_piece :", margin, 26); doc.setFont("helvetica", "normal"); - doc.text(importRecord.refPiece ?? "", margin + 25, 29); + doc.text(importRecord.refPiece ?? "", margin + 22, 26); // Données tableau const tableData = lines.map((l) => [ @@ -140,37 +142,48 @@ function buildPdfDoc(importRecord: ImportRecord, lines: VentilationLine[]): jsPD const colType = usableW * 0.32; 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 availH = pageH - 38 - 15; - const rowH = Math.min(8, Math.floor(availH / nbRows)); - const fontSize = rowH >= 7 ? 9 : rowH >= 6 ? 8 : rowH >= 5 ? 7 : 6; - const cellPad = rowH >= 7 ? 2 : 1.2; + // Hauteur disponible entre fin d'en-tête et bas de page + const availH = pageH - headerEndY - footerH; + // Hauteur max par cellule (en mm) pour tenir en 1 page + 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, { - startY: 33, + startY: headerEndY, head: [["Structure", "Type", "Montant TTC"]], body: tableData, foot: [["Total general", "", formatMontantPdf(totalCentimes)]], - styles: { fontSize, cellPadding: cellPad, overflow: "linebreak" }, + styles: { fontSize, cellPadding: cellPad, overflow: "ellipsize", minCellHeight: 0 }, headStyles: { fillColor: [255, 255, 255], textColor: [0, 0, 0], fontStyle: "bold", lineWidth: 0.3, lineColor: [0, 0, 0], + minCellHeight: 0, }, footStyles: { fillColor: [255, 255, 255], textColor: [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: { 0: { cellWidth: colStructure }, 1: { cellWidth: colType }, 2: { cellWidth: colMontant, halign: "right" }, }, alternateRowStyles: { fillColor: [248, 248, 248] }, + // Ne pas autoriser de saut de page pageBreak: "avoid", rowPageBreak: "avoid", - margin: { left: margin, right: margin }, + showFoot: "lastPage", + margin: { left: margin, right: margin, bottom: footerH }, tableWidth: usableW, });