How to Convert HTML to PDF in Java

21 June 2026
This post thumbnail

The fastest path to convert HTML to PDF in Java in 2026 is Open HTML to PDF for server-rendered, CSS-styled templates, iText 8 with pdfHTML when you need print-grade fidelity and can pay for a license, and a hosted Chrome engine when your HTML depends on JavaScript or you need to scale past your JVM's heap. Pick one path, paste the code below, and ship.

Key Takeaways

  • Open HTML to PDF 1.1+ is the modern default Java library: pure Java, LGPL/Apache-licensed, built on Apache PDFBox 3, and the right answer for invoices, statements, and reports rendered from Thymeleaf or JSP.
  • iText 8 + pdfHTML has the strongest CSS Paged Media support in any pure-Java library, but it's AGPL: closed-source and SaaS projects need a commercial license, often starting in the $5,000-per-year range.
  • No pure-Java HTML-to-PDF library executes JavaScript. If your HTML renders charts or React components client-side, a print-CSS engine will silently emit a broken document.
  • The canonical Spring Boot pattern is Thymeleaf → HTML string → Open HTML to PDF → ResponseEntity<byte[]> with application/pdf. Async it onto a bounded executor before traffic gets serious.
  • When fonts, JS, or end-of-month batch volume start eating your week, call a hosted Chrome engine like Transformy instead of running Chromium yourself.

Most Java tutorials for HTML to PDF pick the wrong library because they answer the wrong question. They ask "which library has the most stars?" instead of "which library can render this HTML reliably at my volume?" The two answers are rarely the same. This guide walks through the four paths a Java team can take in 2026, the runnable code for each one, the gotchas you'll hit in week three of production, and a clear marker for when local rendering stops being worth the operations cost. By the end you'll have a controller-ready Spring Boot endpoint, a decision tree for picking the engine that fits your page, and an honest read on when to stop owning the renderer.

Why HTML to PDF in Java is three problems pretending to be one

Converting HTML to PDF in Java looks like a one-liner until you ship it. Then it becomes three:

  1. Picking a library that parses your HTML correctly. Most Java HTML-to-PDF libraries parse XHTML, not loose HTML. The <br> you forgot to close becomes a parse error in production.
  2. Getting the CSS to render the way it looks in your browser. Print-CSS engines cover CSS 2.1 well, CSS 3 partially, and bleeding-edge features not at all. The layout your designer built in Chrome may not survive the round trip.
  3. Scaling it without your JVM melting. PDF rendering is CPU-bound and holds the whole document in memory. One marketing email blasting 10,000 customers to "download your report" can starve every other endpoint in your app.

Most tutorials cover problem one in depth, gesture at problem two, and skip problem three entirely. That's why a scrubbed-from-Stack-Overflow setup works fine in dev and fails on the first batch run. The rest of this guide treats all three as real.

The four paths to convert HTML to PDF in Java

In 2026, a Java team has four serious options. Match the option to the page, not your habits.

Path License Engine type JavaScript CSS coverage Best for
Open HTML to PDF LGPL 2.1 + Apache 2.0 Print-CSS, PDFBox 3 No CSS 2.1 + selected CSS 3 Most server-rendered, CSS-styled documents
iText 8 + pdfHTML AGPL or commercial Print-CSS No CSS 2.1 + most of CSS 3 + Paged Media Print-grade output, PDF/A, accessibility tags, if you can pay
Flying Saucer + OpenPDF LGPL Print-CSS (iText 2.x fork) No CSS 2.1 (older) Legacy projects already on this stack
Hosted Chrome engine (Transformy and similar) SaaS Full Chromium Yes Everything Chrome ships JS-rendered pages, webfonts, scale, dashboards

The decision reduces to three questions. Does your HTML need JavaScript to look right? Can you absorb iText's AGPL or pay for a commercial license? Do you want to operate a browser at scale? Answer honestly and the path picks itself.

Tip: Want to skip the library evaluation entirely and see if a hosted engine fits? Render your first PDF on the Transformy free tier. Point it at the same HTML you'd send Open HTML to PDF, get a URL back with your PDF file

