Estoy trabajando en el sistema de recaudacion de una organizacion. Uno de los requerimientos es poder generar y descargar un PDF con java que contenga el detalle de pagos a realizar por uno de los actores.
Apoyandome en este articulo de Baeldung resolvi utilizar Thymeleaf para ṕoder tener templates HTML.Esto permite darle formato a los Documentos generados junto con la ayuda de la biblioteca flying-saucer-pdf
Estas son las dependencias nacesarias dentro del pom.xml
<dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>3.0.6.RELEASE</version> </dependency> <dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-core</artifactId> <version>9.1.6</version> </dependency>
Dentro del directorio resources se colocan los html que serviran de template. Siendo cuidadosos de utilizar el siguiente formato para aquellos atributos que es necesario completar dinamicamente en runtime.
Directorio:
src/main/resources/pdf_templates/voucher.html
Formato del Artibuto dentro del template:
[[${nombreDeAtributo}]]
Una vez creado el html que usaremos de template, en la clase service es necesario implementar un metodo que permita utilizar ese template y pasar los atributos correspondientes.
@Override public String buildHtmlFromTemplate(String att1, Date att2, String att3, String att4) { ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver(); templateResolver.setSuffix(".html"); templateResolver.setTemplateMode("HTML"); templateResolver.setCacheable(false); TemplateEngine templateEngine = new TemplateEngine(); templateEngine.setTemplateResolver(templateResolver); Context context = new Context(); context.setVariable("att1", att1); context.setVariable("att2", att2); context.setVariable("att3", att3); context.setVariable("att4", att4); return templateEngine.process("pdf_templates/voucher", context); }
Este codigo lo que nos permite justamente es decirle al template que vamos a trabajar con un codigo html. Luego el engine recibe el nombre del template a utilizar junto con el mapa de atributos y sus valores.
En el ejemplo se utiliza un tipo Date para demostrar que se permiten multiples tipos de dato.
El siguiente metodo, se encarga de recibir el html generado en el paso anterior con los valores correspondientes a cada atributo. En estas lineas, se utiliza el renderer de itext para justamente renderizar ese html y generar un ByteArrayoutputStream quien fuera el que nos permite descargar el PDF generado:
@Override public ByteArrayOutputStream generateVoucherDocumentBaos(String html) throws IOException, DocumentException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ITextRenderer renderer = new ITextRenderer(); renderer.setDocumentFromString(html); renderer.layout(); renderer.createPDF(baos); baos.close(); return baos; }
La frutilla del postre la termina de realizar la capa web en nuestro controller. Para fines didacticos, paso los atributos «hardcodeados», pero en este punto es imperativo que en un paso anterior la capa de negocio haya resuelto los valores de los mismos.
@GetMapping("/download") public void download(HttpServletResponse response) throws IOException, DocumentException { //TODO implementar logica para obtener los datos del voucher String htmlInvoice = voucherService.buildHtmlFromTemplate(UUID.randomUUID().toString(), new Date(), "Esto es un string", "[email protected]"); ByteArrayOutputStream bos = voucherService.generateVoucherDocumentBaos(htmlInvoice); byte[] pdfReport = bos.toByteArray(); String mimeType = "application/pdf"; response.setContentType(mimeType); response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", "voucherLiquidacion.pdf")); response.setContentLength(pdfReport.length); ByteArrayInputStream inStream = new ByteArrayInputStream( pdfReport); FileCopyUtils.copy(inStream, response.getOutputStream()); }
De este modo, se puede incorporar la funcionalidad requerida y generar y descargar un PDF con java
Por Martin Larizzate, Java & Salesforce Developer
Fuentes: