PHP

Gerando PDF com Snappy

Por em

Nesse tutorial irei ensinar como gerar PDF no PHP utilizando a biblioteca Snappy da KnpLabs.

Sobre a Snappy

O Snappy é uma biblioteca PHP (5+) para geração de thumbnail, snapshot ou PDF de uma URL ou página HTML. Funciona como um wrapper sobre o wkhtmltopdf (que também tem a wkhtmltoimage).

O wkhtmltopdf é uma ferramenta de linha de comando Open-source (LGPLv3) que utiliza o motor QT WebKit (motor do Chrome, Safari, Opera e etc), para conversão/renderização de HTML em PDF ou imagens. E funciona em Linux, OSX e Windows.

Então para o Snappy funcionar, você precisa instalar ele no seu sistema operacional.

Instalação

Levando em consideração que possua conhecimento sobre como utilizar o Composer e que possua-o instalado, vamos instalar o Snappy:

composer require knplabs/knp-snappy

Se possuir alguma necessidade de ter o Snappy sem utilizar o Composer, pode baixar do repositório deles no Github: https://github.com/KnpLabs/snappy. Mas não ensinarei como fazer autoloader nesse caso.

Utilização do Snappy

Após a instalação do Snappy, você terá a biblioteca na sua pasta vendor e integrada ao autoloader do Composer. Então basta incluir o autoloader onde deseja utilizar e terá acesso as classes do Snappy.

Configuração

Para a efetiva utilização do Snappy, devemos configurar o caminho para o wkhtmltopdf que será utilizado para conversão para PDF e imagens.

Levando em consideração que você já possui o wkhtmltopdf instalado e sabe o caminho para ele, então vamos configurar!

Vamos supor que o caminho seja: /usr/local/bin/wkhtmltopdf

A passagem do caminho para o wkhtmltopdf pode ser feita via construtor ou pelo método setBinary da classe Knp\Snappy\Pdf:

<?php 

require_once 'vendor/autoload.php'; // Ou seu autoloader

use Knp\Snappy\Pdf;

$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');

Ou

<?php 

require_once 'vendor/autoload.php'; // Ou seu autoloader

use Knp\Snappy\Pdf;

$snappy = new Pdf();
$snappy->setBinary('/usr/local/bin/wkhtmltopdf');

Com a configuração concluída, vamos testar várias situações.

Gerar PDF de uma URL e abrir no próprio navegador

Aqui será gerado um PDF com o conteúdo da página principal do blog da School of Net e irá abrir o mesmo na janela do navegador:

<?php

require_once 'vendor/autoload.php';

use Knp\Snappy\Pdf;

$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');

// Cabeçalho para o navegador entender que o conteúdo é um PDF
header('Content-Type: application/pdf');

echo $snappy->getOutput('http://www.schoolofnet.com/blog/');

Gerar PDF com conteúdo de várias URLs e abrir no próprio navegador

Aqui será gerado um único PDF com o conteúdo de várias URLs, que são de 4 artigos do blog da School of Net:

<?php

require_once 'vendor/autoload.php';

use Knp\Snappy\Pdf;

$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');

// Cabeçalho para o navegador entender que o conteúdo é um PDF
header('Content-Type: application/pdf');

echo $snappy->getOutput(
    [
        'http://www.schoolofnet.com/2015/07/trabalhando-com-repository-no-laravel/',
        'http://www.schoolofnet.com/2015/04/como-usar-os-metodos-magicos-no-php/',
        'http://www.schoolofnet.com/2015/04/enviando-emails-utilizando-swift-mailer/',
        'http://www.schoolofnet.com/2015/04/instalando-e-integrando-apache-com-php-no-windows/',
    ]
);

Gerar PDF a partir de um HTML e abrir no próprio navegador

Aqui será gerado um PDF com o conteúdo de um HTML, vindo de uma variável, e será mostrado no navegador:

<?php

require_once 'vendor/autoload.php';

use Knp\Snappy\Pdf;

$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');

// Cabeçalho para o navegador entender que o conteúdo é um PDF
header('Content-Type: application/pdf');

$html = <<<'EOD'
<h1>Relatório</h1>
<br/>
<table width="100%">
    <thead>
        <tr>
            <th>Nome</th>
            <th>E-mail</th>
            <th>Telefone</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Fulano da Silva</td>
            <td>[email protected]</td>
            <td>11 99999-8888</td>
        </tr>
        <tr>
            <td>Sicrano Santos</td>
            <td>[email protected]</td>
            <td>11 99999-7777</td>
        </tr>
        <tr>
            <td>João das Botas</td>
            <td>[email protected]</td>
            <td>11 99999-6666</td>
        </tr>
    </thead>
</table>
EOD;

$snappy->getOutputFromHtml($html, ['encoding' => 'UTF8']);

Gerando imagens PNG ao invés de PDF

Se deseja ao invés de gerar PDF, deseje que sejá uma imagem PNG, temos que:

  • Mudar a classe de use Knp\Snappy\Pdf para use Knp\Snappy\Image;
  • Utilizar o wkhtmltoimage ao invés do wkhtmltopdf;

Vamos considerar que o caminho para o wkhtmltoimage seja: /usr/local/bin/wkhtmltoimage.

Gera imagem PNG de uma URL e mostra no navegador

<?php

require_once 'vendor/autoload.php';

use Knp\Snappy\Image;

