Recentemente eu percebi que nunca havia criado uma aplicação que gerasse pdf personalizados, então decidi criar um projeto para isso. 😁
O primeiro passo foi criar um projeto Spring Boot, e adicinar algumas dependências bem básicas.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
Pesquisando um pouco a primeira biblioteca que encontrei foi o iText, que é uma biblioteca Java para criar e manipular arquivos PDF.
Eu queria pode criar um arquivo em HTML e transformar ele em PDF, com isso eu utilizei o html2pdf um add-on do iText que permite renderizar HTML/CSS em PDF.
Adicionando a dependência no POM.xml
<!-- https://central.sonatype.com/artifact/com.itextpdf/html2pdf?smo=true -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>6.1.0</version>
</dependency>
Com isso eu precisava que os valores do pdf fossem dinâmicos, então eu adicionei o Thymeleaf para processar os templates HTML.
O Thymeleaf é um motor de templates para Java para gerar páginas HTML dinâmicas server side, ele permite integrar dados do backend diretamente no HTML usando atributos personalizados, como th:text
, th:each
e th:if
.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Caso tiver algum problema em adicionar a dependências pom.xml
Com as dependências adicionadas, criei um serviço para gerar PDFs a partir de um arquivo HTML.
pdf-generator
├── services
│ └── PdfService.java
Na classes eu utilizei o TemplateEngine para processar o template HTML e gerar o PDF com o HtmlConverter do html2pdf.
Para fins de exemplo eu estou utilizando dados hard code, mas você pode substituir por dados dinâmicos do seu banco de dados ou de uma API.
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import java.io.ByteArrayOutputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
@Service
public class PdfService {
private final TemplateEngine templateEngine;
public PdfService(TemplateEngine templateEngine) {
this.templateEngine = templateEngine;
}
public byte[] generatePdf(String templateName) {
Context context = new Context();
context.setVariable("dataHora", LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")));
List<Map<String, String>> vendasMensais = List.of(
Map.of("category", "Eletrônicos", "total", "R$ 90.000,00", "qtd", "90", "comission", "R$ 1.500,00"),
Map.of("category", "Móveis", "total", "R$ 79.200,00", "qtd", "132", "comission", "R$ 1.320,00"),
Map.of("category", "Vestuário", "total", "R$ 54.000,00", "qtd", "108", "comission", "R$ 900,00"),
Map.of("category", "Acessórios", "total", "R$ 48.000,00", "qtd", "192", "comission", "R$ 800,00")
);
context.setVariable("itens", vendasMensais);
// Processando o HTML passando os dados do contexto
String html = templateEngine.process(templateName, context);
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
ConverterProperties properties = new ConverterProperties();
// Setando o diretório base para buscar os recursos (imagens, css, etc)
properties.setBaseUri("src/main/resources/static/");
HtmlConverter.convertToPdf(html, outputStream, properties);
return outputStream.toByteArray();
} catch (Exception e) {
throw new RuntimeException("Erro ao gerar PDF", e);
}
}
}
Em seguida eu criei o controller que será aonde será feita as requisições, e a onde será utilizado o serviço criado para gerar o PDF.
pdf-generator
├── service
│ └── PdfService.java
├── controller
│ └── PdfController.java
Eu configurei o nome do relatório que será processado pelo Thymeleaf e convertido em PDF, e defini o nome do arquivo que será baixado.
import com.pdf_generator.services.PdfService;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/pdf")
@RestController
public class PdfController {
private final PdfService pdfService;
public PdfController(PdfService pdfService) {
this.pdfService = pdfService;
}
@GetMapping("/download")
public ResponseEntity<byte[]> downloadPdf() {
// Deve ser passado o mesmo nome do arquivo HTML que será processado
byte[] pdfBytes = pdfService.generatePdf("report");
HttpHeaders headers = new HttpHeaders();
// Define o nome do arquivo que será baixado
headers.add("Content-Disposition", "attachment; filename=Relatorio.pdf");
return new ResponseEntity<>(pdfBytes, headers, HttpStatus.OK);
}
}
Agora vamos criar o template HTML que será processado pelo Thymeleaf e convertido em PDF, para conhecer mais sobre o Thymeleaf, acesse a documentação oficial.
pdf-generator
├── service
│ └── PdfService.java
├── controller
│ └── PdfController.java
├── resources
│ └── templates
│ └── relatorio.html
Devemos adicionar a tag xmlns:th="http://www.thymeleaf.org"
no HTML para usar o Thymeleaf, e adicionar os atributos th:text
e th:each
para adicionar os valores dinâmicos no HTML.
<!DOCTYPE html>
<html lang="pt-BR">
<!-- Essa tag é necessária para usar o thymeleaf -->
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Relatório de Vendas</title>
</head>
<body>
<div>
<div>
<h2>Vendas por Categoria</h2>
<table>
<thead>
<tr>
<th>Categoria</th>
<th>Quantidade</th>
<th>Total Vendido</th>
<th>Comissão</th>
</tr>
</thead>
<tbody>
<tr th:each="item : ${itens}">
<td th:text="${item.category}"></td>
<td th:text="${item.qtd}"></td>
<td th:text="${item.total}"></td>
<td th:text="${item.comission}"></td>
</tr>
</tbody>
</table>
</div>
<footer>
<p>Relatório gerado em <span th:text="${dataHora}"></span></p>
</footer>
</div>
</body>
</html>
Você também pode adicionar imagens e CSS no HTML, basta adicionar o caminho relativo no atributo src
da tag img
e no atributo href
da tag link
.
Se tiver dúvidas sobre como adicionar imagens e CSS no HTML, você poder da uma olhada nesse commit.
Para executar a aplicação, basta rodar o comando mvn spring-boot:run no terminal ou utilizar uma IDE como IntelliJ ou Eclipse.
Após a aplicação subir, acesse o endpoint http://localhost:8080/pdf/download
e o PDF será baixado automaticamente.
No final o PDF gerado deve ficar parecido com isso: Relatório de Vendas Simples
O código completo você pode encontrar no repositório do projeto, e se tiver alguma dúvida ou sugestão você pode entrar em contato comigo pelo linkedin. 😊