Thymeleaf y Flying Saucer para generar PDF

Cómo generar fácilmente un informe pdf

Para generar de formar rápida un informe PDF os presentamos la librería Flying Saucer que se integra fácilmente con Thymeleaf, que ya fue presentado en el post Thymeleaf como alternativa MVC de nuestro compañero Sebas.

De hecho, hemos realizado un fork de su proyecto para integrar su ejemplo con Flying Saucer PDF y añadir un pequeño informe.

5 Pasos sencillos para construir nuestro informe

Estos son los pasos que hemos seguido para conseguirlo:

  • Añadir las dependencias necesarias en el pom.xml

<!-- Generacion pdf: flying saucer pdf -->
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.1.4</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>

  •  Creación de un servicio PdfGenerator para que genere contenido PDF a partir de una plantilla HTML:

Este servicio utiliza el motor de plantillas de Thymeleaf  para obtener de forma dinámica un HTML (que revisaremos más adelante) utilizando los parámetros recibidos.

Posteriormente se crea un objeto ITextRenderer a partir del HTML generado, que se encarga de generar el ByteArrayOutputStream de salida que se utilizará para generar nuestro PDF.


@Service
public class PdfGenarator {

	private static final Logger logger = LoggerFactory.getLogger(PdfGenarator.class);

	@Autowired
	private TemplateEngine templateEngine;

	@Autowired
	private ApplicationContext context;

	@Autowired
	ServletContext servletContext;

	String urlBase = "http://localhost:8080";

	public ByteArrayOutputStream createPdf(final String templateName, final Map map, final HttpServletRequest request, final HttpServletResponse response)
			throws DocumentException {

		logger.debug("Generando informe pdf");

		Assert.notNull(templateName, "The templateName can not be null");

		IWebContext ctx = new SpringWebContext(request, response, servletContext, LocaleContextHolder.getLocale(), map, context);

		String processedHtml = templateEngine.process(templateName, ctx);

		ByteArrayOutputStream bos = new ByteArrayOutputStream();

		try {

			ITextRenderer renderer = new ITextRenderer();
			renderer.setDocumentFromString(processedHtml, urlBase);

			renderer.layout();
			renderer.createPDF(bos, false);
			renderer.finishPDF();
			logger.info("PDF created correctamente");

		} finally {
			if (bos != null) {
				try {
					bos.close();
				} catch (IOException e) {
					logger.error("Error creando pdf", e);
				}
			}
		}
		return bos;
	}
}

  • Añadir al controlador RaffleController un nuevo método para obtención del informe pdf

A continuación añadiremos la invocación al servicio que acabamos de implementar desde el controlador RaffleController, que es el encargado de obtener la lista de ganadores.

El objeto ByteArrayOutputStream obtenido se utiliza para generar la salida mediante ResponseEntity. Podéis observar que se establece la cabecera Content-Disposition como fichero adjunto, el Content-Type como application/pdf, el Content-Length con el tamaño en bytes del fichero y, por último, el body con los bytes del PDF.


	@GetMapping("/raffle/pdf")
	public ResponseEntity rafflePDF(@ModelAttribute final Raffle raffle, final HttpServletRequest request,
			final HttpServletResponse response) throws DocumentException {

		List winners = raffle.getWinners();

		Map<String, Object> mapParameter = new HashMap<String, Object>();
		mapParameter.put("name", "Softtekiano");
		mapParameter.put("winners", winners);

		ByteArrayOutputStream byteArrayOutputStreamPDF = pdfGenarator.createPdf(templateName, mapParameter, request, response);
		ByteArrayResource inputStreamResourcePDF = new ByteArrayResource(byteArrayOutputStreamPDF.toByteArray());

		return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName).contentType(MediaType.APPLICATION_PDF)
				.contentLength(inputStreamResourcePDF.contentLength()).body(inputStreamResourcePDF);

	}

  • Creación de la template HTML, CSS e imágenes necesarias para el informe PDF.

Podéis encontrar la fuente en esta ruta: src\main\resources\templates\templatePDF.html

La variable ${name} se debe establecer de forma dinámica al igual que la lista ${winers} de acuerdo al servicio de negocio previamente que se ejecutó para obtener la lista de ganadores.

PlantillaPDF

  • Ajustar estilos y subir imagen a incrustar en nuestro proyecto

A modo de ejemplo se muestra cómo incluir unos estilos e imágenes en el pdf que se generará. Estos recursos se encuentran en:

src\main\resources\public\css\styles.css

src/main/resources/public/images/softtek.png

Probando la generación de nuestro informe

  • Ejecutar main de aplicación Spring Boot: ThymeleafdemoApplication
  • Acceder a la siguiente URL: http://localhost:8080/raffle e indicar participantes con sus puntos

Raffle Thymeleaf Demo PDF

  • Hacer click en botón "Sortear"

Resultado Raffle Thymeleaf Demo

  • Hacer click en el botón "Dowload PDF" y la magia empezará...

reaffle-informe.pdf

 

Aquí podéis ver cómo queda en informe PDF

Conclusión

Nos encontramos otro modo de generar informes con Flying Saucer integrado con Thymeleaf.

En comparación con otros frameworks de generación de informes lo que nos encontramos es un modo muy visual y más sencillo desde el punto de vista del Front Developer, ya que la plantilla se define con XHTML.

La maquetación de dicho XHTML se puede realizar con CSS 2.1.

Todo el código fuente del ejemplo que os hemos mostrado los podéis encontrar aquí:

https://github.com/ejalarcon/thymeleafdemo