Usando wkhtmltopdf e um daemon Xvfb para renderizar HTML em PDF

HERETIC – DESIGN + TECNOLOGIA + MARKETING

Tenho muitos projetos que consistem em coletar informações e depois renderizá-las em PDF, inicialmente usei o tcpdf, mas a renderização é MUITO exigente. Eu estava fazendo algumas pesquisas adicionais há algumas semanas para um projeto pessoal e encontrei wkhtmltopdf.

Eu estava raspando minhas planilhas de tempo do Harvest para reunir minhas horas das duas semanas anteriores, renderizando em PDF e criando automaticamente um documento RightSignature para ser assinado por meus empregadores. Se você já teve que fazer isso, pode ser um tanto chato fazer isso repetidamente, especialmente quando você está muito ocupado. Além disso, automatizar as coisas é incrível.

Para esse processo, usei alguns comandos python (com solicitações), wkhtmltopdf e Xvfb (X virtual framebuffer) xvfb-run. Isso é bom, porque eu executo o processo manualmente, à vontade.

Tenho alguns outros projetos que exigem renderização repetida em um site ao vivo, para vários usuários. Executar processos xvfb-run individuais para cada solicitação é apenas uma ideia idiota. Seu servidor vai explodir se você fizer isso. Não faça isso.

Para usar um único processo Xvfb, eu precisei configurá-lo como um daemon que é executado pelo usuário www-data, para que o wrapper de PHP que estou usando (PHPWkHtmlToPDF) possa acessar o buffer de quadro virtual e usar o webkit mecanismo de renderização (Chrome) para salvá-lo em PDF.

Eu poderia (e provavelmente irei) configurar uma fila de processamento usando rabbitmq ou similares, mas por agora (e para este post), tudo bem.

Vamos começar, vamos?

Estas instruções são centradas no Ubuntu, então sua milhagem pode variar dependendo da configuração que você está usando.

Etapa 1: Instale wkhtmltopdf e xvfb

# apt-get install wkhtmltopdf xvfb

Etapa 2: crie e ative o script de inicialização para o daemon xvfb

Nota “: 0 ″ ​​em XVFBARGS que define para exibir 0, e o parâmetro –set-guid, que executa o daemon como o usuário www-data. Se você não estiver executando um servidor headless, certifique-se de alterar todas as referências a “: 0 ″ ​​para“: # ”(ou seja:: 1,: 2 ou: 22) para evitar conflitos com sua (s) sessão (ões) X existente (s)

Coloque o seguinte em “/etc/init.d/xvfb”:

XVFB=/usr/bin/Xvfb
XVFBARGS
=":0 -screen 0 1024x768x24 -ac +extension GLX +render -noreset"
PIDFILE
=/var/run/xvfb.pid
case "$1" in
start
)
echo
-n "Starting virtual X frame buffer: Xvfb"
start
-stop-daemon --chuid www-data --start --quiet --pidfile $PIDFILE --make-pidfile --background --exec $XVFB -- $XVFBARGS
echo
"."
;;
stop
)
echo
-n "Stopping virtual X frame buffer: Xvfb"
start
-stop-daemon --chuid www-data --stop --quiet --pidfile $PIDFILE
echo
"."
;;
restart
)
$0 stop

$0 start

;;
*)
echo
"Usage: /etc/init.d/xvfb {start|stop|restart}"
exit 1
esac

exit 0

Habilite seu script de inicialização:

# update-rc.d xvfb defaults 10

Execute seu script init:

# /etc/init.d/xvfb start
Starting virtual X frame buffer: Xvfb.

Verifique se o seu Xvfb está em execução:

# ps auxU www-data | grep [X]vfb
www
-data 17852 0.0 0.2 54960 7904 ? S 04:31 0:00 /usr/bin/Xvfb ...

Ótimo! O daemon está instalado e funcionando (espero).

Etapa 3: modifique o wrapper PHPWkHtmlToPdf

Precisamos modificar a chamada do wrapper para wkhtmltopdf para que ele saiba que estamos usando uma sessão X específica (consulte as notas sobre “: 0 ″ ​​acima)

Abra a classe / arquivo WkHtmlToPdf.php e encontre a função “getCommand” (em torno da linha # 252 no momento de escrever este post) e adicione uma linha ao início da variável de comando que exporta a variável DISPLAY para corresponder ao número de exibição que você usava. No meu caso é “: 0 ″, já que estou executando um servidor headless.

/**
* @param string $filename the filename of the output file

* @return string the wkhtmltopdf command string

*/

public function getCommand($filename)
{
$command
= 'export DISPLAY=":0";';
$command
.= $this->enableEscaping ? escapeshellarg($this->bin) : $this->bin;

$command
.= $this->renderOptions($this->options);

foreach ($this->objects as $object) {
$command
.= ' ' . $object['input'];
unset
($object['input']);
$command
.= $this->renderOptions($object);
}

return $command . ' ' . $filename;
}

Salve e você estará pronto para….

Etapa 4: RENDER!

Eu uso o twig para preencher alguns modelos de html, mas por causa de um exemplo, usaremos PHP para pegar a página inicial do Google e renderizá-la em um PDF.

include "classes/WkHtmlToPdf.php";

// instantiate the wrapper
$pdf
= new WkHtmlToPdf;

// add the content, addPage can take HTML strings, URLs, or filenames.
$pdf
->addPage("http://google.com");

// for this example, we'll just use send, which sends the PDF directly to the browser
// if (!$pdf->saveAs('/tmp/google.pdf')): // this saves to a file
if (!$pdf->send()):
throw new Exception('Could not create PDF: ' . $pdf->getError());
endif
;

Salve isso, execute-o e você verá um PDF bem renderizado da página inicial do Google.

Existem algumas opções para tornar a renderização um pouco mais precisa, mas você pode vê-las em http://mikehaertl.github.io/phpwkhtmltopdf/ .

Aproveite e feliz renderização!