picture picture
décembre 20, 2019 PHP 17 Commentaires

[Tuto] Créer un PDF en PHP avec FPDF

Petit tutoriel FPDF pour la créer un fichier PDF en PHP avec données extraites d'une base des données, et incluant un tableau: cela suffit dans 95% des cas.

Créer un PDF dynamiquement avec PHP est un travail long, je dirais presque fastidieux…

La librairie gratuite FPDF simplifie sensiblement cette tâche, sans pour autant la rendre totalement intuitive. Concrètement, l’affichage des données s’apparente à un dessin créé avec les chaîne de caractères à afficher : tout doit être positionné à l’avance, il n’y aura dans la plupart des cas pas de retour à la ligne automatique (ni de redimensionnement selon la taille des écrans puisqu’il s’agit d’un format d’impression)

Voici un petit tutoriel FPDF de base, pour la créer un fichier PDF avec des données extraites d’une base des données et incluant un tableau : cela suffira à couvrir 95% des besoins…

Étape 1 : installer la librairie FPDF

Télécharger la librairie sur le site de FPDF > choisissez la version la plus récente au format ZIP.
Après extraction des fichiers, mettez les tous dans un dossier nommé fpdf par exemple, créé sur la racine de votre serveur (vous adapterez ce chemin pour la suite du tutoriel s’il est différent)

Étape 2 : connecter le fichier de création à la base des données

Créez un fichier nommé par exemple test.php (vous pourrez l’appeler dans un autre fichier si vous le souhaitez avec la fonction require_once())
Dans ce fichier, puisque nous partons du principe que les données sont extraites d’une base des données, vous allez commencer par vous connecter à la base, puis à la librairie FPDF elle-même.

Dans cet exemple, nous allons créer un PDF affichant le nombre de repas que chaque voyageur d’une agence de voyage aura pris par ville et par pays (c’est un voyage gastronomique ;) )

<?php
// Connexion à la BDD (à personnaliser)
$link = mysqli_connect('localhost','login','mot_de_passe','nom_base');
// Si base de données en UTF-8, il faudra utiliser la fonction utf8_decode() pour tous les champs de texte à afficher

// extraction des données à afficher dans le sous-titre (nom du voyageur et dates de son voyage)
$requete = "SELECT * FROM voyageur WHERE id_voyageur='1'";
$result = mysqli_query($link, $requete);
// tableau des résultats de la ligne > $data_voyageur['nom_champ']
$data_voyageur = mysqli_fetch_array($result);
mysqli_free_result($result);

// Appel de la librairie FPDF
require("fpdf/fpdf.php");
									

Étape 3 : créer la classe FPDF, avec une en-tête et un pied de page

Dans cette classe, et pour cet exemple, on va définir une en-tête incluant un logo, un titre commun à toutes les pages, et un pied de page avec affichage d’un compteur de pages. Cette étape est facultatives si vous ne souhaitez ni en-tête ni pied de page. Toutes les valeurs de positionnement sont en mm (par défaut)
Les explications sont en commentaires : pour plus de détails voir les tutoriels du site FPDF ou le liste des fonctions

// Création de la class PDF
class PDF extends FPDF {
  // Header
  function Header() {
    // Logo : 8 >position à gauche du document (en mm), 2 >position en haut du document, 80 >largeur de l'image en mm). La hauteur est calculée automatiquement.
    $this->Image('logo_agence.png',8,2);
    // Saut de ligne 20 mm
    $this->Ln(20);

    // Titre gras (B) police Helbetica de 11
    $this->SetFont('Helvetica','B',11);
    // fond de couleur gris (valeurs en RGB)
    $this->setFillColor(230,230,230);
     // position du coin supérieur gauche par rapport à la marge gauche (mm)
    $this->SetX(70);
    // Texte : 60 >largeur ligne, 8 >hauteur ligne. Premier 0 >pas de bordure, 1 >retour à la ligneensuite, C >centrer texte, 1> couleur de fond ok  
    $this->Cell(60,8,'VOYAGE GASTRO',0,1,'C',1);
    // Saut de ligne 10 mm
    $this->Ln(10);    
  }
  // Footer
  function Footer() {
    // Positionnement à 1,5 cm du bas
    $this->SetY(-15);
    // Police Arial italique 8
    $this->SetFont('Helvetica','I',9);
    // Numéro de page, centré (C)
    $this->Cell(0,10,'Page '.$this->PageNo().'/{nb}',0,0,'C');
  }
}
									

Étape 4 : créer la première page avec les caractères par défaut

Il est possible de choisir si la page est au format portrait (P) ou paysage (L > Landscape), si les données sont affichées en mm ou en pixels, et le format d’impression final. On aurait pu par exemple demander une page A5 au format paysage et avec des calculs en points…

