Backend Development 12 min read

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.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Generating Dynamic PDF Invoices with iTextPdf in Java

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.

JavamavenSpringBootPDFiTextPDF
Code Ape Tech Column
Written by

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

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.