Backend Development 11 min read

Generating Dynamic PDF Invoices with iTextPdf in Java

This article explains how to use the iTextPdf library in a Java SpringBoot project to create, fill, and merge PDF invoice templates—covering dependency setup, template design, dynamic table generation, and final PDF concatenation for both fixed and variable invoice data.

Architecture Digest
Architecture Digest
Architecture Digest
Generating Dynamic PDF Invoices with iTextPdf in Java

Background – The author needed to replace a costly reporting tool (FineReport) with a lightweight solution for generating PDF invoices in a new project. After researching alternatives, iTextPdf was chosen for its flexibility and support for Chinese characters.

Solution Overview – The approach consists of creating two PDF parts: a static template for fixed information (buyer, seller, etc.) and a dynamic table for product details, then merging them into a single document.

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. Design the PDF template – Use Adobe Acrobat to create a fillable form, placing text fields whose names will be used as keys when populating data with iTextPdf.

3. Java implementation

3.1 Read the template

// Read local file (for demo)
PdfReader reader = new PdfReader("C:\\Users\\User\\Desktop\\开票预览模板.pdf");
// In production you would read from a byte[] obtained from S3
PdfReader reader = new PdfReader(bytes);

3.2 Fill static fields

ByteArrayOutputStream bos1 = new ByteArrayOutputStream();
PdfStamper stamper = new PdfStamper(reader, bos1);
AcroFields form = stamper.getAcroFields();
form.setGenerateAppearances(true);
form.setField("purName", "购买方对应公司");
stamper.close();

3.3 Generate dynamic product table

Document document = new Document(reader.getPageSize(1));
PdfWriter writer = PdfWriter.getInstance(document, bos2);
document.open();
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.add(table);
document.close();
writer.close();

Helper method to build the table:

public static PdfPTable generatePdfPTable(float totalWidth, Font font, List
> data, List
meta) throws DocumentException {
    PdfPTable table = new PdfPTable(meta.size());
    table.setTotalWidth(totalWidth);
    // set column widths
    float[] widths = meta.stream().mapToFloat(HeadRowMetaInfo::getWidth).toArray();
    table.setWidths(widths);
    // header
    for (HeadRowMetaInfo h : meta) {
        table.addCell(new Phrase(h.getColName(), font));
    }
    // rows
    for (Map
row : data) {
        for (HeadRowMetaInfo h : meta) {
            Object val = row.get(h.getColKey());
            table.addCell(new Phrase(val != null ? val.toString() : "-", font));
        }
    }
    table.setLockedWidth(true);
    return table;
}

3.4 Merge the two PDFs

public static byte[] copy(List
files) throws DocumentException, IOException {
    Document document = new Document();
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    PdfCopy copy = new PdfCopy(document, bos);
    document.open();
    for (byte[] file : files) {
        PdfReader reader = new PdfReader(file);
        int n = reader.getNumberOfPages();
        for (int i = 1; i <= n; i++) {
            copy.addPage(copy.getImportedPage(reader, i));
        }
        reader.close();
    }
    document.close();
    return bos.toByteArray();
}

Finally, the two byte arrays (bos1 and bos2) are combined and written to a file or uploaded to S3.

Conclusion – By creating a fillable PDF template for static content and using iTextPdf to generate a dynamic table for product lines, developers can produce complete invoice PDFs programmatically, handling both fixed and variable data efficiently.

backendJavaSpringBootiTextPDFPDF generationInvoiceDynamic PDF
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

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.