PHP : Créer ses devis au format PDF avec html2pdf

Partager cet article

Temps estimé pour la lecture de cet article : 46 min

Présentation de la bibliothèque

Exemple de document réalisé avec html2pdf

Exemple de document réalisé avec html2pdf

Il est possible de générer des documents PDF à partir d’un code html avec certaines bibliothèques PHP, Html2PDF en est un bon exemple. La librairie a été faite afin de faciliter la création de PDF, toutefois, il faut bien comprendre qu’elle ne sert pas à convertir directement une page HTML. Certaines balises (, , ) ne peuvent pas être utilisées. Voici un lien vers la documentation. Nous verrons dans cet article comment générer automatiquement un devis.

Installation de la bibliothèque

Une fois que vous aurez téléchargé html2pdf. Il vous suffit d’extraire l’archive dans le dossier de votre projet et ensuite vous n’avez plus qu’à inclure le fichier html2pdf.class.php. Pour ma part je renomme le dossier, afin qu’il soit plus pratique à appeler, ici je raccourcis juste le nom du dossier en supprimant juste la version du projet. Il ne vous reste plus qu’à inclure le tout dans votre fichier php :

require_once "html2pdf/html2pdf.class.php";

Et voilà, la bibliothèque est prête à être utilisée.

Remarque : pour ma part j’ai supprimé le dossier d’exemples qui n’est pas nécessaire.

Créer ses devis avec html2pdf

Attention, ce tutoriel n’est qu’une introduction à la bibliothèque et n’ayant jamais eu à réaliser des devis auparavant, il manquera surement des informations que vous devriez ajouter, mais ça c’est votre boulot d’adapter cet article à votre convenance ;).

Pour commencer, il nous faut un ensemble de données, un devis c’est un échange entre vous et un client pour un projet donné. Le but de l’article n’étant pas de vous montrer, comment effectuer des requêtes sur une base de données, je vais donc utiliser des données factices.

$user = array(
	"id" => 1,
	"siret" => "152 356 785",
	"firstname" => "John",
	"lastname" => "Doe",
	"email" => "john.doe@gmail.com",
	"portable" => "06.25.35.45.35",
	"address" => "26 Avenue du Bourg\n75000 Paris"
);

$client = array(
	"id" => 1,
	"firstname" => "Luc",
	"lastname" => "Kennedy",
	"mail" => "luc.kennedy@gmail.com",
	"portable" => "06.32.23.15.58",
	"address" => "5 Avenue du Boulevard Maréchal Juin\n14000 Caen",
	"infos" => "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Praesentium eos tempora, magni delectus porro cum labore eligendi."
);

$project = array(
	"id" => 1,
	"name" => "Création d'un Portfolio",
	"status" => 1,
	"infos" => "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Praesentium eos tempora, magni delectus porro cum labore eligendi.",
	"created" => 1,
	"paid" => false,
	"client_id" => 1,
	"user_id" => 1
);

$tasks[] = array(
	"id" => 1,
	"ref" => "96ER1",
	"description" => "Veille technologique",
	"price" => 200,
	"quantity" => 1,
	"project_id" => 1
);

$tasks[] = array(
	"id" => 2,
	"ref" => "152DE",
	"description" => "Création et intégration d'un thème pour WordPress",
	"price" => 500,
	"quantity" => 1,
	"project_id" => 1
);

$tasks[] = array(
	"id" => 3,
	"ref" => "25365",
	"description" => "Développement d'un plugin WordPress sur mesure pour le client",
	"price" => 1000,
	"quantity" => 1,
	"project_id" => 1
);

Les bases de html2pdf

La première chose à faire c’est bien sur d’instancier la classe. Pour ce faire, il suffit de faire :

$pdf = new HTML2PDF("p","A4","fr");

Alors regardons dans le détail, les arguments du constructeur :

  • Le premier argument, c’est le sens de la page, par défaut c’est en portrait. Pour passer la page en landscape, il suffit de remplacer le p par un l.
  • Le second, c’est le format de la page encore une fois par défaut c’est le format A4.
  • Le troisième, c’est la langue utilisée.

Une fois qu’on a une instance, les deux fonctions principales qui vont nous intéressées sont writeHTML et Output. La première c’est tout simplement la fonction qui va permettre d’écrire, le contenu du pdf. On a alors plusieurs solutions pour écrire le contenu.

Soit, on concatène le contenu au fur et à mesure dans une variable, soit on utilise les fonctions ob_start() et ob_get_clean(), on peut alors écrire le contenu comme si on réalisé un script et la fonction ob_get_clean retournera l’ensemble du script écrit depuis l’appel à la fonction ob_start.