// On active la classe une fois pour toutes les pages suivantes
// Format portrait (>P) ou paysage (>L), en mm (ou en points > pts), A4 (ou A5, etc.)
$pdf = new PDF('P','mm','A4');

// Nouvelle page A4 (incluant ici logo, titre et pied de page)
$pdf->AddPage();
// Polices par défaut : Helvetica taille 9
$pdf->SetFont('Helvetica','',9);
// Couleur par défaut : noir
$pdf->SetTextColor(0);
// Compteur de pages {nb}
$pdf->AliasNbPages();
									

Étape 5 : afficher un sous-titre de page calé à gauche et encadré

Dans cet exemple, il y a 2 lignes de sous-titre : une avec la dates du voyage, l’autre, avec un nom-prénom du voyageur (précédemment extraits de la base à l’étape 1).
Chaque chaîne est insérée dans un cellule d’une seule ligne : Cell(). Par défaut cette cellule est positionnée à gauche :
– les 2 premiers chiffres correspondent à la largeur et la hauteur de la cellule (ne pas oublier qu’une page A4 fait 210 mm de large, moins les marges : testez !)
– ensuite vient le texte à afficher décodée en UTF8 (cette étape dépend de l’encodage de votre base des données et de celui de la page)
– ensuite la présence (1) ou non (0) d’une bordure autour de la cellule. On peut remplacer le « 1 » par les positions de la bordure éventuelle (L>gauche, T>haut, R>droite, B>bas) : par ex. « LR » pour un encadrement sur les bords droite et gauche. L’ordre de déclaration est indifférent.
– ensuite le positionnement de la cellule suivante (valeur facultative : mettez 1 pour positionner le curseur au début de la ligne suivante.
– ensuite l’alignement du texte (L>à gauche, C>centré, R>à droite)
– ensuite le remplissage (1) ou non (0) de la cellule par une couleur de fond (ou rien, s’il n’y a pas de background déclaré précédemment)
Quand une valeur est omise ou égale à zéro, elle prend la valeur par défaut (texte à gauche sur fond transparent et sans retour chariot ensuite)


Vous pouvez aussi utiliser la fonction Text() : personnellement je l’utilise peu mais elle est équivalente.

// Sous-titre calées à gauche, texte gras (Bold), police de caractère 11
$pdf->SetFont('Helvetica','B',11);
// couleur de fond de la cellule : gris clair
$pdf->setFillColor(230,230,230);
// Cellule avec les données du sous-titre sur 2 lignes, pas de bordure mais couleur de fond grise
$pdf->Cell(75,6,'DU '.$data_voyageur['date_deb'].' AU '.$data_voyageur['date_fin'],0,1,'L',1);    
$pdf->Cell(75,6,strtoupper(utf8_decode($data_voyageur['prenom'].' '.$data_voyageur['nom'])),0,1,'L',1);        
$pdf->Ln(10); // saut de ligne 10mm
									

Étape 6 : créer la fonction d’affichage de l’en-tête d’un tableau

Un petit peu plus de calculs de positions : c’est juste du dessin, avec des abscisses (X) et des ordonnées (Y) !
$pdf->SetX(xx) déclare que le coin supérieur gauche du tableau va être à xx mm de la marge de la page (abscisse).
On positionne donc les cellules d’en-tête les unes à côté des autres, en calculant chaque fois la nouvelle position xx de son coin supérieur gauche (on additionne aux largeurs des cellules précédentes). Chaque cellule peut avoir une largeur différente de ses voisines, déclarée dans la fonction Cell() comme vu précédemment.

// Fonction en-tête des tableaux en 3 colonnes de largeurs variables
function entete_table($position_entete) {
  global $pdf;
  $pdf->SetDrawColor(183); // Couleur du fond RVB
  $pdf->SetFillColor(221); // Couleur des filets RVB
  $pdf->SetTextColor(0); // Couleur du texte noir
  $pdf->SetY($position_entete);
  // position de colonne 1 (10mm à gauche)  
  $pdf->SetX(10);
  $pdf->Cell(60,8,'Ville',1,0,'C',1);  // 60 >largeur colonne, 8 >hauteur colonne
  // position de la colonne 2 (70 = 10+60)
  $pdf->SetX(70); 
  $pdf->Cell(60,8,'Pays',1,0,'C',1);
  // position de la colonne 3 (130 = 70+60)
  $pdf->SetX(130); 
  $pdf->Cell(30,8,'Repas',1,0,'C',1);

  $pdf->Ln(); // Retour à la ligne
}
// AFFICHAGE EN-TÊTE DU TABLEAU
// Position ordonnée de l'entête en valeur absolue par rapport au sommet de la page (70 mm)
$position_entete = 70;
// police des caractères
$pdf->SetFont('Helvetica','',9);
$pdf->SetTextColor(0);
// on affiche les en-têtes du tableau
entete_table($position_entete);
									

Étape 7 : remplir dynamiquement les lignes du tableau

Il s’agit d’une simple extraction en boucle d’une base MySQL. Les valeurs extraites s’affichent dans des cellules avec la fonction MultiCell() qui supportent les retour à la ligne automatique, puisque par défaut on ignore la longueur des chaînes variables extraites.
Cette fonction a à peu près le même fonctionnement que la fonction Cell() :
largeur > hauteur > texte > bordure > alignement > remplissage.
La seule nouveauté est l’apparition d’une fonction SetY() qui permet de positionner le coin supérieur gauche de chaque ligne de cellule, non seulement par rapport à la marge de gauche (SetX : abscisse), mais également par rapport à la marge du haut de page (SetY : ordonnée).

$position_detail = 78; // Position ordonnée = $position_entete+hauteur de la cellule d'en-tête (60+8)
$requete2 = "SELECT * FROM gastro WHERE id_voyageur='1'";
$result2 = mysqli_query($link, $requete2);
while ($data_visit = mysqli_fetch_array($result2)) {
  // position abcisse de la colonne 1 (10mm du bord)
  $pdf->SetY($position_detail);
  $pdf->SetX(10);
  $pdf->MultiCell(60,8,utf8_decode($data_visit['ville']),1,'C');
    // position abcisse de la colonne 2 (70 = 10 + 60)  
  $pdf->SetY($position_detail);
  $pdf->SetX(70); 
  $pdf->MultiCell(60,8,utf8_decode($data_visit['pays']),1,'C');
  // position abcisse de la colonne 3 (130 = 70+ 60)
  $pdf->SetY($position_detail);
  $pdf->SetX(130); 
  $pdf->MultiCell(30,8,$data_visit['nb_repas'],1,'C');

  // on incrémente la position ordonnée de la ligne suivante (+8mm = hauteur des cellules)  
  $position_detail += 8; 
}
mysqli_free_result($result2);
									

Étape 8 : créer une nouvelle page

Aucune déclaration n’est nécessaire pour terminer une page. Le footer s’insère automatiquement en bas de page s’il a été déclaré.
Mais ATTENTION ! C’est à vous de calculer le bon nombre de lignes pour que le flux d’extraction ne dépassent pas la hauteur de la page !

S’il est nécessaire de créer une nouvelle page, c’est très simple : il vous suffira d’ajouter ensuite dans le code une fonction AddPage() :
elle a pour effet d’insérer une nouvelle en-tête, un nouveau footer (avec un compteur de page incrémenté si vous en avez inséré un), et de positionner la curseur en haut à droite de la nouvelle page. Vous pouvez si vous le souhaitez modifier les caractéristiques des polices de caractères de cette page, sinon ce sont les valeurs de l’en-tête qui prévalent.

// Nouvelle page PDF
$pdf->AddPage();
// Polices par défaut : Helvetica taille 9
$pdf->SetFont('Helvetica','',11);
// Couleur par défaut : noir
$pdf->SetTextColor(0);
// Compteur de pages {nb}
$pdf->AliasNbPages();
$pdf->Cell(500,20,utf8_decode('Plus rien à vous dire ;-)'));
									

Étape 9 : exporter le PDF

2 solutions :
1. Soit vous voulez afficher/exporter le PDF (utile en phase de création et de test). En ce cas vous utilisez la fonction output() avec :
– le premier argument qui donne le nom du fichier PDF,
– le second argument qui donne l’ordre d’afficher sur le navigateur internet (I >Inline), ou de forcer le téléchargement du fichier (D > Download)

 // affichage à l'écran...
$pdf->Output('test.pdf','I');
									

2. Soit vous souhaitez stocker ce fichier PDF sur votre serveur (pour l’envoyer dans un second temps par e-mail en pièce jointe, par exemple)
En ce cas vous utiliserez également la fonction output(), mais avec :
– comme premier argument F (> File),
– et comme second argument le chemin de destination du fichier (plus besoin de préciser le nom du fichier, puisqu’il apparait sur cette chaîne) :

 // ...ou export sur le serveur dans un dossier "fic"
$pdf->Output('F', '../fic/test.pdf');
?>
									

Afficher le résultat

Cliquez ici pour afficher le PDF à l’écran

Télécharger les fichiers de ce tutoriel

Cliquez ici pour télécharger tous les fichiers de ce tutoriel

Et les images ?

Parce qu’une bonne image, justement, vaut mieux qu’un long discours !

source : https://www.plus2net.com/

That’s all folk ! Bon courage :)

