HTML to PDF
in Java.
The Java options are an old XHTML renderer, a library with a licence that makes legal nervous, or driving a browser yourself. A plain HttpClient call avoids all three.
The call
Post your markup with an API key. The PDF comes back in the same response, base64 encoded by default. Nothing is written to disk on our side.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
public class GeneratePdf {
public static void main(String[] args) throws Exception {
String payload = """
{"html": "<h1>Invoice INV-1042</h1><p>Total: $1,645.40</p>",
"responseFormat": "binary",
"options": {"format": "A4", "printBackground": true}}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.html4pdf.com/generate-pdf"))
.header("Authorization", "Bearer " + System.getenv("HTML4PDF_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
HttpResponse<Path> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofFile(Path.of("invoice.pdf")));
System.out.println("status " + response.statusCode() + ", wrote " + response.body());
}
}In Spring Boot
Return the bytes as a ResponseEntity and the browser downloads the file. Nothing is written to disk on your side.
@GetMapping(value = "/invoices/{id}.pdf", produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<byte[]> invoice(@PathVariable String id) throws Exception {
String html = templateEngine.process("invoice", contextFor(id));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.html4pdf.com/generate-pdf"))
.header("Authorization", "Bearer " + apiKey)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(
mapper.writeValueAsString(Map.of("html", html, "responseFormat", "binary"))))
.build();
byte[] pdf = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofByteArray())
.body();
return ResponseEntity.ok()
.header("Content-Disposition", "attachment; filename=\"" + id + ".pdf\"")
.body(pdf);
}What comes back
The default response is JSON with the document base64 encoded, the credits you have left, and how long the render took. Send responseFormat as binary instead and the body is the PDF itself, with a Content-Type of application/pdf.
{
"success": true,
"pdf": "JVBERi0xLjQKJeLjz9MKMyAwIG9...",
"creditsRemaining": 9,
"metadata": { "size": 42813, "processingTime": 2841 }
}A 402 means you are out of credits, a 401 means the key is wrong, and a 422 means the markup could not be rendered. Everything else is worth retrying once.
The usual Java libraries
Worth knowing before you add a dependency, because for a lot of documents one of these is the right answer and costs nothing.
| Library | What it is | The trade-off |
|---|---|---|
| Flying Saucer and openhtmltopdf | Render well-formed XHTML with CSS 2.1 to PDF, in pure Java. | No external dependencies and good typography. Your HTML must be valid XHTML, and modern CSS and JavaScript are out. |
| iText | The most complete PDF toolkit on the JVM. | Powerful, and dual licensed: AGPL, or a commercial licence if your product is not open source. Worth checking with legal before it reaches production. |
| Apache PDFBox | Reads and writes PDFs at a low level. | Great for manipulating existing files. It is not an HTML renderer. |
| Playwright or Selenium | Drive a real browser from Java and print to PDF. | Accurate, but now you operate browsers next to your JVM. |
Java: common questions
What is the best HTML to PDF library for Java?
openhtmltopdf if your markup is valid XHTML with simple CSS and you want zero services. iText if you need deep PDF features and can accept the licence. If the page is a modern web page, render it in a browser rather than reimplementing one.
Do I need an external dependency for the API call?
No. java.net.http.HttpClient has been in the JDK since Java 11. Jackson only appears in the Spring example for building JSON.
Is iText free to use?
It is AGPL, which means publishing your source or buying a commercial licence. That is the usual reason teams look elsewhere.
Can I stream the PDF directly to a file?
Yes. BodyHandlers.ofFile writes the response body to disk as it arrives, which is what the first example does.