How to Generate PDFs in Java
Create a PDF in Java with Apache PDFBox, from Maven setup to fonts, images, errors, and licensing decisions.
To generate a PDF in Java, use a PDF library. Apache PDFBox is a practical starting point: add its Maven dependency, create a PDDocument, add a page and content, then save and close the document. PDFBox is licensed under Apache License 2.0 and supports creating PDFs from scratch, including embedded fonts and images. See the PDFBox project and its getting started guide.
1. Add PDFBox to a Maven project
The PDFBox 3.0 Getting Started page listed version 3.0.8 when the research for this guide was performed. Check the official page for a later release before building.
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>3.0.8</version>
</dependency>
Put this inside the <dependencies> element of your pom.xml. The example below uses Java APIs from PDFBox 3.0.
2. Create and save a PDF
Save this as src/main/java/example/GeneratePdf.java. It creates a one-page PDF named hello.pdf in the current working directory.
package example;
import java.io.IOException;
import java.nio.file.Path;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
public class GeneratePdf {
public static void main(String[] args) throws IOException {
Path output = Path.of("hello.pdf");
try (PDDocument document = new PDDocument()) {
PDPage page = new PDPage();
document.addPage(page);
try (PDPageContentStream content = new PDPageContentStream(document, page)) {
content.beginText();
content.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 18);
content.newLineAtOffset(72, 720);
content.showText("Hello from Java and PDFBox");
content.endText();
}
document.save(output.toFile());
}
System.out.println("Created " + output.toAbsolutePath());
}
}
Compile and run with Maven from the project directory:
mvn compile
mvn exec:java -Dexec.mainClass=example.GeneratePdf
If you use the exec plugin, configure it in your Maven build or invoke the class through your IDE. The essential lifecycle is the same: create the document, add pages and content, save it, and close the resources. Try-with-resources closes both the content stream and document if writing fails.
3. Understand coordinates and layout
PDFBox gives you low-level drawing operations rather than a high-level HTML-like layout engine in this example. The coordinates in newLineAtOffset(x, y) are measured from the lower-left of the page. A standard letter page is 612 by 792 points; one point is 1/72 inch. The sample’s baseline at y=720 leaves room above the text.
For more content, calculate positions and text widths yourself, or build reusable layout helpers for paragraphs, margins, headers, and page breaks. A content stream does not automatically wrap text or create new pages when a paragraph reaches the bottom. Check the page dimensions and your layout rules before writing each block.
4. Add multiple pages and paragraphs
For a multi-page document, create a new PDPage when your layout reaches its page-break threshold, add it to the same document, and open a content stream for that page. The following helper writes a single line; for paragraphs, split or wrap text to fit your chosen content width before calling it.
static void writeLine(PDDocument document, PDPage page, String text,
float x, float y, float fontSize) throws IOException {
try (PDPageContentStream content = new PDPageContentStream(document, page)) {
content.beginText();
content.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), fontSize);
content.newLineAtOffset(x, y);
content.showText(text);
content.endText();
}
}
For repeated content, consider keeping one stream open while writing a page, then close it before moving to the next page. Text drawing calls need to be inside a text block between beginText() and endText(). Keep page-level state such as current y position in your own layout code.
5. Fonts, Unicode, and images
Fonts and non-ASCII text
The Standard 14 fonts used in the basic example are suitable for simple text, but they do not cover every Unicode character. For multilingual content, load an appropriate TrueType font and embed it in the PDF:
import java.io.File;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
PDType0Font font = PDType0Font.load(document, new File("fonts/NotoSans-Regular.ttf"));
Use that font with content.setFont(font, size). Ensure the font file is available in the deployed application and that its license permits your intended use. Test glyph coverage for the scripts and symbols your content contains. Font embedding increases file size, and some font licensing terms restrict embedding.
Images
PDFBox supports embedding images. A typical workflow loads an image as a PDImageXObject and draws it onto a page with PDPageContentStream.drawImage. Choose image dimensions and placement deliberately: a large source image can make a PDF unnecessarily large, while scaling it too far can make it unreadable. Consult the current PDFBox documentation for the image APIs that match your version.
6. Choose a library and check the license
| Library | License information | When to evaluate it |
|---|---|---|
| Apache PDFBox | Apache License 2.0 | PDF creation and manipulation, including documented font and image support. |
| iText Core | Dual-licensed under AGPL and commercial terms | Evaluate its documented PDF creation and editing capabilities and whether its license fits your distribution model. |
iText’s Java repository documents its licensing model and capabilities; its installation guide covers Maven setup and a Java example. iText says a commercial license is available for software or services that cannot comply with AGPL terms. Confirm current terms directly with the project. Licensing can affect how you distribute software or provide a service, so review the applicable license with your organization before shipping. This is not legal advice.
Choose based on your needed layout control, forms, signing, archival or accessibility requirements, Java baseline, support needs, and license. The cited sources do not establish a universal performance or ease-of-use winner.
7. Handle output streams and failures
document.save(File) writes to a file. If your application returns a PDF from a web endpoint or stores it in an object store, save to an appropriate output stream using the API supported by your PDFBox version. Own and close streams at the correct boundary: close the PDF document after saving, and avoid closing a response stream before the web framework finishes sending it.
- Write to a temporary file and move it into place if readers must never see a partially written final file.
- For HTTP responses, set the PDF content type and a sensible download filename in your web framework.
- Handle
IOExceptionat the application boundary and log enough context to identify the document job without logging sensitive document content. - Validate user-provided text, image sizes, and paths; do not let untrusted input choose arbitrary server-side output paths.
8. Troubleshooting common problems
| Symptom | Likely cause | Fix |
|---|---|---|
| Maven cannot resolve PDFBox | Dependency coordinates, version, repository connectivity, or Maven configuration is wrong. | Check the group, artifact, and version against the official getting-started page; retry after resolving repository or proxy errors. |
| Text is missing or garbled | The selected font lacks the required glyphs, or the content encoding does not represent the intended characters. | Use an embedded TrueType font with the needed glyph coverage and test representative multilingual strings. |
IllegalStateException while writing text |
A text operation was called outside a text block, or text blocks were improperly nested. | Pair beginText() and endText() correctly and keep text operations within that block. |
| PDF is blank or content is off the page | Coordinates use the lower-left origin, or the content stream was not closed/saved as expected. | Check page dimensions and y positions; close streams and call save before closing the document. |
| Output file is incomplete or corrupt | Saving failed, the process stopped early, or the document was not finalized. | Propagate save errors, use try-with-resources, and write to a temporary destination before publishing the completed file. |
| PDF is unexpectedly large | High-resolution images or embedded fonts increase output size. | Resize images for their display dimensions and embed only the fonts and assets the document requires. |
9. Performance, reliability, and cost
The research sources provide no version-specific PDFBox benchmark, so estimate performance with representative documents from your own workload. Large images, font embedding, page count, and concurrent generation can affect memory, CPU, and output size. Measure peak memory and latency with realistic input, place limits on uploaded assets, and avoid keeping many complete documents in memory at once without a reason.
Use try-with-resources so documents and content streams are closed on both success and failure. For reliable delivery, treat generation as a job with explicit failure handling, and only make a file available after save succeeds. PDFBox is an open-source Apache-licensed project; account for engineering, hosting, storage, and any support costs in your application rather than assuming PDF generation itself has no operational cost.
10. Validate the generated PDF
A file that opens is not automatically accessible, archival-valid, secure, or visually consistent across viewers. If you require forms, digital signatures, PDF/A archival conformance, or PDF/UA accessibility, confirm the chosen library’s current support and implement those requirements explicitly. Test generated files in the PDF readers and workflows your users rely on. PDFBox also documents operations such as filling forms, signing, merging, splitting, extracting, and rendering; these capabilities still require configuration and validation for your specific use case. See the PDFBox command-line documentation for workflow examples.
Or skip the browser setup
If the PDF you need is a rendered web page, ScreenshotNeo can capture a URL through one GET request. This complements Java PDF generation for web-page capture; it is not a Java PDF library. See the ScreenshotNeo API documentation for available options and PDF output configuration.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
These examples request the default screenshot output. Check the docs for the PDF option when you need a PDF of a page. ScreenshotNeo accepts cookie banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, inspect page information, and capture PDFs. The free plan includes 1,000 shots a month with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan. Learn more at ScreenshotNeo, or sign up free for 1,000 screenshots a month with no card.
FAQ
Can Java generate a PDF without a third-party library?
For application code, a PDF library is the practical route because it handles the PDF document format and writing operations. PDFBox is one documented option.
Does adding text automatically wrap or paginate?
No. The low-level content stream example places text at coordinates. Implement line wrapping and page-break logic or use a higher-level layout approach.
Is PDFBox suitable for PDFs containing Unicode text?
Yes, with a font that contains the required glyphs embedded in the document. Verify script coverage and the font’s embedding terms.
Should I choose PDFBox or iText?
Start with functional requirements and license fit. PDFBox is Apache 2.0; iText is dual-licensed under AGPL/commercial terms. Review current project documentation and your deployment model before deciding.
How do I know the PDF is accessible or archival-valid?
Opening successfully does not prove conformance. Identify the required standard, implement its requirements, and validate the output with suitable checks and reader workflows.