La seconde fonction permet tout simplement de préciser le rendu effectué par la classe, par défaut par exemple, le pdf sera affiché dans le navigateur. Elle permet aussi de préciser le nom du pdf. Un autre argument est disponible, par exemple l’option ‘D’ permet de forcer le téléchargement du pdf.

Il est également possible de préciser les informations sur le document, à savoir l’auteur du document, le titre du document, le sujet ou encore des mots-clés. Ce qui nous donne :

try {
	$pdf = new HTML2PDF("p","A4","fr");
	$pdf->pdf->SetAuthor('DOE John');
	$pdf->pdf->SetTitle('Devis 14');
	$pdf->pdf->SetSubject('Création d\'un Portfolio');
	$pdf->pdf->SetKeywords('HTML2PDF, Devis, PHP');
	$pdf->writeHTML($content);
	$pdf->Output('Devis.pdf',' D');
} catch (HTML2PDF_exception $e) {
	die($e);
}

On réalise un try et catch afin d’afficher les exceptions en cas d’erreurs.

La balise page introduit par hmtl2pdf

La balise page permet de définir la mise en page du document. On peut ainsi préciser :

  • Les marges : backleft, backright, backtop, backbottom
  • Options de la page: numéro de page…
  • Le fond : une couleur ou encore une image
  • Un haut et un pied de page

Les balises page_header et page_footer doivent impérativement se situer juste en dessous de l’ouverture de la balise page.

<page> 
    <page_header> 
       ...              
    </page_header> 
    <page_footer> 
       ...
    </page_footer> 
    ...
 </page> 

Regardons maintenant les options que j’ai utilisées pour mon exemple :

<page backtop="10mm" backleft="10mm" backright="10mm" backbottom="10mm" footer="page;">
	<page_footer>
		<hr />
		<p>Fait a Paris, le <?php echo date("d/m/y"); ?></p>
		<p>Signature du particulier, suivie de la mension manuscrite "bon pour accord".</p>
		<p>&amp;nbsp;</p>
	</page_footer>
        ...
</page>

Rien de compliqué, on applique une marge de 10 mm sur chaque côté et on réalise un pied de page. On rajoute un paragraphe avec un espace afin de pouvoir laisser de la place pour la signature du client. L’option footer permet de créer le numéro en bas de page.

Le contenu de notre devis

Il nous reste donc plus qu’à réaliser notre devis, généralement afin de faire la mise en forme de notre fichier, on utilise les tableaux html.

Du coup, c’est un peu verbeux mais il n’y a rien de compliqué dans le code qui suit. Commençons par le CSS, utilisé dans le script.

<style type="text/css">
	table { 
		width: 100%; 
		color: #717375; 
		font-family: helvetica; 
		line-height: 5mm; 
		border-collapse: collapse; 
	}
	h2 { margin: 0; padding: 0; }
	p { margin: 5px; }

	.border th { 
		border: 1px solid #000;  
		color: white; 
		background: #000; 
		padding: 5px; 
		font-weight: normal; 
		font-size: 14px; 
		text-align: center; 
        }
	.border td { 
		border: 1px solid #CFD1D2; 
		padding: 5px 10px; 
		text-align: center; 
	}
	.no-border { 
		border-right: 1px solid #CFD1D2; 
		border-left: none; 
		border-top: none; 
		border-bottom: none;
	}
	.space { padding-top: 250px; }

	.10p { width: 10%; } .15p { width: 15%; } 
	.25p { width: 25%; } .50p { width: 50%; } 
	.60p { width: 60%; } .75p { width: 75%; }
</style>