17 Responses to “[Tuto] Créer un PDF en PHP avec FPDF”

17 Commentaires

  1. webtolosa dit :

    @jean Matanta Kitengejean : euh… non. Ça ne se passe pas comme ça. Vous suivez le tuto, et ensuite, vous dites sur quel point précis vous bloquez (avec le message d’erreur ») !

  2. Ir Jean MK dit :

    Quelqu’un peut m’aider ?

  3. webtolosa dit :

    @Tom : Lorsque tu crées ta page, tu détermines sa longueur : elle est fixe (ici, c’est du A4). Il te faut donc l’évaluer avant de créer chaque page : tu peux faire, par exemple, une petite fonction qui va calculer la longueur de ta page selon la quantité de caractères ou de lignes que tu vas extraire de ta base pour chaque page, puis insérer la variable résultante à la place de ‘A4’ :
    $pdf = new PDF(‘P’,’mm’,’A4′); va devenir : $pdf = new PDF(‘P’,’mm’,$longueur);

  4. Thom dit :

    Bonjour!
    Merci pour le tuto,
    Dans mon case je veux redimensionner
    La lageur et la couper a la longueur du contenu

  5. Dan-Emmanuel dit :

    merci beaucoup pour ce tuto il marche super bien!!!

  6. webtolosa dit :

    Il y a tout ce qu’il faut sur ce tuto : tu extrais en boucles et tu affiches un tuple par page…

  7. Michael dit :

    Bonjour, j’ai une base de données contenant des informations sur des clients. J’aimerais générer un pdf dont sur chaque page je mettre des informations concernant chaque client. Merci d’avance.

  8. Paco dit :

    Bonsoir,

    Merci pour votre tuto qui m’a permis de m’initier à FPDF que j’ai découvert très récemment. Je viens d’appliquer votre solution à une petite application que je commence à développer pour gérer notre association sportive mais je rencontre un problème: les entêtes de tableau (entete_table) n’apparaissent en effet que sur la page 1. Par contre, les header et footer sont bien présents sur toutes les pages. Avez-vous eu l’occasion de faire tourner votre code sur une base sql plus fournie? Merci d’avance de votre retour et bonne soirée.

    Paco

  9. webtolosa dit :

    C’est une boucle PHP simple :
    – tu lances l’extraction de ta base
    – tu affiches l’en-tête de la page
    – tu lances un décompte de 1 à 10 (boucle for()) sur l’extraction de tes lignes.
    – Quand tu arrives à 10 lignes, tu appelles le footer et tu recommences au point 2 (en-tête)

  10. Stephan dit :

    Tout d’abord meilleurs vœux et merci pour ce tuto super bien rédigé qui me sauve la vie.
    j’ai un soucis s’il vous plaît ma requête renvoie 200 lignes et je souhaite afficher le résultats sur plusieurs pages. avec un max de 10 lignes par pages….
    merci pour votre aide

  11. webtolosa dit :

    Vous créez un tableau (fonction Multicell) avec une ligne correspondant à une cellule de la largeur souhaitée

  12. meriem dit :

    comment créer un ligne horizontal svp (axe y )

  13. Jenisa dit :

    Merci pour ce tuto simple, clair et efficace.

  14. fif dit :

    Et voilà, je viens de trouver une solution : pour ceux qui veulent utiliser tfpdf comme moi et qui ont eu ce probleme :du coup pour faire apparaitre le header et le footer, il faut passer directement sur la page tfpdf.php, et y implanter votre function dans les rubriques destinées « function header » et « function footer » .
    bonne soirée !

  15. fif dit :

    Merci beaucoup pour ce tuto, il m’a beaucoup aidé!
    Cependant , je rencontre un problème avec tfpdf (Cette classe est une version modifiée de FPDF qui ajoute le support de l’UTF-8), je n’ai pas encore reussi à la faire fonctionner correctement : mes données s’affichent mais pas le header, ni le footer, mais je persevere !
    Merci encore.

  16. Mr. Apogee dit :

    Je vous remercie, vous m’avez grave aide…. Super code. Encore merci

  17. Norore dit :

    Merci beaucoup !
    J’étais bloquée sur les MultiCell, votre article m’a aidé à pointer ce qui n’allait pas ! Il suffisait de repositionner correctement la ligne sur l’axe des y.
    Petite subtilité néanmoins, et qui pourrait vous être utile un jour : il ne faut pas négliger de calculer la hauteur du MultiCell par rapport au texte entré, pour pouvoir avoir des lignes à la bonne hauteur par la suite !
    Bonne continuation, et encore merci pour ce billet.

Commentaire

Name

Mail (ne sera pas publié)

Website

Laisser ces deux champs tels quels :
:D :-) :( :o 8O :? 8) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: