Tecnologia

[How to] – Criar relatórios em PDF a partir de HTML

Por: , outubro 28, 2013

Acredito que seja consenso o fato de que “montar um relatório usando HTML, CSS e JavaScript é muito mais fácil do que um em PDF (usando iText, Crystal Reports entre outros)”. E isso por diversos motivos, e citarei apenas um: é muito mais provável que o cliente ou webdesigner não saiba como fazer um relatório em Java, mas tenha maestria com HTML, CSS e JS. Todavia o formato PDF é amplamente requisitado, então acaba-se fazendo o relatório da maneira mais complexa e custosa – em tempo e depois em manutenção.

Podem até dizer que existem APIs em Java e outras linguagens que convertem o HTML em PDF, mas muitas vezes ela falha por completo, ou tem de se fazer algum “ajustezinho”, ou “soluções mirabolantes” para tratar de um caso, com outra para outro caso, então o layout muda, e essa “solução” já não serve mais. Por exemplo, se as larguras das colunas de uma tabela estiverem em uma tag HTML col – o que é comum quando o HTML é gerado via alguma ferramenta, como Pentaho Report Designer – o com.itextpdf.tool.xml.iText.XMLWorkerHelper se perde na transformação HTML -> PDF, pois ele remove todas as tags de conteúdo antes de gerar o PDF, desformatando a tabela por consequência.

E como fazer para explicar ao cliente que não dá para gerar o relatório do jeito que ele quer, sendo que ao clicar em “Imprimir como PDF” no browser fica como ele deseja?

Bom, se o browser consegue, por que nós não (e de forma simples)? E se usássemos essa funcionalidade do browser a nosso favor? Mas tem como? O servidor nem tem interface gráfica… e agora?

Simples! Use um browser headless! Parece estranho, ou inusitado, não?

Dado que o relatório já existe em HTML (ou não há uma API bem desenvolvida e simples para gerar o PDF), o browser headless entra em cena.

O PhantomJS, que é idêntico a um browser comum, mas não tem saída em tela, é muito utilizado para testes, tem base em JavaScript, é gratuito e open source, perfeito para o que queremos. E por ser usado para testes, era de se esperar que ele tivesse alguma funcionalidade nativa de imprimir a “tela” para verificar se os testes estão certos, ou “debugar”. Felizmente essa expectativa não é frustrada, e o PhantomJS de fato tem a funcionalidade nativa de imprimir o que estaria na tela, caso ele usasse interface gráfica, como PDF!

 


Veja como funciona

Se o browser pedir ao servidor o relatório em HTML, tudo segue normal.

Se o browser pedir ao servidor o relatório em PDF, então o servidor pede ajuda ao PhantomJS, que faz uma requisição ao servidor do relatório em HTML, então o PhantomJS grava o HTML em formato PDF em algum lugar em disco, o servidor lê esse PDF, e retorna para o usuário.

 

Receita

Existe um arquivo que já vem com o PhantomJS que se chama “rasterize.js” e vamos usá-lo. Ele é um código simples em JavaScript que recebe como entrada uma URL, o nome de arquivo de saída, e , opcionalmente, o formato (largura*altura ou tipo, como A4) do PDF e um fator de zoom. Caso o arquivo de saída seja um tipo de imagem, ele grava uma imagem do conteúdo da URL, caso seja PDF, ele faz o mesmo, só que o arquivo será um PDF.

Ingredientes

1 binário do PhantomJS fresco

1 JavaScript que gera o output em PDF, como o examples/rasterize.js

1 servidor a gosto

 

Modo de preparo

  1. Baixe o PhantomJS de acordo com as configurações da máquina do servidor escolhido.

  2. Mova o bin/phantomjs(.exe) e o examples/rasterize.js para locais estratégicos de fácil acesso ao seu servidor (#Tip: guarde os diretórios em algum arquivo de propriedades de fácil leitura/escrita).

  3. Adicione um serviço GET em seu servidor que

    1. receba uma URL como parâmetro

    2. execute “phantomjs rasterize.js URL UUID.pdf A4”

    3. retorne “application/pdf” com conteúdo os bytes lidos do UUID.pdf.

  1. Faça deploy e sirva PDFs.

 

Note que a solução funciona sempre que se tenha espaço para escrever o relatório em disco, e saiba onde seu servidor está (seu IP pelo menos, de alguma forma) para poder instalar o PhantomJS nele.

Usamos um UUID para que não se corra o risco de retornar ao clientside o relatório pedido por outro usuário.

Aos mais curiosos, vale a pena abrir o rasterize.js e customizá-lo, o que é simples, pois é tudo JavaScript.

Curtiu? Tem uma solução melhor? Essa solução não cabe ao seu ambiente? Dificuldades em colocar header e footer? Deixe seu comentário!
 


  • Receba nosso conteúdo em primeira mão.