Open HTML to PDF: the modern default

Open HTML to PDF is the actively maintained fork of Flying Saucer, rebuilt on Apache PDFBox 3. It's the right starting point for the boring 80% of Java HTML-to-PDF work: invoices from Thymeleaf, statements from JSP, reports from any server-side template engine that produces HTML. LGPL on the renderer, Apache 2.0 on the PDFBox layer underneath, no commercial gotchas.

Maven dependency

<dependency>
    <groupId>com.openhtmltopdf</groupId>
    <artifactId>openhtmltopdf-pdfbox</artifactId>
    <version>1.1.34</version>
</dependency>

Open HTML to PDF 1.1+ requires Java 11 or newer. If you're still on Java 8, pin to the 1.0.x line until you can upgrade the toolchain, since PDFBox 3 fixes only land on 1.1.

Minimal converter (HTML string to PDF file)

import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.FileOutputStream;
import java.io.OutputStream;

public class HtmlToPdf {
    public static void main(String[] args) throws Exception {
        String html = """
            <!DOCTYPE html>
            <html><head><meta charset="UTF-8">
            <style>
              body { font-family: sans-serif; padding: 24px; }
              h1 { color: #1f6feb; }
              table { width: 100%; border-collapse: collapse; }
              td, th { border: 1px solid #ddd; padding: 8px; }
              tr { break-inside: avoid; page-break-inside: avoid; }
            </style></head>
            <body>
              <h1>Invoice #1041</h1>
              <table>
                <tr><th>Item</th><th>Total</th></tr>
                <tr><td>Hosting (Jun)</td><td>$49.00</td></tr>
              </table>
            </body></html>
            """;

        // Normalize loose HTML to well-formed XHTML before handing it to the renderer.
        Document jsoupDoc = Jsoup.parse(html);
        jsoupDoc.outputSettings().syntax(Document.OutputSettings.Syntax.xml);
        String xhtml = jsoupDoc.html();

        try (OutputStream os = new FileOutputStream("invoice.pdf")) {
            PdfRendererBuilder builder = new PdfRendererBuilder();
            builder.useFastMode();
            builder.withHtmlContent(xhtml, "/");
            builder.toStream(os);
            builder.run();
        }
    }
}

Paste it into a main(), run it, and you get an invoice.pdf next to your compiled classes. Two pieces are worth defending.

The JSoup pre-pass exists because Open HTML to PDF expects XHTML. The first time a template emits an unclosed <meta> or <br>, the renderer throws a parse error you'll spend an hour chasing. JSoup with syntax(xml) normalizes the markup once and ends an entire category of bugs.

useFastMode() switches to the modern renderer. The legacy renderer exists for backward compatibility with old Flying Saucer templates and is meaningfully slower. Turn fast mode on unless you have a specific reason not to.

Gotcha: page breaks that split your tables

The most common Open HTML to PDF question is "why is my table cut in half at the page break?" The answer is one CSS line on the rows you don't want broken:

tr { break-inside: avoid; page-break-inside: avoid; }
.invoice-totals { break-inside: avoid; page-break-inside: avoid; }

Use both break-inside and the older page-break-inside. Open HTML to PDF respects them; so does iText pdfHTML. The full property surface lives in the W3C CSS Paged Media spec, which is worth a skim if you write print-styled HTML often.

Gotcha: webfonts that won't load

Open HTML to PDF will not pull a webfont from a CDN at render time. The JVM is not a browser. If your template depends on a font, register it on the builder and ship the file with your app:

builder.useFont(new File("src/main/resources/fonts/Inter-Regular.ttf"), "Inter");

Self-host the font, register it explicitly by family name, and the PDF stops mysteriously rendering in Times New Roman. The Apache PDFBox docs at pdfbox.apache.org cover what font formats and embedding modes are supported.

Devon's invoice job and the day it stopped scaling

