Pular para o conteúdo principal

Do Browser para Impressora - Considerações Finais

Problemas com XML-FO
Solução Flying Saucer


Apesar de ser apreciável a iniciativa da Apache com o XML-FO, o Apache FOP não está suficiente maduro para ser utilizado em qualquer
ocasião. Uma interessante alternativa é outro projeto que realiza uma transformação XSL mas diretamente para PDF. Esse é o projeto Flying
Saucer, que contém entre outras coisas suporte para CSS3. Para o meu problema, O Flying Saucer tem mostrado como a melhor solução em termos de precisão além de contar com qualidades como flexibilidade e eficiência.

Podemos verificar no projeto que ele faz uso do iText para gerar o arquivo PDF, mas tem a possibilidade de utilizar renderizadores para arquivos de saída diferentes.

Procedimento necessário

  1. Instalar o Flying Saucer e suas dependências no Classpath do seu projeto. No caso de um projeto WEB, adicionar as bibliotecas no
    diretório WEB-INF.
  2. Utilizar o JAPX (The Java API for XML Processing ) para converter um arquivo XML num documento DOM (org.w3c.dom.Document).
    DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
    InputSource inputSource = new InputSource(new InputStreamReader(arrayInputStream, "ISO-8859-1"));
    builder.setEntityResolver(new CatalogResolver());
    Document doc = builder.parse(inputSource);
    
  3. Passar o documento para o Flying Saucer:
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ITextRenderer renderer = new ITextRenderer();
    renderer.setDocument(doc, rootPath);
    renderer.layout();
    renderer.createPDF(bos);
    
PROBLEMAS COM ANÁLISE SINTÁTICA
Solução XML Catalog

Cabe aqui uma observação sobre o uso do Docbuilder – o analisador sintático (parser) de XML da especificação JAPX. O comportamento padrão do DocumentBuilder é processar o arquivo de origem com base nos recursos descritos nele mesmo, incluindo seus descritores como o DTD ou
XSD. O problema que a declaração do tipo do documento (Document Type Declaration or Doctype) geralmente aponta para a URL do publicador. Por exemplo :

DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd
E especificações grandes como o Xhtml são particionados em vários fragmentos dtd que apontam para outros resultando numa míriade de documentos virtualmente remotos.

Para que o Docbuilder faça a análise sintática, é necessário que o processo tenha acesso a essas localizações. Num servidor de aplicação, acessos remotos poderiam ser impedido por questões de segurança. Nesse caso é preciso ter uma cópia de cada recurso localmente e utilizar um proxy que converta as localizações virtualmente remotas para os arquivos locais.

Na verdade, seria possível abrir arquivo por arquivo e procurar as referências remotas e trocá-las por referências locais, mas isso poderia dar um trabalho demasiado grande e sujeito a erros.

XML CatalogO propósito do XML Catalog da Apache Foundation (explicado em detalhes nesse artigo) é servir de proxy para o analisador sintático encontrar os recursos descritos nos documentos com localizações universais. A solução é bem simples. Uma objeto (Factory) cria o proxy com base num arquivo properties localizado no mesmo nível que o classloader. Isso é feito de forma transparente, ao instanciar um objeto da classe CatalogResolver.

A solução passo a passo seria a seguinte:

  1. Procure o projeto catalog da apache. Coloque o resolver.jar no seu classpath... (WEB-INF,etc)
  2. Baixe o sgml-lib da w3c. Adicione o contéudo do sgml-lib no diretório de códigos-fonte da sua aplicação.
  3. Adicione também o arquivo Catalog.properties.
    relative-catalogs=no
    catalogs=sgml-lib/xml.soc
    verbosity=0
    prefer=system
    allow-oasis-xml-catalog-pi=no
    static-catalog=yes
    
  4. E adiciona o suporte ao catálogo no Parser.
    builder.setEntityResolver(new CatalogResolver());
    
    
Problemas com Mime Type
Solução: Especificar o Mime Type


Ter um arquivo PDF em memória, não garante sua renderização correta no cliente. Essa questão é resolvida com atributos MIME. Aespecificação dos Servlet descreve como os tipos MIME podem ser especificados na interface de resposta (Response) do Servlet.

response.setContentType("application/pdf");
response.setHeader("Content-disposition", "attachment; filename=NotaFiscal.pdf");
response.setContentLength(bos.size());
OutputStream os = response.getOutputStream();
bos.writeTo(os);
os.flush();
os.close();
Problemas com o processo manual de imprissão
Solução: Incluir Javascript no PDF

