COMO GERAR RELATÓRIOS EM HTML COM PAGINAÇÃO
1. Relatórios puramente HTML: pesadelo do desenvolvedor, com uma solução simples e elegante
Qual a utilidade deste artigo?
Ele se aplica a qualquer desenvolvedor que já tenha imaginado em criar todos os
relatórios do seu sistema em HTML, para caso precise migrar para outra linguagem
futuramente ou portar parte do sistema para web, pelo menos isso já saia funcionando
sem grandes mudanças. Qualquer um que já tenha desenvolvido grandes sistemas empresarias
tem noção de que muitas vezes relatórios e formulários chegam a representar mais
de 50% do código total do programa. Também torna-se útil para quem já teve (como
eu) necessidade de criar um formulário de pedido ultra complexo, com tabelas aninhadas,
imagens de produtos que podem exceder o tamanho restante da página e conteúdos de
texto MEMO com trechos de formatação própria.
Claro que tudo isto pode ser feito em PDF, ou desenhando diretamente na impressora,
mas recai então sobre o programador a responsabilidade fazer todos os cálculos,
verificações, quebras de página, saltos de linha, redimensionamentos, margens, cabeçalhos e rodapés que se façam necessários.
Como tempo é dinheiro, esta é a utilidade deste artigo: permitir que se faça mais
com menos, e com mais qualidade.
Então, imagine a seguinte situação: "temos um relatório em HTML, consistente de uma tabela,
um cabeçalho geral apresentando os dados da Empresa que o emitiu, e um rodapé indicando informação adicional.
O que se necessita é que em todas as páginas impressas seja repetido o cabeçalho com as informações da Empresa,
os cabeçalhos das colunas da tabela de dados, e a informação de rodapé. A informação de "dados" reais deve ser
impressa ao longo das páginas.".
Em princípio parece simples, porém ao imprimir um relatório em tabela com várias páginas, logo você notará que
o cabeçalho da Empresa e da tabela será exibido apenas na primeira página, depois todo o conteúdo da tabela, e
em seguida o rodapé. Em resumo: perde-se o vínculo entre as páginas, no que se refere a indicar quais os rótulos das
colunas, e qual Empresa emitiu o relatório. Se o objeto é identificar o relatório com um logotipo da empresa que o emitiu,
então a situação piora.
Esta é uma questão de suma importância para quem gera relatórios e formulários de
documentos em HTML, já que os componentes de HTML, sejam OCX ou DLLs, em sua grande maioria não nos dão
opções de personalização quanto a este aspecto (salvo componente pagos - e bem pagos -
que tornam inviável sua utilização em projetos menores).
Para completar a dificuldade do desenvolvedor, os navegadores de internet, como IE e Firefox, ainda
que automatizados via OLE ou DDE, não oferecem nada além de uma simples linha de cabeçalho e outra
de rodapé aceitando apenas textos simples e umas poucas variáveis de ambiente.
Então, é disso que trata este artigo: não diretamente .NET, mas de código CSS integrado a código HTML que pode ser
facilmente integrado em qualquer aplicação (VB6, .NET, C#, Java, etc), para permitir controlar o aspecto
de um relatório em HTML na impressão.
2. Poucas linhas que fazem mágica
Após procurar por diversos lugares, acabei encontrado uma referência a poucas linhas de código que resolvem a questão de forma simples.
Obviamente a maioria das dicas encontradas estavam coalhadas de código adicional, formatações, código CSS estranho, e uma infinidade de
coisas que não faziam muito sentido para o problema em si, que era manter a estrutura do documento na paginação de impressão.
Portanto, o caminho foi perder horas reduzindo o código e testando, até chegar finalmente ao menor trecho de código que
fazia acontecer este efeito tão relevante.
Considere o exemplo abaixo, imaginando que isto possa ser a representação da estrutura
de uma página:
|
ESTE É O CABEÇALHO |
Linha 1 do conteúdo
Linha 2 do conteúdo
Linha 3 do conteúdo
Linha 4 do conteúdo
Linha 5 do conteúdo
(...) |
|
Este é o Rodapé |
Esta tabela é gerada da forma tradicional:
<table border=1 align="center" width="100%" cellpadding=0 cellspacing=0>
<tr>
<th width=100%>
ESTE É O CABEÇALHO</th>
</tr>
<tr>
<td width="100%">
Linha 1 do conteúdo<br>
Linha 2 do conteúdo<br />
Linha 3 do conteúdo<br />
Linha 4 do conteúdo<br />
Linha 5 do conteúdo<br />
(...)</td>
</tr>
<tr>
<td width=100%>
Este é o Rodapé</td>
</tr>
</table>
Caso sejam adicionadas diversas linhas como conteúdo, na sequencia de "Linha N do conteúdo", a tabela será impressa como um formulário contínuo, conforme é o funcionamento padrão, exibindo o texto "ESTE É O CABEÇALHO" apenas no começo da primeira página
o texto "Este é o Rodapé" apenas no final da última página.
Agora, desenhe a tabela usando o seguinte código:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML>
<HEAD>
<style type="text/css">
thead { display: table-header-group; }
tfoot { display: table-footer-group;
overflow: visible;
}
</STYLE>
</HEAD>
<BODY>
<table border=0 align="center" width="100%" cellpadding=0 cellspacing=0>
<thead>
<tr>
<th width=100%>
ESTE É O CABEÇALHO</th>
</tr>
</thead>
<tfoot>
<tr>
<td width=100%>
Este é o Rodapé</td>
</tr>
</tfoot>
<tbody>
<tr>
<td width="100%">
Linha 1 do conteúdo<br>
Linha 2 do conteúdo<br />
Linha 3 do conteúdo<br />
Linha 4 do conteúdo<br />
Linha 5 do conteúdo<br />
(...)</td>
</tr>
</tbody>
</table>
</BODY>
</HTML>
Este código fará exatamente o desejado: será impresso o conteúdo da primeira até
a última página, tantas páginas quantas necessárias, repetindo-se cabeçalho e rodapé
em todas as páginas. Ainda, o cabeçalho e o rodapé poderão conter imagens, linhas
e formatação complexa, e as tags <TD> usadas para compôr a tabela podem ser
divididas em quantas colunas quantas necessárias, apenas respeitando que a quantidade
de colunas de
tbody,
thead e
tfoot
seja a mesma.
Importante: caso seja preciso aninhar tabelas, basta repetir a mesma estrutura de
tabela dentro da tag
tbody / tr / td existente, e a tabela contida
na tabela terá o mesmo comportamento, ou sejá é possível criar-se por exemplo um
formulário de pedido, contendo o cabeçalho da empresa, conteúdo geral, como dados
do cliente, e em seguida a tabela de produtos. Ao visualizar em tela (navegador
ou componentes HTML) será exibido como um documento corrido. Ao imprimir, será colocado
em todas as páginas o cabeçalho da empresa, e o cabeçalho da tabela de produtos
em todas as páginas necessárias para a mesma.
Como dito anteriormente, este é um tópico altamente prático e produtivo, portanto
o código está reduzido a um mínimo, permitindo que você apenas altere os dados em
thead,
tbody e
tfoot, e já consiga
implementar seus próprios relatórios sem preocupar-se com impressão.
Você pode fazer experiências adicionando outros componentes aninhados e testando
o comportamento, enviando por e-mail e imprimindo na máquina que receber o documento
HTML. As possibilidades em cima destas poucas linhas de código são enormes, e os
resultados compensadores, especialmente para que costuma trabalhar com Sistemas
de ERP, com relatórios e documentos bastante completos, e exigindo personalização
de documentos de cliente a cliente. Realmente, na prática isto é uma grande
mão
na roda.
Se você deseja apenas o código em si para resolver o problema, então isto é suficiente:
copie, cole e use.
Caso você seja daqueles programadores curiosos que precisam saber como a coisa funciona,
ou apenas precise de segurança de que o código vai realmente funcionar, veja o tópico
seguinte.
3. Um pouco de teoria: como funciona este código?
Claro que como qualquer programador que tenha visto este código em ação pela primeira
vez, e compreendendo sua abrangência, surge a famosa pergunta do "mas, e como?!".
Pois então vamos aos fundamentos:
Basicamente o código é composto de uma parte em CSS e outra em HTML puro, que desenha
a tabela em si.
A parte de código em HTML é composta de uma tabela, contendo 3 seções: cabeçalho
(thead), rodapé (tfoot), e conteúdo (tbody), sendo que cada uma das partes será
impressa da forma já conhecida e esperada. Tudo que for colocado entre as tags <thead></thead> será
considerado cabeçalho, entre as tags <tfoot></tfoot> será rodapé e o <tbody></tbody> delimita o conteúdo. Em qualquer uma das seções pode ser
adicionada qualquer tag HTML, imagens, formatações, etc. respeitando-se é claro
as dimensões da página em relação a elementos únicos (por exemplo tentar adicionar
um rodapé com uma imagem 1024x768, a ser impressa em papel A4). Outro tópico importante
é que como trata-se de uma tabela, todos os elementos descritos precisam conter
o mesmo número de colunas
<TD> e tantas linhas <TR>
quantas sejam desejadas. Pasta simplificar, pense na página impressa como sendo
uma tabela simples de 3 linhas e 1 coluna.
Caso você conheça um pouco de HTML, terá notado que se forem tiradas as tags de
seção descritas acima, a tabela se parecerá com uma tabela normal. Isto determina
a funcionalidade destas 3 tags: indicar quais são as 3 partes do documento.
Mas então o que determina como cada seção será impressão?
O código CSS, está delimitado entre as tags <style type="text/css">
e </style>, e
contém os seguintes elementos:
thead { display: table-header-group; }
tfoot { display: table-footer-group;
}
O CSS ou "folha de estilos" é utilizado para determinar
de que forma partes de um documento html devem ser apresentadas, formatadas ou dimensionadas.
Ele foi criado especificamente para auxiliar na separação do conteúdo de uma página
HTML de sua formatação. Torna possível que se crie um texto e este seja reformatado
sob diversos padrões sem que seja preciso mudar uma linha no texto de conteúdo em
si. Sua sintaxe lembra um pouco de java ou C e derivados.
Para quem nunca trabalhou
com CSS antes: thead {} e tfoot {}
servem para indicar todos os blocos de formatação a serem aplicados aos elementos
com estes nomes na página. Dentro de cada bloco de chaves
{}podem estar contidos diversos comandos de formatação, como fonte, cores,
imagens de fundo, bordas, dimensões relativas ou absolutas, etc. Vale fazer algumas
experiências com um editor CSS para ver os resultados surpreendentes e flexíveis
que se pode obter.
Voltando ao nosso código: o CSS acima está indicando basicamente uma única instrução:
display:. Como valor deste comando estão sendo
passados:
- table-header-group - indica
que este bloco de código deve ser exibido antes
de todo o resto da tabela, conforme a mídia;
-
table-footer-group - indica que este bloco de código
deve ser exibido depois de todo
o resto da tabela, conforme a mídia;
Conforme consta no
w3.org, notamos que estas duas instruções, bem como outras adicionais, podem
ser aplicadas a qualquer bloco de código HTML, como por exemplo um <p>, <pre>,
etc. porém utilizando da forma indicada mantemos o vínculo de pensamento como uma
tabela estruturada, tornando o código mais legível. Obviamente isto é questão de
gosto.
Ainda, o código CSS não precisa ser aplicado no cabeçalho do html, podendo ser incluso
diretamente na tag HTML, como por exemplo <thead style="display:
table-header-group;">, mas isto acaba misturando a formatação
com a estrutura, e qualquer desenvolvedor ASP ou PHP com um pouco de prática sabe
o quanto é fácil criar um código confuso e difícil de manter seguindo por este caminho.
Observação: vale comentar
sobre o bloco tfoot, que o comando overflow:
visible; foi acrescentado para forçar o navegador a exibir
o rodapé, ainda que esteja um pouco fora de posição, conforme cálculos e arredondamentos
executados pelo navegador. O comando permite que o bloco seja desenhado fora de
sua área delimitada, caso não caiba ou tenha sido "empurrado" por elementos anteriores.
Caso este comando seja omitido, em alguns navegadores observa-se que o rodapé é
exibido em apenas algumas páginas, e sempre é exibido na última página.
A técnica funciona porquê em tela o HTML é visto como uma
única página, então o comportamento não é afetado. Na impressora, cada página é
"vista" como uma pequena tela, e o texto é "rolado" sobre elas levando em conta
as regras do CSS. Com algumas modificações do código, pode-se simular em tela o
comportamento de paginação, criando-se uma espécie de "preview" de impressão, porém
este não é o objetivo deste artigo. Também pode-se criar duas versões do mesmo relatório
apenas indicando o tipo de mídia e duas classes CSS distintas, usando o mesmo conteúdo
e dentro do mesmo arquivo. Para aqueles que notaram as potencialidades disso, sugiro
um bom livro ou uma consulta ao w3.org, que controla os padrões.
Limitação: como nem tudo são flores, esta técnica
não permite que seu código saiba em que página ele está, ou seja, não se pode incluir
no cabeçalho personalizado o número da página ou o total de páginas. Até onde descobri,
pode-se fazer isso ou usando os recursos limitados de cabeçalho do navegador, ou
via script. Penso que talvez o CSS 3.0 já tenha alguma disposição sobre isto.
4. Conclusões
Obviamente estas explicações são simplistas, e servem apenas
para dar uma noção dos comandos empregados e do que se pode fazer com eles.
Resumidademente, a técnica empregada consiste em dividir um documento em uma tabela
HTML, delimitando os elementos da tabela com as tags thead,
tfoot e tbody, e em seguida controlando a exibição
destes elementos através de CSS, com a instrução display:.
Os conceitos fundamentais do CSS permitem fazer-se uma série de coisas de forma
mais simples e deixando por conta de programas já testados e especializados, como
esta questão do relatório.
Da próxima vez que você pensar em desenvolver um relatório complicado em sua linguagem
de programação preferida, desenhando diretamente na impressora ou usando APIs, perdido
entre coordenadas diversas (twips, pixels, polegadas, etc), lembre-se de que na
grande maioria dos casos é possível obter um resultado com qualidade superior, e
sendo muito mais simples e flexível na montagem, através do HTML com CSS. Obviamente
podem ser empregados recursos avançados nesta finalidade, como flash ou scripts,
porém tais recursos são caros, misturam muitas tecnologias e normalmente são bloqueados
por controles de segurança dos navegados ou componentes.
E o melhor de tudo: usando HTML com CSS você estará garantindo que seu relatório
possa ser reaproveitado em qualquer migração de linguagem, ou se você precise portar
seu sistema para web (ASP, PHP, etc.).