Devon, an engineer on a 12-person fintech I worked with, picked Flying Saucer in 2023 for monthly account statements. A few hundred PDFs at month-end, no problems for two years. By April 2026 the customer base had tripled, the batch generated 18,000 statements in a four-hour window, and his JVM started swapping to disk around statement 9,000. The fix wasn't switching libraries; it was migrating to Open HTML to PDF on PDFBox 3 and moving the renders off the request thread onto a dedicated worker pool capped at four concurrent jobs. Peak heap dropped by 60% and the 2 a.m. pages stopped. The lesson wasn't "Flying Saucer is bad." It was that the smallest tool that worked in 2023 wasn't the smallest tool that worked in 2026.

iText 8 with pdfHTML: print-grade fidelity, license required

iText 8 with the pdfHTML add-on is the strongest pure-Java HTML-to-PDF path. It supports more of CSS Paged Media than Open HTML to PDF, including running headers, page counters, PDF/A archival output, and accessible (tagged) PDFs. It's also AGPL, which means closed-source applications, SaaS products, and anything you ship to customers without source need a commercial license. iText doesn't publish a price sheet, but real-world quotes generally start in the $5,000-per-year range and scale with deployment footprint.

That's a business decision, not a knock on the library. iText is the best at what it does. Price it in before you commit, or pick a different path.

Maven dependency

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext-core</artifactId>
    <version>9.0.0</version>
    <type>pom</type>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>pdfhtml</artifactId>
    <version>6.0.0</version>
</dependency>

Minimal converter

import com.itextpdf.html2pdf.HtmlConverter;

import java.io.FileOutputStream;

public class ItextHtmlToPdf {
    public static void main(String[] args) throws Exception {
        String html = "<h1>Invoice #1041</h1><p>Total: $49.00</p>";
        try (FileOutputStream out = new FileOutputStream("invoice.pdf")) {
            HtmlConverter.convertToPdf(html, out);
        }
    }
}

One method call, one PDF. Reach for iText when you need PDF/A for regulatory archival, accessibility tags, or print-shop fidelity. Reach for Open HTML to PDF when none of those apply.

HTML to PDF in Spring Boot with Thymeleaf templates

The most common production pattern is a Spring Boot controller that takes a request, renders a Thymeleaf template to an HTML string, hands it to Open HTML to PDF, and returns the bytes with Content-Type: application/pdf. Here's the whole thing.

import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import java.io.ByteArrayOutputStream;
import java.util.Map;

@Controller
public class InvoiceController {

    private final TemplateEngine templateEngine;

    public InvoiceController(TemplateEngine templateEngine) {
        this.templateEngine = templateEngine;
    }

    @GetMapping("/invoice/{id}.pdf")
    public ResponseEntity<byte[]> invoice(@PathVariable String id) throws Exception {
        Context ctx = new Context();
        ctx.setVariables(Map.of(
            "invoiceId", id,
            "total", "$49.00"
        ));
        String html = templateEngine.process("invoice", ctx);

        Document jsoupDoc = Jsoup.parse(html);
        jsoupDoc.outputSettings().syntax(Document.OutputSettings.Syntax.xml);
        String xhtml = jsoupDoc.html();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        PdfRendererBuilder builder = new PdfRendererBuilder();
        builder.useFastMode();
        builder.withHtmlContent(xhtml, "/");
        builder.toStream(baos);
        builder.run();

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_PDF);
        headers.setContentDispositionFormData("attachment", "invoice-" + id + ".pdf");
        return new ResponseEntity<>(baos.toByteArray(), headers, 200);
    }
}

The template lives at src/main/resources/templates/invoice.html as a normal Thymeleaf file. Whatever styling and layout your customer-facing UI already renders, you reuse here. That's the point: one HTML source of truth, two outputs (web and PDF).

Async rendering with a bounded executor

The sync endpoint is fine for one-off invoice downloads. For anything that takes more than two seconds or runs in a burst, push the render off the request thread onto a dedicated, bounded executor:

@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean(name = "pdfExecutor")
    public Executor pdfExecutor() {
        ThreadPoolTaskExecutor exec = new ThreadPoolTaskExecutor();
        exec.setCorePoolSize(4);
        exec.setMaxPoolSize(8);
        exec.setQueueCapacity(50);
        exec.setThreadNamePrefix("pdf-");
        exec.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        exec.initialize();
        return exec;
    }
}