Em casos extremos, fornecer um arquivo PDF para o usuário, com o Adobe Acrobat Reader instalado em sua máquina não é o suficiente paraque ele se sinta a vontade para imprimir o documento corretamente. Para evitar maiores transtornos com a parte de treinamente, é possível automatizar o processo de impressão.

Para isso, felizmente, o arquivo PDF permite a inclusão de scripts que, por sua vez, permitem a interação entre os recurso da máquinacliente e o documento a ser exibido/imprimido. É uma excelente oportunidade para demonstrar como a API do iText pode incluir Script em um PDF ao mesmo tempo que automatizo a ação de imprimir na máquina cliente. Para realizar tal tarefa é necessário duplicar o documento a ser imprimido.

PdfReader reader = new PdfReader(bytes);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
com.lowagie.text.Document document = new com.lowagie.text.Document(new Rectangle(585, 936), 0 ,0 ,0 ,0);
PdfWriter writer = PdfWriter.getInstance(document, bos);
document.open();
PdfContentByte cb = writer.getDirectContent();
document.newPage();
PdfImportedPage page = writer.getImportedPage(reader, 1);
cb.addTemplate(page, 0, 0);

Agora, basta incluir o fragmento Javascript via PDFWriter:

writer.addJavaScript("this.print({bUI: false,bSilent: false,bShrinkToFit: true});" + "\r\n"+ "this.closeDoc();");
document.close();
return bos;

Ufa! Isso é tudo.

Comentários

Postagens mais visitadas deste blog

Expressões, preconceito e racismo

Expressões preconceituosas e racistas Antes de alguma outra frase, primeiro peço licença para falar de mais um assunto do qual não domino. Falo por acreditar que um leigo presta serviço maior ao debater assunto com base em fontes (ainda que seja uma Wikipedia) e no pensamento lógico do que simplesmente se manter mudo a questões do cotidiano. Em voga agora está em falar quais são ou eram as expressões preconceituosas e racistas que até a pouco eram toleradas em muitos meios. Como é covarde dizer que em boca fechada não entra racismo. O racismo não é perpetrado apenas por quem profere mas por quem se cala à agressão perpetrada a outrem. Mas veremos que a questão é muito mais complexa que os cães raivosos do politicamente correto querem dizer. Tomo aqui a palavra racista, como sendo algo usado para impor a dominação de uma “raça” sobre outra. Portanto, a acusação de racismo vai muito além da mera acusação de preconceito. Não tenho o menor apreso por vitimismo barato, onde expressões q...

A hard logic problem - The escape of blue eyed vampires

Once upon a time, a vampire clan lived peacefully on an island (as long as vampire clans can live peacefully). Then, a demon lord came, overwhelmed the vampires and became the ruler of the island. The demon didn't want any vampire to escape so he created a gargoyle to guard the only way out. This gargoyle was a fantastic creature, so powerful that he was kept petrified for the whole time until a vampire appears. Then he awakened and started to fight until seeing no more vampire "alive" (as far a vampire can be alive). All vampires crazy enough to try were killed only left a hundred of vampires. There was a catch, of course. The gargoyle was not perfectly designed. It did not awaken when blue eyes vampires appeared. And all remaining vampire were blue eyes but as you know vampires cannot see him/her selves on reflections. For any reason, they were not aware of their eye colors. Besides all that, blue eyed vampires didn't like each other (so they would never say ...

Curry with JS

Partial application and currying with Javascript In the strict way, currying is the technique of transforming a function that takes multiple arguments (a tuple of arguments) to one function that receive only one. In such way, currying techniques allow transform one multi-parameter function in a chain of functions, each one with a single argument. Looks complicated? Blah.. it is not true. In this little article, we are actually more interesting in partial applications. Let’s take the Mozilla Example for replace function in String. As we know, we can use a “replacer” function as paramenter for replace method in String object. Let’s say that we want to split a String defined by a non-numerical part, a numerical part and finally a non-alphanumeric part. Here is how: function replacer(match, p1, p2, p3, offset, string){ // p1 is nondigits, p2 digits, and p3 non-alphanumerics return [p1, p2, p3].join(' - '); }; We can try it as usual… var newString = "abc12345#$*%...