Criando PDF em PHP através de HTML com DOMPDF

Um recurso que temos que desenvolver em muitos sistemas são relatórios, em muitos casos exportando-os em PDF. Existem diversas bibliotecas para gerar PDF em PHP, inclusive já teve post aqui a muito tempo atrás, mas o controle do estilo desta biblioteca deixava a desejar. Então nada melhor que utilizar uma tecnologia que conhecemos bem para a apresentação como HTML/CSS e simplesmente converter para PDF, para isto temos o componente DOMPDF.
Primeiramente baixamos a biblioteca pelo composer.
composer require dompdf/dompdf
Então criamos um script para gerar um PDF bem básico.
<?php
require('./vendor/autoload.php');
use Dompdf\Dompdf;
$dompdf = new Dompdf();
$dompdf->loadHtml('<h1>Olá Mundo</h1><p>O básico do DOMPDF</p>');
$dompdf->setPaper('A4');
$dompdf->render();
$dompdf->stream('documento.pdf',['Attachment'=>false] );
Começamos na linha 2 carregando o autoload do composer para poder acessar a biblioteca. Na linha 6 criamos um objeto Dompdf
este objeto que será responsável por converter o HTML para PDF.
Na linha 7 utilizamos o método loadHtml
para carregar o HTML que iremos utilizar para gerar o PDF. O código HTML aceita o uso de CSS, seja inline, incorporado ou em arquivo externo. Podemos também utilizar o método loadHtmlFile
para carregar um arquivo HTML para transformar em PDF, mas este método não interpreta PHP, tornando este método não muito utilizado já que fica limitado a um arquivo estático. Veremos mais adiante como ter um uso parecido com o do método loadHtmlFile
mas que interprete PHP.
Na linha 8 configuramos o tamanho da folha do PDF através do método setPaper
, que aceita os formatos de comuns de papel: ‘letter’, ‘legal’, ‘A4’, ‘A3’, etc. Como segundo parâmetro recebe a orientação da página, que por padrão é portrait(retrato), e podemos mudar landscape(paisagem).
Na linha 9 executamos o render
que irá converter o HTML para os dados em PDF, podemos visualizar a resposta chamando o método stream
, que aceita como argumento o nome do arquivo e um array de opções, entra elas 'Attachment'
que por padrão é true, fazendo que o arquivo seja obrigatoriamente baixado, para podermos visualizar o PDF no navegador configuramos a opção 'Attachment'
para false.
Carregando um arquivo PHP para PDF
Como visto acima podemos carregar o HTML para transformar em PDF através de dois métodos, loadHtml
e loadHtmlFile
, o método loadHtmlFile
não é muito conveniente para relatórios e etc, pois ele só carrega HTML estático, não sendo possível carregar uma página com código PHP através dele, e loadHtml
só carrega uma string, e como sabemos escrever um HTML dentro de uma string não é a coisa mais divertida do mundo, melhor sendo o HTML escrito em um arquivo separado. Podemos resolver este problema utilizando a função ob_start
e um require
. Mas antes vamos ver uma pagina simples de relatório.
<?php
require('./vendor/autoload.php');
use BotecoDigital\UserDAO;
$dao = new UserDAO();
$users = $dao->all();
?>
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Relatório de Usuários</title>
<style>
@page{
margin: 150px 50px ;
}
body{
font-family: 'Verdana', sans-serif;
margin:0px;
padding:0px;
}
.header{
position: fixed;
left: 0;
right:0;
top: -100px;
height: 50px;
padding: 10px;
background: #333;
margin-bottom:100px;
text-align: center;
}
.header img{
height: 50px;
}
.footer{
position: fixed;
left: 0;
right:0;
bottom:0;
background: #333;
color:#FFF;
text-align: center;
padding: 10px;
}
h1{
text-align: center;
}
table{
width: 100%;
border:1px solid #333;
padding: 5px;
}
table tr th{
background: #333;
color:#FFF;
padding:5px;
}
table tr:nth-child(even) td{
background: #EEE;
}
.image{
text-align: center;
}
.image img{
border: 1px solid #CCC;
padding:3px;
margin:5px;
}
</style>
</head>
<body>
<header class="header">
<img src="https://www.botecodigital.dev.br/wp-content/themes/boteco_v4/img/logob.png" alt="" height="50">
</header>
<h1>Relatório de Usuários</h1>
<table>
<tr>
<th>Foto</th>
<th>Nome</th>
<th>Sobrenome</th>
<th>E-mail</th>
<th>Username</th>
</tr>
<?php foreach ($users as $user) { ?>
<tr>
<td class="image"><img src="<?php echo $user->getFoto() ?>" alt=""></td>
<td><?php echo $user->getNome() ?></td>
<td><?php echo $user->getSobrenome() ?></td>
<td><?php echo $user->getEmail() ?></td>
<td><?php echo $user->getUsername() ?></td>
</tr>
<?php } ?>
</table>
<footer class="footer">
Gerado em <?php echo (new DateTime())->format('d/m/Y h:i:s')?>
</footer>
</body>
</html>
Como podemos ver esta é uma página simples de relatório, com um pouco de CSS(regras suportadas). Pegamos os dados através de um componente de acesso a dados na linha 5 e os listamos em uma tabela. Mas temos algumas regras diferentes por exemplo a regra @page
que é uma regra para a impressão, nela podemos configurar as margens de todas as páginas do documento PDF.
Também utilizamos a regra position: fixed
no cabeçalho e no rodapé com os devidos left, right, top, bottom. Com o position definido para fixed, ele irá colocar o cabeçalho e o rodapé em cada página do documento PDF.
Quanto a imagens vale salientar que caminhos relativos podem gerar alguns problemas, podendo o caminho absoluto de imagens ser uma melhor alternativa. Caso a imagem que deseje adicionar não pode ser servida via http de forma aberta, também é possível inserir uma imagem inline simplesmente carregando-a via PHP e convertendo para base64. Veja o exemplo.
<img src="data:image/png;base64,<?php echo base64_encode(file_get_contents('logo.png'))?>" alt="" height="50">
Veja o exemplo em html da página acima(pelas regras CSS o cabeçalho só vai aparecer no PDF).
Agora vamos ao código para carregar este arquivo PHP e transformar em PDF.
<?php
require('./vendor/autoload.php');
use Dompdf\Dompdf;
$dompdf = new Dompdf(['enable_remote'=>true]);
ob_start();
require(__DIR__ . '/relatorio.php');
$html = ob_get_clean();
$dompdf->loadHtml($html);
$dompdf->setPaper('A4');
$dompdf->render();
$dompdf->stream('documento.pdf',['Attachment'=>false] );
Este código é bastante semelhante ao inicial, mas na criação do componente Dompdf
passamos um array de opções, e neste caso, configurando a opção enable_remote
para true, isso deve ser feito para que ele possa acessar as imagens com caminho absoluto.
Na linha 6 chamamos a função ob_start
esta função inicializa o cache de saída, ou seja, a partir dela todo conteudo que seria “exibido” será armazenado em cache. Com o cache de saída ativado utilizamos a função require
para carregar e interpretar relatório que criamos acima, sendo o resultado armazenado em vez de exibido.
Na linha 7 chamamos a função ob_get_clean
que retorna todo o conteúdo do cache de saída e o limpa. Armazenamos este conteúdo em um variável e o passamos para o método loadHtml
. O resto segue sem alteração do exemplo inicial.
E um detalhe importante, se desejarmos alguma fonte diferente como as Google Fonts, vamos precisar configurar um diretório para as fontes no componente Dompdf
, isto é feito através do array de opções. Depois é só usar as fontes normalmente no documento HTML.
$dompdf = new Dompdf([
'enable_remote'=>true,
'fontDir' => './fonts',
'fontCache' => './fonts',
'tempDir' => './fonts',
]);
Bom era isso espero que criar arquivos PDF tenha ficado um pouco mais fácil, para mim ficou 🙂
T++!