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.
Procedimento necessário
- 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.
- 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);
- Passar o documento para o Flying Saucer:
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ITextRenderer renderer = new ITextRenderer(); renderer.setDocument(doc, rootPath); renderer.layout(); renderer.createPDF(bos);
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.dtdE 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.
A solução passo a passo seria a seguinte:
- Procure o projeto catalog da apache. Coloque o resolver.jar no seu classpath... (WEB-INF,etc)
- Baixe o sgml-lib da w3c. Adicione o contéudo do sgml-lib no diretório de códigos-fonte da sua aplicação.
- 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
- E adiciona o suporte ao catálogo no Parser.
builder.setEntityResolver(new CatalogResolver());
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