Tutoriel : créer un fichier PDF avec FPDF
Encore un billet en rapport avec un tutoriel. Mais cette fois-ci, c'est un brin plus compliqué à mettre en place. Sans vous livrer tous mes secrets de fabrication, je vais vous livrer quelques bases pour construire un fichier PDF à partir d'une base de données.
Utilisation de la librairie FPDF
Pour ce tutoriel, nous allons avoir besoin de la librairie FPDF. Grâce aux fonctions de base, nous allons pouvoir construire de nombreux types de documents. Pour ce tutoriel, je vais vous proposer un mélange d'informations afin d'utiliser au maximum les possibilités de FPDF. Uploadez la librairie FPDF sur votre serveur.
C'est parti ! Nous devons créer un fichier nommé pdf.php par exemple que l'on commence à remplir comme suit :
<?php // Connexion à la BDD $bddname = 'nom_de_la_base'; $hostname = 'localhost'; $username = 'utilisateur'; $password = 'mot_de_passe'; $db = mysqli_connect ($hostname, $username, $password, $bddname); // Appel de la librairie FPDF require("fpdf/fpdf.php");
A partir de là, nous pouvons commencer à afficher des données. Pour info, si votre base de données est en UTF-8, vous devrez utiliser la fonction utf8_decode pour tous les champs contenant du texte. Rien de bien sorcier, mais il faut y penser pour éviter les caractères exotiques.
Commençons par afficher un logo en header et les coordonnées en footer. Tout le code qui suit sera à ajouter au précédent. Ça m'évitera d'avoir des lignes de code trop longues. :)
// Création de la class PDF class PDF extends FPDF { // Header function Header() { // Logo $this->Image('images/logo-infiniblog.jpg',8,2,80); // Saut de ligne $this->Ln(20); } // Footer function Footer() { // Positionnement à 1,5 cm du bas $this->SetY(-15); // Adresse $this->Cell(196,5,'Mes coordonnées - Mon téléphone',0,0,'C'); } }
Je vous ai perdu ? Ne partez pas, je vous explique. Pour le header, je souhaite afficher un logo. J'appelle donc mon image en indiquant le chemin. Le chiffre 8 correspond à la position à gauche du document, le 2 à la position en haut du document et le 80 à la largeur de l'image. La hauteur est calculée automatiquement.
Pour le footer, j'utilise la fonction Cell pour créer une ligne. Le chiffre 196 correspond à la largeur de la ligne et le chiffre 5 à la hauteur. Ensuite j'écris mon texte (ne pas oublier d'échapper les apostrophes). Le premier 0 indique que je ne souhaite pas de bordure, le second qu'il ne doit pas y avoir de retour à la ligne et le C indique que je souhaite centrer mon texte.
Avant d'afficher le PDF, il faut activer la classe et définir quelques paramètres tels que le format du PDF (portrait, en mm et A4) la police et sa taille, ainsi que la couleur du texte par défaut. La fonction AddPage créée le document et permet d'afficher notamment le logo et les coordonnées en pied de page.
// Activation de la classe $pdf = new PDF('P','mm','A4'); $pdf->AddPage(); $pdf->SetFont('Helvetica','',11); $pdf->SetTextColor(0);
Voilà concrètement ce que donne l'affichage des ces informations : voir le PDF.
Construire le document depuis la base de données
Nous partons du principe que nous possédons une base de données avec plusieurs tables. Pour l'exemple, je dispose de données clients et de données commandes.
Commençons par afficher deux blocs d'informations. Celui de la commande et les coordonnées du client.
$req = "SELECT id, id_client, date_com, reglement FROM table_commandes WHERE id=".$_GET['id']; $rep = mysqli_query($db, $req); $row = mysqli_fetch_array($rep); // Infos de la commande calées à gauche $pdf->Text(8,38,'N° de facture : '.$row['id']); $pdf->Text(8,43,'Date : '.$row['date_com']); $pdf->Text(8,48,'Mode de règlement : '.$row['reglement']);
Pour la fonction Text, les deux chiffres correspondent à la position gauche puis la position haut pour le placement des informations. Donc on cale ces infos à 8mm de la gauche et 43mm du haut du PDF pour la première ligne. On ajoute 5mm par ligne pour caler les infos les unes en dessous des autres.
$req1 = "SELECT nom, prenom, adresse, code_postal, ville FROM table_clients WHERE id=".$row['id_client']; $rep1 = mysqli_query($db, $req1); $row1 = mysqli_fetch_array($rep1); // Infos du client calées à droite $pdf->Text(120,38,utf8_decode($row1['prenom']).' '.utf8_decode($row1['nom'])); $pdf->Text(120,43,utf8_decode($row1['adresse'])); $pdf->Text(120,48,$row1['code_postal'].' '.utf8_decode($row1['ville']));
Pour afficher le bloc adresse à droite, j'ai remplacé le premier chiffre 8 par 120. A vous de peaufiner le calage selon vos besoins. Voyons ce que donne le PDF avec ces informations supplémentaires : voir le PDF.
Construction d'un tableau
La ça va nettement se compliquer puisque l'on va construire un tableau pour afficher les lignes de la commande avec un entête doté d'une couleur en background. Qui dit tableau, dit multi-colonnes et filets pour délimiter l'ensemble. Vous pouvez au choix prendre un Doliprane ou vous faire un café. ;)
On commence par créer l'entête du tableau avec quelques paramètres et une fonction dédiée selon le code suit :
// Position de l'entête à 10mm des infos (48 + 10) $position_entete = 58; function entete_table($position_entete){ global $pdf; $pdf->SetDrawColor(183); // Couleur du fond $pdf->SetFillColor(221); // Couleur des filets $pdf->SetTextColor(0); // Couleur du texte $pdf->SetY($position_entete); $pdf->SetX(8); $pdf->Cell(158,8,'Désignation',1,0,'L',1); $pdf->SetX(166); // 8 + 96 $pdf->Cell(10,8,'Qté',1,0,'C',1); $pdf->SetX(176); // 104 + 10 $pdf->Cell(24,8,'Net HT',1,0,'C',1); $pdf->Ln(); // Retour à la ligne } entete_table($position_entete);
Le premier $pdf->SetX(8) est la position de la première colonne, soit 8mm de la gauche. La valeur 158 correspond à la largeur de la colonne, le 8 à la hauteur. Ensuite le libellé de la colonne, le 1 pour indiquer que l'on souhaite un filet de 1mm, le 0 pour le saut de ligne, le C pour centrer le texte et le 1 pour afficher la couleur de fond.
Vous suivez toujours ? Donc si c'est OK, nous allons afficher les détails de la commande. Pour ce faire, nous allons utiliser la fonction MultiCell, pour créer plusieurs lignes comme son nom l'indique. ;)
// Liste des détails $position_detail = 66; // Position à 8mm de l'entête $req2 = "SELECT libelle, qte, prix_ht FROM table_details WHERE id_commande=1"; $rep2 = mysqli_query($db, $req2); while ($row2 = mysqli_fetch_array($rep2)) { $pdf->SetY($position_detail); $pdf->SetX(8); $pdf->MultiCell(158,8,utf8_decode($row2['libelle']),1,'L'); $pdf->SetY($position_detail); $pdf->SetX(166); $pdf->MultiCell(10,8,$row2['qte'],1,'C'); $pdf->SetY($position_detail); $pdf->SetX(176); $pdf->MultiCell(24,8,$row2['prix_ht'],1,'R'); $position_detail += 8; }
Comme vous le voyez, on appelle les détails de la commande en utilisant while. Comme cela, le script remontera toutes les lignes en rapport avec notre commande. Pour les chiffres, nous avons indiqué les mêmes que l'entête afin de caler les détails sur l'entête. Le 1 et le C, L ou R (centre, left ou right) correspondent à l'affichage du filet et à la position de l'info dans la colonne.
La petite nuance concerne la dernière ligne. Puisque nous sommes dans un while, il faut préciser que chaque nouvelle ligne est positionnée à 8mm en dessous de la précédente.
Il faut penser à donner un nom à votre document PDF lorsque vous souhaitez le télécharger et fermer le document pdf.php :
// Nom du fichier $nom = 'Facture-'.$row['id'].'.pdf'; // Création du PDF $pdf->Output($nom,'I');?>
Nous obtenons un superbe tableau en seulement quelques lignes de code : voir le PDF.
Pour appeler le fichier, un simple lien suffit :
<a href='lien-du-fichier.pdf'>Fichier PDF</a>
Voilà c'est fini
Ce tutoriel est à présent terminé. Je vous laisse réfléchir pour afficher d'autres informations comme le total de la facture, une ligne avec un RIB par exemple en cas de paiement par virement ou encore les informations obligatoires à faire apparaître sur une facture (taux de pénalité, indemnité forfaitaire, etc.). Il est tout à fait possible de créer des documents très intéressantes dès que l'on fait travailler ses neurones.
Une fois que vous maîtriserez ce code, il suffit de 15/20 minutes maxi pour monter le PDF d'une facture. Par contre, pour d'autres documents, faites couler une cafetière, préparez un paquet de gâteaux et armez vous de patience.
Pour la petite info, il m'est arrivé de bosser 5/6 jours sur un même document. Notamment lorsque vous avez une trame en background et qu'il faut placer des données dans des cases comme le code postal par exemple. Il faut décomposer la donnée pour placer chaque chiffre dans les 5 cases prévues à cet effet. Je ne vous dis pas la prise de tête parfois. :)
22 réflexions sur ce billet
Le 10/12/2014 à 02h39
Sinon il existe des librairies HTML to PDF (html2pdf, dompdf). Tellement plus pratique.
Le 23/04/2015 à 03h19
Merci beaucoup :D
Le 02/09/2015 à 18h49
Merci beaucoup ! Votre tutoriel ma vraiment sauvé. Dieu vous bénisse.
Le 06/12/2015 à 23h10
Vraiment un grand merci pour la clarté de ces explications. Bravo.
Le 01/02/2016 à 00h36
Merci.
Petite question : comment imprimer la bonne hauteur du tableau par rapport au besoin de lignes de la facture ?
Le 21/05/2018 à 20h40
Salut et merci beaucoup pour ce bon cours. Mais j'ai encore un soucis, comment appeler le fichier pdf ?
Le 22/05/2018 à 10h37
@Manequin
J'ai ajouté l'explication dans l'article. Merci de me l'avoir signalé. ;)
Le 05/09/2018 à 08h16
Merci beaucoup, c'est clair et détaillé.
Bravo pour ces explications.
Le 17/03/2019 à 00h16
Bonjour,
Merci pour ton travail. J'ai un soucis. Quand je suis dans le dossier fpdf le pdf s'affiche mais depuis un autre repertoire ça ne marche pas. Quelqu'un aurait une idée ?
Merci
Le 18/03/2019 à 15h39
J'ai un tableau dont je connais le nombre de colonnes. Je n'ai pas utilisé de while mais quand j'ai fini d'entrer mes informations, je n'arrive pas à clôturer. Les lignes verticales perdurent jusqu'à la fin de la page et je n'ai pas la ligne verticale qui clôture le tableau.
Le 19/03/2019 à 10h54
Awizonon
Si tu changes le nom du répertoire, il faut t'assurer de le changer aussi à l'intérieur des fichiers du dossier FPDF.
Commences par faire cette vérification. ;)
Le 19/03/2019 à 10h56
Fred
As-tu regardé dans les logs pour vérifier s'il y avait une erreur ?
Le 13/05/2019 à 01h06
Génial, tout fonctionne, merci infiniment !
Le 21/05/2019 à 16h14
Je tiens à vous remercier pour ce tutoriel car il va beaucoup m'aider.
Le 19/08/2020 à 08h59
Bonjour !
Quelle magnifique tutoriel et une explication sublime. Grâce à vous j'ai très bien compris cette bibliothèque fpdf.
Le 20/01/2021 à 10h20
Bonjour, j'ai parfaitement sû faire ce tableau, mais la, j'aimerai générer un document PDF non complexe, sans tableau.
Le 20/01/2021 à 12h23
Jonathan
Pour des PDF plutôt simples, il existe des tutoriels sur le site http://www.fpdf.org/
Le 04/03/2022 à 17h37
Bonjour Hervé,
Bon j'arrive 8 ans après la bataille, mais votre tuto me sauve la vie !
j'ai une question tout de même, tout votre code fonctionne chez moi j'ai même pu adapter sans soucis à partir de ce que vous avez mis, mais je reste bloqué sur un point les "é" de Désignation et Qté m'apraissent comme ça : "é" une idée d'où ca peut provenir svp ?
Le 04/03/2022 à 17h45
Bonjour Gildas
Effectivement, 8 ans ça fait un bail. Et quand je vois le code, ça a bien évolué. :-)
Aurais-tu un charset (utf-8 ou autre) qui traînerait quelque part dans le fichier ou sur les identifiants de connexion à la BDD ?
Je ne vois que ça.
Le 04/03/2022 à 17h50
merci de ton retour rapide,
effectivement dans ma connexion à, la bdd array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8') , (ma connexion ce fait via PDO) .
mais même en l'enlevant ca ne fonctionne pas , et mise à part les utf8_decode, j'ai pas d'autres utf_8
Le 04/03/2022 à 17h58
Gildas
Tu n'appelles pas de fichier externe via un include ou require autre que la librairie FPDF ?
J'ai déjà eu le cas, à part chercher ce qui cloche, je ne vois pas.
Tu peux jeter un coup d'œil ici dans la rubrique FAQ (sur la gauche). Le point 3 en parle : http://www.fpdf.org/
Le 04/03/2022 à 18h00
Nop j'ai que le require('fpdf.php) ;
Mais pas grave je les aient mis en majuscule ca sera très bien ,
Merci pour ton aide
La création de site Internet sur mesure est le coeur de métier de l'agence web Infini'click.
D'autres prestations sont également proposées, découvrez-les !
- Tutoriel : installer un serveur Debian (wheezy)
- Tutoriel : Installer un certificat SSL
- Piwik : monitorez tous vos sites discrètement
- Tutoriel Paypal : Intégration et configuration du paiement en ligne
- Tutoriel PayPlug : intégration au panier d'achat
- Référencement des images : l'état de l'art
- Des onglets en CSS avec JQuery
- Gildas le 04/03/2022 à 18:00
Nop j'ai que le require('fpdf.php) ; Mais pas... - Hervé le 04/03/2022 à 17:58
Gildas Tu n'appelles pas de fichier externe via un... - Gildas le 04/03/2022 à 17:50
merci de ton retour rapide, effectivement dans ma... - Hervé le 04/03/2022 à 17:45
Bonjour Gildas Effectivement, 8 ans ça fait un bail.... - Gildas le 04/03/2022 à 17:37
Bonjour Hervé, Bon j'arrive 8 ans après la... - Olivier le 16/01/2022 à 23:09
C'est clair que les actions réalisées ne correspondent...