Backend Development 12 min read

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.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Generating and Merging PDF Invoices with iTextPdf in Java

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.

Javabackend developmentSpringBootiTextPDFPDF generation
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.