On force les tableaux à prendre toute la largeur possible (la largeur de la page moins les 20 mm définies grâce à la balise >page>, on précise que les cellules doivent se coller et on met un léger gris sur le texte. La seconde propriété intéressante, c’est le tableau qui possède la classe border, on applique un fond et une couleur sur les headers du tableau afin de faire ressortir la description du tableau.

<?php
	require_once "lib/html2pdf.php";

      // Initialisation des données

	ob_start();
	$total = 0;  $total_tva = 0;
?>

// Insertion du CSS 

<page backtop="10mm" backleft="10mm" backright="10mm" backbottom="10mm" footer="page;">

	<page_footer>
		<hr />
		<p>Fait a Paris, le <?php echo date("d/m/y"); ?></p>
		<p>Signature du particulier, suivie de la mension manuscrite "bon pour accord".</p>
		<p>&amp;nbsp;</p>
	</page_footer>

	<table style="vertical-align: top;">
		<tr>
			<td class="75p">
				<strong><?php echo $user['firstname']." ".$user['lastname']; ?></strong><br />
				<?php echo nl2br($user['address']); ?><br />
				<strong>SIRET:</strong> <?php echo $user['siret']; ?><br />
				<?php echo $user['email']; ?>
			</td>
			<td class="25p">
				<strong><?php echo $client['firstname']." ".$client['lastname']; ?></strong><br />
				<?php echo nl2br($client['address']); ?><br />
			</td>
		</tr>
	</table>

	<table style="margin-top: 50px;">
		<tr>
			<td class="50p"><h2>Devis n°14</h2></td>
			<td class="50p" style="text-align: right;">Emis le <?php echo date("d/m/y"); ?></td>
		</tr>
		<tr>
			<td style="padding-top: 15px;" colspan="2"><strong>Objectif:</strong> <?php echo $project['name']; ?></td>
		</tr>
	</table>

	<table style="margin-top: 30px;" class="border">
		<thead>
			<tr>
				<th class="60p">Description</th>
				<th class="10p">Quantité</th>
				<th class="15p">Prix Unitaire</th>
				<th class="15p">Montant</th>
			</tr>
		</thead>
		<tbody>
			<?php foreach ($tasks as $task): ?>
			<tr>
				<td><?php echo $task['description']; ?></td>
				<td><?php echo $task['quantity']; ?></td>
				<td><?php echo $task['price']; ?> &amp;euro;</td>
				<td><?php
						$price_tva = $task['price']*1.2;
						echo $price_tva;
					?>
				&amp;euro;</td>

				<?php
					$total += $task['price'];
					$total_tva += $price_tva;
				?>
			</tr>
			<?php endforeach ?>

			<tr>
				<td class="space"></td>
				<td></td>
				<td></td>
				<td></td>
			</tr>

			<tr>
				<td colspan="2" class="no-border"></td>
				<td style="text-align: center;" rowspan="3"><strong>Total:</strong></td>
				<td>HT : <?php echo $total; ?> &amp;euro;</td>
			</tr>
			<tr>
				<td colspan="2" class="no-border"></td>
				<td>TVA : <?php echo ($total_tva - $total); ?> &amp;euro;</td>
			</tr>
			<tr>
				<td colspan="2" class="no-border"></td>
				<td>TTC : <?php echo $total_tva; ?> &amp;euro;</td>
			</tr>
		</tbody>
	</table>

</page>

<?php
	$content = ob_get_clean();
	try {
		$pdf = new HTML2PDF("p","A4","fr");
		$pdf->pdf->SetAuthor('DOE John');
		$pdf->pdf->SetTitle('Devis 14');
		$pdf->pdf->SetSubject('Création d\'un Portfolio');
		$pdf->pdf->SetKeywords('HTML2PDF, Devis, PHP');
		$pdf->writeHTML($content);
		$pdf->Output('Devis.pdf');
	} catch (HTML2PDF_exception $e) {
		die($e);
	}

?>

Regardons ce qui peut paraître compliqué dans le code :

  • La fonction nl2br permet simplement de remplacer ce genre de caractères \r, \n par des
    . Ainsi par exemple ici, l’adresse contenant un \n, on aura d’abord la voie puis un retour à la ligne et le code postal plus le nom de la ville.
  • $task[‘price’]*1.2. Ici, on calcule simplement la TVA, on suppose qu’elle est de 20%, or mathématiquement rajouter 20% à un nombre revient a le multiplier par 1.2.
  • L’attribut colspan du td permet de préciser que cette cellule va fusionner avec la suivante et donc prendre la place de deux cellules.
  • L’attribut rowspan permet de fusionner les colonnes, ce qui nous permet de créer la case totale.

Conclusion

À partir de ce script, vous allez pouvoir facilement adapter le code. On peut par exemple penser à un back-office qui gère nos projets, on créé un bouton qui généra automatiquement le code en fonction des tâches de la bdd. Vous pouvez télécharger l’ensemble du code du tutoriel dans cette archive. Cet article est inspiré d’un tutoriel vidéo fait par grafikart.

2 comments

  1. Bonjour,

    Je rencontre des difficultés pour implémenter la librairie dans mon application.

    Voilà le message d’erreur :

    [ERROR] It seems that Html2Pdf dependencies are not installed… you must install thems with `composer install`

    Est-il possible d’utiliser Html2Pdf sans passer par composer ?

    Merci par avance pour vos réponses.

    • Salut !

      Alors, il semblerait que sur le repo officiel, ils expliquent que pour utiliser la librarie, il faut utiliser composer.

      If you do not know what is Composer, you have a few years late

      Ca se passe de commentaires… Une solution alternative serait de télécharger une vieille version de Html2Pdf, la 4.6.1 semble conserver la possibilité d’être utilisé en faisant simplement un require.

      Bon courage !
      Et bonne fin de semaine !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.