spring-gotenberg-reports
spring-gotenberg-reports shows how to produce PDF reports in Spring Boot without JasperReports. Each report is a plain Thymeleaf HTML template with a print stylesheet. Gotenberg, a headless Chromium behind an HTTP API, prints the HTML to PDF. Long reports are split into sections, converted in parallel, merged into one file, and the browser follows progress over Server-Sent Events.
Source: github.com/Aquilabot/spring-gotenberg-reports (MIT).
What kind of project this is
Section titled “What kind of project this is”It is a runnable Spring Boot application, not a library. The pom.xml builds an executable jar (spring-boot-maven-plugin, version 0.1.0-SNAPSHOT) and the README gives no artifact coordinates to add as a dependency. You clone the repository, run it, and copy or adapt the parts you need: the Gotenberg client, the renderer, the job service and the template conventions.
The author wrote it after leading a migration from JasperReports to HTML templates rendered by Gotenberg; the repository shows that approach on a sample domain. All data is synthetic: fictional banks, invented names and .example e-mail domains.
The problem it addresses
Section titled “The problem it addresses”JasperReports layouts live in .jrxml files produced by a designer tool, with absolute coordinates, font extensions packaged as jars, and text that overflows fixed-size boxes. Reviewing a layout change in a pull request is hard. With HTML templates the report is ordinary source code: it renders in a browser while you work on it, tests can assert on it, and the PDF engine runs as a separate process that can crash, restart or scale on its own.
How a single PDF is produced
Section titled “How a single PDF is produced”-
The browser calls
GET /api/reports/{type}/{id}.ReportControllerlooks up the sample record (anInvoice,StatementorPortfolioSummary, each implementingReport). -
ReportRendererprocesses the Thymeleaf template named byReport.template(), pluspage-header.htmlandpage-footer.html, and bundles them with every.css,.woff2,.svgand.pngfile fromreport-assets/into anHtmlDocument. -
GotenbergClientposts that document as a multipart form to Gotenberg’s/forms/chromium/convert/htmlroute withpreferCssPageSize=trueandprintBackground=true. Transport errors, 429 and 5xx answers are retried with exponential backoff. -
The PDF body is streamed from Gotenberg to the client without being buffered in memory.
Combined reports follow the same path for each section on its own virtual thread, bounded by a Semaphore, then call Gotenberg’s /forms/pdfengines/merge route. See Combined reports as background jobs.
Main pieces
Section titled “Main pieces”| Package | Classes | Role |
|---|---|---|
dev.aquilabot.reports.gotenberg |
GotenbergClient, PdfEngine, HtmlDocument, PdfStream, GotenbergException, GotenbergProperties |
HTTP client for the two Gotenberg routes, with retries and Micrometer timers |
dev.aquilabot.reports.report |
Report, ReportRenderer, Formats, LineChart, BarChart |
Template rendering and chart geometry |
dev.aquilabot.reports.job |
CombinedReportService, ReportJob, JobEvent, JobProperties, ResultStore |
Parallel section rendering, merge, cancellation, temporary storage |
dev.aquilabot.reports.web |
ReportController, CombinedReportController, ErrorHandling, SampleReports |
HTTP endpoints and problem-detail errors |
dev.aquilabot.reports.sample |
SampleData, Invoice, Statement, PortfolioSummary |
Deterministic fake data for the three sample reports |
What it does not do
Section titled “What it does not do”- No authentication. The actuator health endpoint shows details.
- Finished combined PDFs and job state live on local disk and in memory, so several app instances need sticky sessions or shared storage.
- Page numbers in a combined report restart in each section.
- Output is plain Chromium PDF, not PDF/A.
- There is no data access layer. Reports come from
SampleData, which generates the same document for the same id.
The full list is on the compatibility and limitations page.
Next steps
Section titled “Next steps”Written from the code and tests of each repository. If a page and the code disagree, the code is right and the page has a bug. Open an issue.
© 2026 Jean Carlo Benites Porras. Code samples are MIT, like the projects they come from.