Rendral

Stack · Java

Generate PDFs in Java

Java's usual options both bite. Flying Saucer renders XHTML with roughly CSS 2.1 support, so modern layout silently falls apart. iText is capable but AGPL — using it in a closed-source product means buying a commercial licence.

If Thymeleaf already renders the document for the browser, render it to a string and POST it. java.net.http.HttpClient has been in the JDK since 11, so this adds no dependency at all.

01Call it from Java

render.ts
// PdfService.java
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Map;

@Service
public class PdfService {

    private final HttpClient client = HttpClient.newHttpClient();
    private final ObjectMapper mapper = new ObjectMapper();

    public byte[] render(String html) throws Exception {
        String body = mapper.writeValueAsString(Map.of(
            "html", html,
            "options", Map.of("format", "A4", "pageNumbers", true)
        ));

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://rendral.com/api/v1/pdf"))
            .header("Authorization", "Bearer " + System.getenv("RENDRAL_API_KEY"))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();

        HttpResponse<byte[]> response =
            client.send(request, HttpResponse.BodyHandlers.ofByteArray());

        if (response.statusCode() >= 300) {
            throw new IllegalStateException("Render failed: " + response.statusCode());
        }
        return response.body();
    }
}

Frequently asked

How do I render a Thymeleaf template to a string?

Call templateEngine.process("invoice", context) with a Context holding your model. That returns the same HTML the browser would receive.

Does this avoid the iText licence question?

Yes. iText is AGPL, which generally requires a commercial licence for closed-source products. This is an API call with no source-licensing implications.

What's wrong with Flying Saucer?

Nothing, within its scope — but it targets XHTML with roughly CSS 2.1. Flexbox, grid and modern web fonts aren't supported, so contemporary templates break.

Do I need an HTTP library?

No. java.net.http.HttpClient ships with the JDK from 11 onward, so there's no new dependency to justify in review.

Related

Start rendering in minutes.

A free tier, five endpoints, and SDKs for Node and Python.