Generating Dynamic PDF Invoices with iTextPdf in Java
This article explains how to use the iTextPdf library in Java to design a PDF template, fill fixed fields, dynamically generate product tables, and merge the two parts into a single invoice PDF, including Maven dependencies and sample code.
Background
Previously a similar feature was implemented with Jasper, but the technology is outdated; after consulting ChatGPT we learned that iTextPdf is a better choice for dynamic PDF generation.
Solution
We first examine the real invoice preview template. The invoice consists of two parts: fixed information (buyer and seller) and product information, which may contain multiple rows and must be filled dynamically.
The approach is to generate two PDFs—one for each part—and then merge them into a single document.
For the fixed part we can design a PDF template with Adobe Acrobat and fill its fields in Java. For the product part we dynamically create a table using iTextPdf.
Implementation Details
1. Add iTextPdf dependencies
<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. Create the PDF template
Use Adobe Acrobat to create a form, placing text fields whose "Name" attribute will be used as keys when filling the form with iTextPdf.
3. Write Java program to generate PDF
3.1 Read the PDF template
PdfReader can read a file path or a byte array.
// Read local file, of course production code would use a stream
PdfReader reader = new PdfReader("C:\\Users\\User\\Desktop\\开票预览模板.pdf");
// Production: read from S3 byte[]
PdfReader reader = new PdfReader(bytes);3.2 Fill fixed fields and create first PDF
PdfStamper is used to populate form fields.
// Temporary output stream for the fixed part
ByteArrayOutputStream bos1 = new ByteArrayOutputStream();
PdfStamper stamper = new PdfStamper(reader, bos1);
AcroFields form = stamper.getAcroFields();
form.setGenerateAppearances(true);
form.setField("purName", "购买方对应公司");
stamper.close();3.3 Create product table PDF
Instantiate Document and PdfPTable, set a Chinese font, and add rows dynamically.
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.FileOutputStream;
public class AdjustTablePositionInPdf {
public static void main(String[] args) {
try {
Document document = new Document(PageSize.A4);
PdfWriter.getInstance(document, new FileOutputStream("C:\\Users\\User\\Desktop\\adjusted_table_position.pdf"));
document.open();
document.add(new Paragraph("Test PDF with Table"));
PdfPTable table = new PdfPTable(2);
table.addCell("Name");
table.addCell("Age");
table.addCell("Alice");
table.addCell("25");
table.addCell("Bob");
table.addCell("30");
table.setSpacingBefore(20f);
table.setSpacingAfter(20f);
table.setTotalWidth(300f);
document.add(table);
document.close();
System.out.println("PDF file generated successfully!");
} catch (Exception e) {
e.printStackTrace();
}
}
}3.4 Merge PDFs
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();
byte[] bytes = bos.toByteArray();
bos.close();
return bytes;
}3.5 Output
In production the merged byte array is uploaded to S3; in the demo we write it to a local file.
log.info(returnPath + " pdf模板填充成功,进行合并");
List
files = new ArrayList<>();
files.add(bos1.toByteArray());
files.add(bos2.toByteArray());
byte[] s3bytes = copy(files);
bos1.close();
bos2.close();
reader.close();
String outputPath = "C:\\Users\\User\\Desktop\\test3.pdf";
FileOutputStream fileOutputStream = new FileOutputStream(outputPath);
fileOutputStream.write(s3bytes);
fileOutputStream.close();Conclusion
This tutorial demonstrates how to generate PDFs in Java using iTextPdf: design a template for fixed fields, dynamically build tables for variable data, and merge the results into a single document.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
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.