Dans le cadre du développement de mon CMS, j'avais besoin de créer des fichiers au format PDF pour le module boutique en ligne. Lundi après-midi j'ai cherché les solutions existantes, lundi soir j'avais fait mon choix, mercredi après-midi j'avais terminé ma classe (et aujourd'hui je boucle cet article).
Des solutions toutes faites existent pour convertir un document HTML en PDF :
Le choix existe, c'est bien, mais c'est plutôt lourd (puisqu'il faut intégrer l'ensemble des fichiers de la libraire dans le cas d'une distribution) et ça ne correspond pas à ce que je cherchais.
De plus les classes existantes utilisent souvent FPDF, libre et gratuite. FPDF est une librairie PHP qui permet de créer un fichier PDF facilement.
Exemple avec la librairie FPDF
<?php
require('FPDF.php');
$pdf = new FPDF();
$pdf->AddPage();
$pdf->SetFont('Arial', '', 18);
$pdf->Cell(0, 10, 'Mon Super Titre');
$pdf->Output();
?>
Cet exemple affiche le texte 'Mon Super Titre' en Arial 18 mm (l'unité par défaut)
Allez sur le site FPDF pour des exemples simples et des scripts près à l'emploi.
La classe facturePDF (11 ko)
J'ai conçu cette classe avec un système très simple d'élément couplé à des gabarits qui reprennent le principe de bloc en HTML avec la possibilité de définir :
- la hauteur de ligne
- la taille de la typo
- la graisse
- la couleur
- l'alignement
- la couleur de fond
- les marges
- les padding
Vous définissez d'abord les éléments à afficher et ensuite leurs gabarits.
Voici un exemple concret.
<?php
// importe le fichier
require('facturePDF.php');
// - - - - - - - - - - - - - - - - - - - - - - - - -
//
// #1 initialise les informations de base
//
// adresse de l'entreprise qui émet la facture
$adresse = "K-soulé SA\n1 rue du Général Donk\n12321 Code Postal City\n\ncontact@general-zorg.fr\n(+33) 3 89 68 27 54";
// adresse du client
$client = "Robert Meinard\n3 place de Clichy\n88154 Nancy le port";
// pied de page
$piedPage1 = "K-soulé SA - 1 rue du Général Donk - 12321 Code Postal City - contact@general-zorg.fr - (+33) 3 89 68 27 54";
$piedPage2 = "Les produits livrés demeurent la propriété exclusive de notre entreprise jusqu'au paiement complet de la présente facture.";
$piedPage3 = "RCS : 245-532-578- NANCY / TVA Intracomunautaire : FR 02 4578 1455 5578 3254 / SIRET 887 547 259 974 125";
// initialise l'objet facturePDF
// gabarit : template['header']
// template['client']
// template['footer']
//
// le constructeur attend 3 paramètres :
// - l'adresse de l'entreprise qui émet la facture
// - l'adresse du client
// - le pied de page
//
$pdf = new facturePDF($adresse, $client, $piedPage1."\n".$piedPage2."\n".$piedPage3);
// défini le logo avec setLogo()
// il suffit de passer l'adresse du logo en paramètre
// par défaut le logo est affiché dans le coin haut gauche.
// si vous voulez changer la position il faut utiliser les variables suivantes :
// - $logoPosX : coordonnée sur X (abscisse)
// - $logoPosY : coordonnée sur X (ordonnées)
// - $logoWidth : largeur de l'image (en mm);
//
$pdf->setLogo('logo.png');
// entête des produits
// gabarit : template['productHead']
//
// l'entête des produits est une array() qui contient la liste des colonnes des produits.
// productHeaderAddRow() attend 3 paramètres :
// - le titre de la colonne
// - la largeur de la colonne
// - l'alignement du texte
// la largeur et l'alignement seront utilisés pour chaque cellule appartenant à cette colonne
// vous pouvez ajouter autant de colonne que vous souhaitez.
// Les dimensions sont exprimé en millimètres.
//
$pdf->productHeaderAddRow('Produit', 75, 'L');
$pdf->productHeaderAddRow('Référence', 40, 'C');
$pdf->productHeaderAddRow('Prix HT', 25, 'C');
$pdf->productHeaderAddRow('QTE', 15, 'C');
$pdf->productHeaderAddRow('Prix HT', 25, 'R');
// entête des totaux
// gabarit : template['totalHead']
//
// idem que l'entête des produits, mais est utilisé pour le tableau qui contiendra les totaux.
// totalHeaderAddRow() attend 2 paramètres :
// - la largeur de la colonne
// - l'alignement du texte
//
$pdf->totalHeaderAddRow(40, 'L');
$pdf->totalHeaderAddRow(30, 'R');
// élément personnalisé
// gabarit : identifiant passé en paramètre
//
// ajoute des éléments personnalisé avec elementAdd()
// on passe 3 paramètre :
// - le texte à afficher
// - un identifiant qui sera utilisé dans le gabarit d'affichage
// - la zone à laquelle sera rattaché cet élément ('header', 'content', 'footer')
//
$pdf->elementAdd('', 'traitEnteteProduit', 'content');
$pdf->elementAdd('', 'traitBas', 'footer');
// - - - - - - - - - - - - - - - - - - - - - - - - -
//
// #2 Créer une facture
// gabarit : template['infoFacture']
// template['infoDate']
// template['infoPage']
//
// initFacture() attend 3 paramètres :
// - numéro de facture
// - date
// - texte affiché avant le numéro de page
//
$pdf->initFacture("Facture n° 555-911", "Paris le 21/03/2014", "Page ");
// produit
// gabarit : template['product']
//
// ajoute des lignes de produits avec la fonction productAdd()
// productAdd attend un array en paramètre. Cet array contient autant de valeurs que vous avez ajouté de colonnes avec productHeaderAddRow()
//
$pdf->productAdd(array('Attrape mouche collant', 'C22M9', '10.00', '7', '70.00'));
$pdf->productAdd(array('Attrape mouche collant CRAFT', 'C42M3', '5.00', '7', '35.00'));
$pdf->productAdd(array('Cylindre Honda ZX10R 125cc à carburateur injecté et intégration de cartouche NOS 2.6 vag-7', 'K345', '2.95', '1', '2.95'));
$pdf->productAdd(array('Baume du tigre rouge 3gr', 'BT45', '54.95', '1', '54.95'));
$pdf->productAdd(array('Batterie GoPro Hero 3 2044 mAh', 'SNCF', '0.99', '9', '9.91'));
$pdf->productAdd(array('Pack Pépito Super Promo Collector 25th anniversary', 'Gift-81', '37,00', '1', '37,00'));
// ligne des totaux
// gabarit : template['total']
//
// même principe que pour les lignes de produits.
// vous pouvez ajouter autant de ligne que vous souhaitez.
$pdf->totalAdd(array('Total HT', '59.95 EUR'));
$pdf->totalAdd(array('TVA', '10.99 EUR'));
$pdf->totalAdd(array('Sous total TTC', '71.94 EUR'));
$pdf->totalAdd(array('Livraison', '100.00 EUR'));
$pdf->totalAdd(array('Remise', '-5.94 EUR'));
$pdf->totalAdd(array('Total TTC', '165.00 EUR'));
// - - - - - - - - - - - - - - - - - - - - - - - - -
//
// #3 Importe le gabarit
//
// pour la démo j'ai enregistré le gabarit dans un fichier externe
//
require('gabarit'.intval($_GET['id']).'.php');
// - - - - - - - - - - - - - - - - - - - - - - - - -
//
// #4 Finalisation
//
// construit le PDF
//
$pdf->buildPDF();
// télécharge le fichier
//
// Output attend 2 paramètres. Le nom du fichier et le mode. 'I' permet d'afficher le fichier, 'D' permet de le télécharger.
// plus d'info à http://www.fpdf.org/fr/doc/output.htm
//
$pdf->Output('Facture.pdf', 'D');
?>
Voilà pour le fichier qui crée la facture.
Ce qui est pratique avec la classe FacturePDF c'est que vous pouvez utiliser différents gabarits qui changeront la forme de la facture, comme des fichiers CSS.
Le fonctionnement de ces gabarits est d'ailleurs calqué sur le box model.
Les gabarits
La classe FacturePDF possède une variable publique $template qui contient les gabarits de chaque élément présent dans le fichier PDF.
Chaque gabarit est enregistré sous forme d'array. C'est la fonction privé templateArrayInit() qui initialise cet array :
<?php
private function templateArrayInit(){
$r = array();
$r['lineHeight'] = 6; // hauteur de ligne
$r['fontSize'] = 12; // taille de la police
$r['fontFace'] = ''; // graisse de la police. Au choix 'B' (gras), 'I'(italiqu) ou 'U' (souligné)
$r['color'] = array('r'=>0, 'g'=>0, 'b'=>0); // couleur du texte au format RGB (RVB en français)
$r['backgroundColor'] = array('r'=>255, 'g'=>255, 'b'=>255);// couleur du fond au format RGB
$r['align'] = 'L'; // alignement du texte
$r['margin'] = array(0, 0, 0, 0); // margin. Idem au CSS : haut, droit, bas, gauche
$r['padding'] = array(0, 0, 0, 0); // padding haut, droit, bas, gauche
return $r;
}
?>
Chaque élément est définit avec ces paramètres de base. Si vous voulez modifier un paramètre vous n'avez qu'à accéder à la variable qui vous intéresse au travers des différentes array.
Par exemple pour modifier la taille de la typo de l'adresse du client.
<?php
$pdf->template['client']['fontSize'] = 11;
?>
Pour modifier la couleur, la graisse et la taille de la typo du numéro de facture.
<?php
$pdf->template['infoFacture']['fontSize'] = 15;
$pdf->template['infoFacture']['fontFace'] = 'B';
$pdf->template['infoFacture']['color'] = array('r'=>150, 'g'=>150, 'b'=>150);
?>
Regardez l'exemple ci-dessous pour voir la liste des variable de gabarits disponibles de base. N'oubliez pas que vous pouvez rajouter vos propres variables en ajoutant des éléments personnalisés avec elementAdd().
Exemple de gabarit (2,5 ko)
Voici un exemple de gabarit pour le code affiché dans "La classe facturePDF (11 ko)".
<?php
// coordonnée de l'entreprise
$pdf->template['header']['fontSize'] = 11;
$pdf->template['header']['lineHeight'] = 5;
$pdf->template['header']['margin'] = array(24, 0, 0, 10);
// numéro de page
$pdf->template['infoPage']['margin'] = array(5, 5, 0, 0);
$pdf->template['infoPage']['align'] = 'R';
// numéro de facture
$pdf->template['infoFacture']['margin'] = array(60, 5, 0, 10);
$pdf->template['infoFacture']['fontFace'] = 'B';
// date
$pdf->template['infoDate']['fontSize'] = 10;
$pdf->template['infoDate']['margin'] = array(20, 0, 0, 120);
$pdf->template['infoDate']['color'] = array('r'=>150, 'g'=>150, 'b'=>150);
// client
$pdf->template['client']['fontSize'] = 15;
$pdf->template['client']['margin'] = array(30, 0, 0, 120);
// pied de page
$pdf->template['footer']['fontSize'] = 11;
$pdf->template['footer']['lineHeight'] = 5;
$pdf->template['footer']['color'] = array('r'=>100, 'g'=>100, 'b'=>100);
$pdf->template['footer']['align'] = 'L';
$pdf->template['footer']['margin'] = array(255, 40, 0, 40);
// entete de produit
$pdf->template['productHead']['fontFace'] = 'B';
$pdf->template['productHead']['color'] = array('r'=>195, 'g'=>0, 'b'=>130);
$pdf->template['productHead']['margin'] = array(20, 0, 0, 0);
$pdf->template['productHead']['padding'] = array(4, 4, 0, 14);
// liste des produit
$pdf->template['product']['fontSize'] = 10;
$pdf->template['product']['lineHeight'] = 4;
$pdf->template['product']['backgroundColor2'] = array('r'=>255, 'g'=>255, 'b'=>255);
$pdf->template['product']['color'] = array('r'=>50, 'g'=>50, 'b'=>50);
$pdf->template['product']['color2'] = array('r'=>50, 'g'=>50, 'b'=>50);
$pdf->template['product']['margin'] = array(1, 0, 0, 10);
$pdf->template['product']['padding'] = array(1, 4, 1, 4);
// entete des totaux
$pdf->template['totalHead']['lineHeight'] = 1;
$pdf->template['totalHead']['backgroundColor'] = array('r'=>195, 'g'=>0, 'b'=>130);
$pdf->template['totalHead']['margin'] = array(10, 0, 0, 0);
// liste des totaux
$pdf->template['total']['lineHeight'] = 5;
$pdf->template['total']['margin'] = array(0, 0, 0, 120);
$pdf->template['total']['padding'] = array(2, 0, 0, 0);
// element personnalisé 1
$pdf->template['traitEnteteProduit']['lineHeight'] = 1;
$pdf->template['traitEnteteProduit']['backgroundColor'] = array('r'=>195, 'g'=>0, 'b'=>130);
$pdf->template['traitEnteteProduit']['margin'] = array(80, 0, 0, 0);
// element personnalisé 2
$pdf->template['traitBas']['lineHeight'] = 1;
$pdf->template['traitBas']['backgroundColor'] = array('r'=>255, 'g'=>210, 'b'=>255);
$pdf->template['traitBas']['margin'] = array(290, 40, 0, 40);
?>
Afficher le résultat de ce gabarit.
Historique
12/12/2013 : version 1.0
15/08/2016 : correction à la ligne 29 "$this->FPDF();" remplacé par "parent::__construct();".
Merci à Raph dans les commentaires.
Développement futur ?
- Prise en compte des bords, comme en CSS (border: 1px)
- Affichage d'image de fond pour les éléments (background-image:url('test.png');
- Utilisation du logo comme un élément quelconque.