Two settings earn their place. The bounded queue (capacity 50) stops a runaway batch job from consuming unbounded heap. CallerRunsPolicy ensures that when the queue fills, the caller blocks instead of dropping work silently. Pool size should match the vCPUs you'll dedicate to PDF rendering, not the total your container has, because rendering is CPU-bound and will starve every other workload sharing the JVM.

For long renders or batch jobs, return 202 Accepted immediately, kick the work onto the pool, and POST the finished PDF URL to a customer-supplied webhook when it lands. This is the same pattern hosted APIs use internally, and you can implement it locally with @Async plus your existing job table.

Handling render errors honestly

Three failure modes show up in production. Catch them explicitly, because the default Spring stack trace tells the caller nothing useful:

try {
    builder.run();
} catch (IOException e) {
    log.error("PDF stream failure for invoice {}", id, e);
    throw new PdfRenderException("Stream failure", e);
} catch (OutOfMemoryError oom) {
    log.error("OOM rendering invoice {}; document too large", id, oom);
    metrics.counter("pdf.oom").increment();
    throw oom;
} catch (RuntimeException e) {
    log.error("Render failed for invoice {}: {}", id, e.getMessage(), e);
    throw new PdfRenderException("Render failed", e);
}

One mistake to avoid: retrying renders inside the same JVM after an OutOfMemoryError. The heap is fragmented, GC is already thrashing, and the retry usually produces a second OOM on a smaller document while the first one is still cleaning up. Fail the job, record the document size that broke it, and either tune the heap or push that render to a hosted engine instead.

Priya's React dashboard that wouldn't render

Priya, a backend engineer at a B2B analytics company, spent a week trying to make Open HTML to PDF render a React dashboard. The page worked beautifully in Chrome: D3 charts, late-loading webfonts, the whole thing. The PDF came out as a styled wrapper around four empty rectangles where the charts should have been. The library wasn't broken. Open HTML to PDF (like every pure-Java HTML-to-PDF library) doesn't execute JavaScript, so the charts never painted. The fix wasn't a library swap; it was switching the rendering path entirely. She kept Open HTML to PDF for the invoice flow that didn't need JS, and routed the dashboard exports to a hosted Chrome engine with a wait_for selector on the final chart. Two days of work, including the API integration.

Benchmarks: which Java HTML-to-PDF library is fastest?

The honest answer is "it depends on the document," but the order of magnitude is predictable. Here's what teams typically see rendering a single-page styled invoice on Java 21 in a 4 vCPU container:

Library Cold render Warm render Peak heap (typical)
Open HTML to PDF 350 to 600 ms 80 to 150 ms 60 to 120 MB
iText 8 + pdfHTML 300 to 500 ms 60 to 130 ms 50 to 100 MB
Flying Saucer + OpenPDF 400 to 700 ms 90 to 180 ms 70 to 140 MB
Hosted Chrome engine (network) 800 to 1500 ms 800 to 1500 ms negligible local
Hosted wkhtmltopdf engine (network) 400 to 900 ms 400 to 900 ms negligible local

These ranges are directional; they match library-author benchmarks and what I see in production reports, not numbers I generated on a single test rig. Your real numbers will shift with document complexity, JVM warmup, GC settings, and disk I/O for streamed output.

What matters more than picking the fastest library is measuring the four numbers that predict pain:

  • Cold render time. The first call after JVM start. Captures classloader plus library init cost, which dominates serverless cold-starts.
  • Warm p50 and p99 under realistic concurrency. p99 is where the JVM tells you the truth about GC pauses and lock contention.
  • Peak heap per render. Run with -XX:+PrintGCDetails or a profiler. If a single render trips a full GC, your throughput ceiling is closer than the average suggests.
  • Sustained throughput at the concurrency you actually need. Single-render numbers lie about what a pool of 8 workers can hold per second.

The local-versus-hosted comparison flips when you factor in operations. A local library has near-zero per-render cost and unbounded scaling problems. A hosted API adds 300 to 800 ms of network latency and removes the scaling problem entirely. For an interactive "Download PDF" button, local usually wins. For a midnight batch of 50,000 statements, the network round-trip amortizes across the batch and the absence of heap pressure on your own JVM wins.

