Shared utility library for the Firefly Framework — FreeMarker-powered template rendering to XHTML, PDF, and images, plus filtering annotations used across framework modules.
- Overview
- Features
- Requirements
- Installation
- Quick Start
- Configuration
- Documentation
- Contributing
- License
fireflyframework-utils is a lightweight, dependency-light utility library used across the Firefly Framework ecosystem. It bundles two independent capabilities that show up repeatedly in microservices but do not warrant a module of their own:
- Document rendering —
TemplateRenderUtilturns FreeMarker templates into well-formed XHTML and, via the Flying Saucer renderer backed by OpenPDF, into PDF documents and raster images (PNG/JPEG). It is the engine behind document-heavy use cases such as invoices, statements, contracts, and reports — with watermarks, encryption, metadata, bookmarks/outlines, custom fonts, template caching, shared variables, pre/post-processing hooks, and asynchronous rendering. - Filtering metadata — the
@FilterableIdannotation marks ID fields on filter DTOs so they participate in dynamic query filtering and OpenAPI parameter generation with exact-match semantics.
The module sits low in the framework's dependency graph. It depends only on fireflyframework-kernel (shared abstractions and exceptions) and brings FreeMarker and Flying Saucer/OpenPDF as its functional dependencies, so it can be added to any service — reactive or servlet, core/domain/experience tier — without pulling in the full framework stack. The @FilterableId annotation it defines is consumed at runtime by the filtering utilities in fireflyframework-web (FilterUtils for query building and FilterParameterCustomizer for OpenAPI/Swagger documentation).
TemplateRenderUtil is a static-method utility — there is no Spring auto-configuration and no bean to wire. Call it directly from any service, mapper, or scheduled job.
Template rendering (TemplateRenderUtil)
- Render FreeMarker templates from the classpath (
classpath:/templates), the filesystem (./templates), or arbitrary customTemplateLoaders — resolved via aMultiTemplateLoaderin that order. - Render either named template files (
*.ftl) or in-memory template strings. - Output to XHTML string, PDF (
OutputStream,File, orbyte[]), or image (byte[], e.g. PNG/JPEG at a chosen width/height). - Automatic XHTML normalization — wraps fragments in a valid XHTML document and injects
@pageCSS for size/margins so Flying Saucer always receives well-formed input. - Rich
PdfOptionsbuilder: page size (A4,LETTER,LEGAL,A3), margins, custom font directories (TTF/OTF embedding), default font, base URI for relative resources. - Watermarks (text, opacity, font size, rotation, color), encryption (user/owner passwords + printing/copy/modify permissions), document metadata (title, author, subject, keywords, creator), and bookmarks/outlines (nested, page-targeted).
- Asynchronous variants (
*Async) returningCompletableFuture, backed by a configurable fixed thread pool. - Template caching (toggleable, with configurable max size), shared variables available to every template, and pre/post-processing hooks (
BiFunction<String, Map, String>). - Template validation for both inline content and files, returning a list of syntax errors instead of throwing.
Filtering metadata
@FilterableId— a runtime-retained field annotation that opts anIdfield into filtering with exact-match-only semantics (noLIKE, no range parameters), and surfaces it as a query parameter in generated OpenAPI documentation.
- Java 21+ (Java 25 recommended)
- Spring Boot 3.x
- Maven 3.9+
- No external runtime services. For TTF/OTF font embedding you provide font files; for classpath template loading place templates under
src/main/resources/templates.
Add the dependency. The version is managed by the Firefly BOM / parent POM, so you normally omit <version>:
<dependency>
<groupId>org.fireflyframework</groupId>
<artifactId>fireflyframework-utils</artifactId>
<!-- version managed by the Firefly BOM / fireflyframework-parent -->
</dependency>If your project does not inherit the Firefly parent or import the BOM, pin the version explicitly to match the rest of your framework dependencies (for example 26.05.08).
Place invoice.ftl under src/main/resources/templates/:
<!-- src/main/resources/templates/invoice.ftl -->
<html><body>
<h1>Invoice for ${customerName}</h1>
<p>Total: ${total}</p>
</body></html>import org.fireflyframework.utils.template.TemplateRenderUtil;
import java.util.Map;
Map<String, Object> model = Map.of("customerName", "Acme Corp", "total", "1,250.00 EUR");
String html = TemplateRenderUtil.renderTemplateToHtml("invoice.ftl", model);byte[] pdf = TemplateRenderUtil.renderTemplateToPdfBytes("invoice.ftl", model);
// e.g. return as a ResponseEntity<byte[]> with Content-Type: application/pdfimport org.fireflyframework.utils.template.TemplateRenderUtil.PdfOptions;
import org.fireflyframework.utils.template.TemplateRenderUtil.PdfOptions.PageSize;
PdfOptions options = new PdfOptions()
.withPageSize(PageSize.A4)
.withMargins(36, 36, 36, 36)
.withDefaultFont("DejaVu Sans")
.withFontDirectory("/opt/fonts") // embeds .ttf/.otf found here
.withWatermark("CONFIDENTIAL", 0.3f, 60, 45, "#888888")
.withMetadata("Q1 Invoice", "Billing", "Invoice", "invoice,billing")
.withEncryption("ownerSecret") // owner password, printing/copy allowed
.withBookmark("Summary", "1")
.withChildBookmark("Line Items", "1");
byte[] pdf = TemplateRenderUtil.renderTemplateToPdfBytes("invoice.ftl", model, options);import java.util.concurrent.CompletableFuture;
CompletableFuture<byte[]> future =
TemplateRenderUtil.renderTemplateToPdfBytesAsync("invoice.ftl", model);byte[] png = TemplateRenderUtil.renderHtmlToImage(
"<h1 style='color:#0a6'>Hello</h1>", 800, 400, "png");import org.fireflyframework.utils.annotations.FilterableId;
import java.util.UUID;
public class UserFilterDTO {
@FilterableId
private UUID customerId; // included in filters with exact matching, exposed as a query param
private UUID accountId; // excluded from filters (default behaviour for *Id fields)
private String name; // regular field with full filtering capabilities
}This module has no Spring @ConfigurationProperties and no auto-configuration — there are no firefly.* properties to set in application.yml. TemplateRenderUtil is configured programmatically via its static methods, which apply globally to the utility:
| Method | Purpose | Default |
|---|---|---|
setTemplateCachingEnabled(boolean) |
Enable/disable in-memory template caching | enabled |
setTemplateCacheMaxSize(int) |
Max number of cached compiled templates | 100 |
setAsyncThreadPoolSize(int) |
Size of the async rendering thread pool | max(2, availableProcessors()) |
addSharedVariable(name, value) |
Expose a variable to every template | none |
setTemplatePreProcessor(BiFunction) |
Transform template source before rendering | none |
setTemplatePostProcessor(BiFunction) |
Transform rendered output after rendering | none |
addTemplateLoader(TemplateLoader) |
Register an additional FreeMarker loader | classpath + ./templates |
setTemplateDirectory(String) / setClasspathAndFileTemplateLoaders(...) |
Override where templates are loaded from | classpath:/templates, then ./templates |
Template lookup order out of the box: classpath:/templates, then the working-directory ./templates folder, then any custom loaders. FreeMarker defaults: UTF-8 encoding, Locale.US, and a rethrow exception handler.
Call TemplateRenderUtil.shutdownAsyncThreadPool() on application shutdown if you used the async APIs.
- In-repo tutorials under
tutorials/— a four-part series coveringTemplateRenderUtilbasics, FreeMarker syntax, PDF customization, and advanced features (caching, shared variables, hooks, async, validation, image rendering). - Framework documentation hub and module catalog: github.com/fireflyframework
- FreeMarker manual and Flying Saucer for template syntax and rendering specifics.
Contributions are welcome. Please read the CONTRIBUTING.md guide for details on our code of conduct, development process, and how to submit pull requests.
Copyright 2024-2026 Firefly Software Foundation.
Licensed under the Apache License, Version 2.0. See LICENSE for details.