$snappy = new Image('/usr/local/bin/wkhtmltoimage', ['format' => 'png']);
$snappy->setDefaultExtension('png');

// Cabeçalho para o navegador entender que o conteúdo é uma imagem PNG
header('Content-Type: image/png');

echo $snappy->getOutput('http://www.schoolofnet.com/blog/');

Gera imagem PNG de várias URLs e mostra no navegador

<?php

require_once 'vendor/autoload.php';

use Knp\Snappy\Image;

$snappy = new Image('/usr/local/bin/wkhtmltoimage', ['format' => 'png']);
$snappy->setDefaultExtension('png');

// Cabeçalho para o navegador entender que o conteúdo é uma imagem PNG
header('Content-Type: image/png');

echo $snappy->getOutput(
    array(
        'http://www.schoolofnet.com/2015/07/trabalhando-com-repository-no-laravel/',
        'http://www.schoolofnet.com/2015/04/como-usar-os-metodos-magicos-no-php/',
        'http://www.schoolofnet.com/2015/04/enviando-emails-utilizando-swift-mailer/',
        'http://www.schoolofnet.com/2015/04/instalando-e-integrando-apache-com-php-no-windows/',
    )
);

Gera imagem PNG a partir de um HTML e mostra no navegador

<?php

require_once 'vendor/autoload.php';

use Knp\Snappy\Image;

$snappy = new Image('/usr/local/bin/wkhtmltoimage', ['format' => 'png']);
$snappy->setDefaultExtension('png');

// Cabeçalho para o navegador entender que o conteúdo é uma imagem PNG
header('Content-Type: image/png');

$html = <<<'EOD'
<h1 style="color: red;">Relatório</h1>
<br/>
<table width="100%">
    <thead>
        <tr>
            <th>Nome</th>
            <th>E-mail</th>
            <th>Telefone</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Fulano da Silva</td>
            <td>[email protected]</td>
            <td>11 99999-8888</td>
        </tr>
        <tr>
            <td>Sicrano Santos</td>
            <td>[email protected]</td>
            <td>11 99999-7777</td>
        </tr>
        <tr>
            <td>João das Botas</td>
            <td>[email protected]</td>
            <td>11 99999-6666</td>
        </tr>
    </thead>
</table>
EOD;

echo $snappy->getOutputFromHtml($html, ['encoding' => 'UTF8']);

Observação

Em todos os casos, se quisermos, ao invés de gerar o PDF e mostrar no navegador, que seja gerado e salvo em arquivo no próprio servidor, devemos:

  • Remover os cabeçalhos que força o navegador a entender que é PDF o conteúdo retornado;
  • Trocar os métodos getOutput e getOutputFromHtml por generate e generateFromHtml, respectivamente. Sendo que os métodos aceitam um segundo parâmetro obrigatório, que é o caminho completo pra o arquivo que será gerado com o conteúdo:

Gerar PDF de uma URL e salva no servidor

<?php 

// Código anterior, sem o getOutput(...);

$snappy->generate('http://www.schoolofnet.com/blog/', '/app/arquivos/son/home-blog.pdf');

Gerar PDF com conteúdo de várias URLs e salva no servidor

<?php 

// Código anterior, sem o getOutput(...);

$snappy->generate(
    [
        'http://www.schoolofnet.com/2015/07/trabalhando-com-repository-no-laravel/',
        'http://www.schoolofnet.com/2015/04/como-usar-os-metodos-magicos-no-php/',
        'http://www.schoolofnet.com/2015/04/enviando-emails-utilizando-swift-mailer/',
        'http://www.schoolofnet.com/2015/04/instalando-e-integrando-apache-com-php-no-windows/',
    ],
    '/app/arquivos/son/artigos.pdf'
);

Gerar PDF a partir de um HTML e salva no servidor

<?php 

// Código anterior, sem o getOutputFromHtml(...);

$snappy->generateFromHtml($html, '/app/arquivos/relatorios/relatorio.pdf', ['encoding' => 'UTF8']); 

Em todos os casos onde ser abre o arquivo no navegador, se quisermos ao invés de mostrar no navegador que seja forçado o download do arquivo, colocamos o cabeçalho de Content-Disposition: attachment;, assim:

  • Se for PDF: header('Content-Disposition: attachment; filename="schoolofnet-blog-home.pdf"');
  • Se for Imagem: header('Content-Disposition: attachment; filename="schoolofnet-blog-home.png"');

Bastando somente modificar o valor do parâmetro filename, para o nome que deseja com a extensão equivalente ao que está no cabeçalho Content-Type.

Considerações finais

Com o Snappy facilita a geração de PDF ou até imagens/snapshot de páginas, mas também serve tranquilamente para relatório. Basta considerar em gerar o html do relatório e passar para o Snappy ou até mesmo o relatório está em uma URL e você passar a URL para ele. Todo o CSS será considerado. Caso tenha versão de CSS para o media-type print, basta passar no $option a opção para forçar esse media-type. Veja na classes classes use Knp\Snappy\Pdf ou use Knp\Snappy\Image como fazer, mas as opções são do próprio wkhtmltopdf, bastando dá o comando dele com o -H e terá acesso a documentação.

Existe outra biblioteca que faz wrapper no wkhtmltopdf, caso deseje saber alternativas: phpwkhtmltopdf.

No packagist existem pacotes para diversos frameworks:

Repositório com o código