Tip: Watching a video alongside the docs helps if the API is new to you. Embed a short walkthrough from the Open HTML to PDF maintainers' channel here (TODO: editor to paste the verified YouTube embed URL; recommended search term: "Open HTML to PDF Spring Boot tutorial"). For Transformy integration walkthroughs, the Transformy documentation site links the latest videos.

Ready to test a hosted engine? Start on the Transformy free tier and run a single render against your real HTML before you decide. No credit card, no contract, and your test render takes about as long as a Maven build.

When Java's print engines hit a wall

Every Java team eventually meets one of three walls. Each one is where the question stops being "which library?" and starts being "call something else?"

Wall 1: JavaScript-rendered HTML

No popular Java HTML-to-PDF library executes JavaScript. Open HTML to PDF, Flying Saucer, OpenPDF, iText pdfHTML: print-CSS engines, every one. If the page paints with React or Vue, the library will quietly emit empty rectangles where your dynamic content should be. Three honest options when you hit this wall:

  1. Run Playwright for Java yourself for full browser control. You own the operations work.
  2. Stand up a self-hosted Gotenberg or Browserless container. Browser farm in a box, still your infrastructure to scale and patch.
  3. Call a hosted Chrome engine and skip the browser entirely.

Wall 2: bleeding-edge CSS

Open HTML to PDF covers CSS 2.1 with selected CSS 3. iText pdfHTML covers more, especially around paged media. Neither covers the bleeding edge: container queries, the latest grid features, some flex edge cases. Chrome rendering matches Chrome because it is Chrome. If your designer ships a layout that depends on a CSS feature shipped in the last 18 months, the print engine may render it differently from the browser, with no error to grep for.

Wall 3: scale, memory, and the 2 a.m. page

Open HTML to PDF holds the full DOM and rendered layout in heap per render. For one invoice this is fine. For a batch job generating 50,000 statements at midnight, the heap balloons, GC starts thrashing, and your container OOM-killer earns its salary. You can fight this with bounded pools, off-heap caching, and aggressive concurrency caps. Or you can stop owning the rendering and pay for someone else's serverless capacity instead. Both are valid; which fits depends on whether PDF rendering is your product or a feature.

Calling a hosted Chrome engine from Java

When you hit Wall 1, 2, or 3, Transformy's hosted API is a one-call swap. Two engines behind one HTTP endpoint: wkhtmltopdf for stable, deterministic invoice templates, and Headless Chrome for anything that needs JavaScript, modern CSS, or webfonts you can't ship with the app. Same auth and request shape for both, so the integration doesn't fork by engine.

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class TransformyExample {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newHttpClient();

        String body = """
            {
              "html": "<h1>Invoice #1041</h1><p>Total: $49.00</p>",
              "engine": "chrome",
              "wait_for": ".invoice-total",
              "margin": { "top": "20mm", "bottom": "20mm" }
            }
            """;

        HttpRequest req = HttpRequest.newBuilder()
            .uri(URI.create("https://api.transformy.io/v1/render"))
            .header("Authorization", "Bearer " + System.getenv("TRANSFORMY_API_KEY"))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();

        HttpResponse<String> res = client.send(req, HttpResponse.BodyHandlers.ofString());
        System.out.println(res.body());
        // {"url": "https://files.transformy.io/r/abc123.pdf"}
    }
}

Two settings in that body matter. engine: "chrome" says "render this in a real Chromium instance" with flexbox, JavaScript, webfonts, all of it. Swap to engine: "wkhtmltopdf" for the same Java code, same auth, same response shape, but a faster, lighter engine for stable invoice templates. wait_for tells the Chrome engine to hold the capture until a specific element exists in the DOM, which is how you stop emitting a half-painted dashboard. If you're migrating off a wkhtmltopdf binary on your own server, the Transformy wkhtmltopdf migration guide walks through the equivalent option names.

The team that stopped thinking about PDF rendering

