Generating and Merging PDF Invoices with iTextPdf in Java
This article explains how to use the iTextPdf library in a Java SpringBoot project to create PDF invoice templates, fill fixed and dynamic product data, merge multiple PDFs, and output the final document, providing full Maven dependencies, code examples, and implementation steps.
Background
The original project used a costly reporting tool, but a new requirement demanded a lightweight solution for generating invoice PDFs. After researching alternatives, the iTextPdf library was chosen as the most suitable option.
Solution Overview
An invoice consists of two parts: fixed information (buyer and seller details) and dynamic product lines. The approach is to generate two separate PDFs for these parts and then concatenate them into a single document.
Specific Implementation
1. Add iTextPdf Dependencies
Include the following Maven dependencies in pom.xml to bring in iTextPdf and the Asian font support module.
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.2</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>2. Edit PDF Template
Use Adobe Acrobat (or any PDF form designer) to create a fillable template. Add text fields for each fixed piece of information; the field names become the keys used by iTextPdf when populating the form.
3. Write Java PDF Generation Program
3.1 Read the Template
Use PdfReader to load the template either from a file path or a byte array.
// Read local file (for testing)
PdfReader reader = new PdfReader("C:\\Users\\User\\Desktop\\开票预览模板.pdf");
// In production you would read the bytes from S3
PdfReader reader = new PdfReader(bytes);3.2 Fill Fixed Information
Use PdfStamper (also called a stamper) to populate the form fields. The key passed to setField must match the field name defined in the template.
ByteArrayOutputStream bos1 = new ByteArrayOutputStream();
PdfStamper stamper = new PdfStamper(reader, bos1);
AcroFields form = stamper.getAcroFields();
form.setGenerateAppearances(true);
form.setField("purName", "购买方对应公司");
// ...set other fields...
stamper.close();3.3 Generate Dynamic Product Table
Create a new Document , set a Chinese font with BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", false) , and build a PdfPTable based on a list of column meta‑information.
BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", false);
Font font = new Font(bfChinese, 12, Font.NORMAL, BaseColor.BLACK);
PdfPTable table = generatePdfPTable(720f, font, mapData, headInfos());
Document document = new Document(reader.getPageSize(1));
PdfWriter writer = PdfWriter.getInstance(document, bos2);
document.open();
document.newPage();
document.add(table);
document.close();
writer.close();3.4 Merge PDFs
Combine the fixed‑info PDF and the product‑table PDF using PdfCopy . Each page of the source PDFs is imported and added to the output document.
public static byte[] copy(List
files) throws DocumentException, IOException {
Document document = new Document();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PdfCopy copy = new PdfCopy(document, bos);
copy.setEncryption(null, null, PdfWriter.ALLOW_PRINTING, PdfWriter.STANDARD_ENCRYPTION_128);
document.open();
for (byte[] file : files) {
PdfReader reader = new PdfReader(file);
int n = reader.getNumberOfPages();
for (int page = 0; page < n; ) {
copy.addPage(copy.getImportedPage(reader, ++page));
}
reader.close();
}
document.close();
return bos.toByteArray();
}3.5 Output
In a production environment the merged byte array is uploaded to S3; for local testing it is written to a file.
List
files = new ArrayList<>();
files.add(bos1.toByteArray());
files.add(bos2.toByteArray());
byte[] merged = copy(files);
FileOutputStream fos = new FileOutputStream("C:\\Users\\User\\Desktop\\test3.pdf");
fos.write(merged);
fos.close();Post‑mortem
The author learned how to generate PDFs in Java, create fillable templates, handle dynamic tables, and merge multiple PDFs, providing a complete solution for invoice generation without relying on heavyweight reporting tools.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.