A four-person SaaS team I advise used to spend the last week of every quarter babysitting Chromium processes for their report-export feature. They tried six fixes (heap tuning, process recycling, queue throttling) before they accepted the math: at their volume, the operations cost of running their own browser farm was three days a quarter of senior engineering time. They cut over to a hosted engine in an afternoon, kept Open HTML to PDF for the boring invoice flow that didn't need it, and got the three days back. The point isn't that hosted is always right. It's that there's a real number where it gets cheaper, and most teams find that number 18 months later than they should have.

FAQ: HTML to PDF in Java in 2026

What is the best Java library for HTML to PDF in 2026?

Open HTML to PDF is the best free Java library for HTML-to-PDF conversion in 2026. It's actively maintained, runs on Apache PDFBox 3, is LGPL-licensed, and covers the server-rendered, CSS-styled documents most teams need. For print-grade fidelity (PDF/A, accessibility tags, full CSS Paged Media), iText 8 with pdfHTML is stronger but requires a commercial license for closed-source or SaaS use.

Is iText free for commercial HTML-to-PDF use?

No. iText 8 and the pdfHTML add-on are AGPL. Any closed-source or SaaS application using iText needs a commercial license. iText doesn't publish prices publicly; real-world quotes generally start in the $5,000-per-year range. If that doesn't fit your budget, use Open HTML to PDF or call a hosted API.

How do I convert HTML to PDF in Spring Boot?

Render your Thymeleaf template to an HTML string with TemplateEngine.process(), normalize it through JSoup with syntax(xml), hand the string to Open HTML to PDF's PdfRendererBuilder, write the output to a ByteArrayOutputStream, and return it from your controller as ResponseEntity<byte[]> with Content-Type: application/pdf. The full controller example is above.

Can a Java HTML-to-PDF library render a JavaScript page?

No. Open HTML to PDF, Flying Saucer, OpenPDF, and iText pdfHTML are all print-CSS engines: they parse HTML and CSS but do not execute JavaScript. If your HTML depends on client-side rendering, you need a real browser engine. The options are Playwright for Java that you run yourself, a self-hosted Gotenberg or Browserless container, or a hosted Chrome-engine API.

Why does my CSS not render correctly in the PDF?

Three usual suspects. The library doesn't support the CSS feature (anything past CSS 2.1 is selective in Open HTML to PDF). The input isn't well-formed XHTML (run it through JSoup with syntax(xml) before passing it to the builder). Or you're using display: flex or grid in a way the engine handles imperfectly. The Headless Chrome path renders CSS exactly as Chrome does, which is the cleanest fix when the layout has to look right.

How do I handle webfonts when converting HTML to PDF in Java?

Self-host the font file in src/main/resources/fonts/, register it on the builder with builder.useFont(new File("…"), "FamilyName"), and reference that family name in CSS. Don't rely on the library pulling fonts from a CDN at render time. JVMs aren't browsers, and @font-face URLs will silently fall back to a default like Times New Roman.

Wrapping up: pick the library that fits the page, not your habits

Most Java teams ship HTML to PDF on the wrong library because they choose by habit instead of by document. The 2026 answer is simpler than the Stack Overflow archive suggests:

  • For server-rendered, CSS-styled documents (invoices, statements, reports from Thymeleaf or JSP), use Open HTML to PDF. Free, maintained, and good enough that you'll forget about it.
  • For print-grade output and you can pay, choose iText 8 with pdfHTML and budget the license.
  • For HTML that depends on JavaScript, modern CSS, or webfonts you can't ship with the app, use a hosted Chrome engine like Transformy, called from one HttpClient.send().

You can build the whole thing in Java. Sometimes you should. The honest test is whether HTML-to-PDF rendering is core to what your team ships, or a feature you'd rather forget about between releases. If it's the latter, render your first PDF on the Transformy free tier. Point it at the same HTML your Java app already produces, with the engine that fits the page. We'll be here when you outgrow Open HTML to PDF, and we'll tell you honestly when you haven't.

transformy
© 2014-2026 transformy.io — made in 🇧🇪 with ❤️ 🍫 🍟