Generating PDF Receipts in Java with iText

Isurie K. Liyanage
3 min readJul 9, 2024

--

In modern applications, generating PDF documents is a common requirement, especially for creating receipts, reports, or any other formatted documents. This article walks you through the process of creating a PDF receipt generator in Java using the iText library.

Overview

We’ll implement a class, ReceiptGenerator, which generates a PDF receipt for fund transfer transactions. The PDF will include a logo, transaction details, and a timestamp. We will use iText for PDF generation and Lombok for logging.

Setting Up the Environment

First, add the iText library dependency to your pom.xml file:

<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.10</version>
</dependency>

The Receipt Generator Class

Let’s break down the ReceiptGenerator class.

@Slf4j
@Component
public class ReceiptGenerator {

@Value("${fund.transfer.receipt.download.folder}")
private String receiptDownloadPath;

@Value("${bank.receipt.companylogo.path}")
String companyLogoPath;

We use Lombok’s @Slf4j for logging and Spring's @Component to mark this class as a Spring bean. The @Value annotations inject values from the application's properties file.

Generating the Receipt

public String generateReceipt(FundTransferTransaction fundTransferTransaction, TransferStateHolder transferStateHolder) {
String downloadPath = "";
String filePath = "";

try {
downloadPath = receiptDownloadPath;
log.info("receiptDownloadPath:" + receiptDownloadPath);

final String timestamp = new SimpleDateFormat("yyyyMMddhhmm").format(new Date());
filePath = downloadPath + "Fund_Transfer" + "_" + fundTransferTransaction.getAux_no() + "_" + timestamp + ".pdf";
log.info("filePath --> " + filePath);

We start by setting the download path and file path for the PDF. The timestamp ensures each file name is unique. sample receipt name will be: Fund_Transfer_0000000001710503147678_202407020346.pdf

Setting Up the Document

        int numberOfDetails = 10;
float rowHeight = 20f;
float logoHeight = 100f;
float headerHeight = 40f;
float totalHeight = logoHeight + headerHeight + (numberOfDetails * rowHeight) + 50f;

Rectangle pageSize = new Rectangle(PageSize.A4.getWidth(), totalHeight);
Document document = new Document(pageSize);

PdfWriter.getInstance(document, new FileOutputStream(filePath));
document.open();

We calculate the custom page size based on the number of details to be included and initialize the iText Document.

Adding the Logo and Header

        Font catFont = FontFactory.getFont(FontFactory.TIMES_BOLD, 20, BaseColor.BLACK);
Font font = FontFactory.getFont(FontFactory.COURIER, 13, BaseColor.BLACK);
Font hFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD, 10, BaseColor.BLACK);

String logoPath = companyLogoPath + "Companylogo.png";
log.info("companyLogoPath:" + companyLogoPath);
Image logo = Image.getInstance(logoPath);
logo.scaleToFit(150, 100);

PdfPTable headerTable = new PdfPTable(2);
headerTable.setWidthPercentage(100);
headerTable.setWidths(new int[]{1, 4});

PdfPCell logoCell = new PdfPCell(logo);
logoCell.setBorder(Rectangle.NO_BORDER);
logoCell.setHorizontalAlignment(Element.ALIGN_LEFT);
headerTable.addCell(logoCell);

Paragraph dateParagraph = new Paragraph(String.valueOf(new Date()), hFont);
dateParagraph.setAlignment(Element.ALIGN_RIGHT);
Paragraph titleParagraph = new Paragraph("Payment Receipt", catFont);
titleParagraph.setAlignment(Element.ALIGN_RIGHT);

PdfPCell dateTitleCell = new PdfPCell();
dateTitleCell.addElement(dateParagraph);
dateTitleCell.addElement(titleParagraph);
dateTitleCell.setBorder(Rectangle.NO_BORDER);
dateTitleCell.setHorizontalAlignment(Element.ALIGN_RIGHT);
headerTable.addCell(dateTitleCell);

document.add(headerTable);

We create and add a header table that includes the company logo and the receipt title with the current date.

Adding Transaction Details

        PdfPTable detailsTable = new PdfPTable(2);
detailsTable.setWidthPercentage(100);
detailsTable.setSpacingBefore(20f);

addDetailRow(detailsTable, "Aux No:", fundTransferTransaction.getAux_no(), font);
// Setting transaction types
String tranType = getTransactionType(fundTransferTransaction.getTnx_type());
addDetailRow(detailsTable, "Transaction Type:", tranType, font);
addDetailRow(detailsTable, "From Account:", fundTransferTransaction.getFr_acc_id(), font);
addDetailRow(detailsTable, "", fundTransferTransaction.getFr_acc_lbl(), font);
addDetailRow(detailsTable, "To Account:", fundTransferTransaction.getToAccountId(), font);
addDetailRow(detailsTable, "", fundTransferTransaction.getTo_acc_lbl(), font);
String data_14 = fundTransferTransaction.getAccountCurr() + "_" + fundTransferTransaction.getOriginal_amt();
addDetailRow(detailsTable, "Transaction Amount:", data_14, font);
addDetailRow(detailsTable, "Narration:", fundTransferTransaction.getNarrat() != null ? fundTransferTransaction.getNarrat() : "N/A", font);
addDetailRow(detailsTable, "Transaction Date:", String.valueOf(fundTransferTransaction.getAdded_date()), font);
addDetailRow(detailsTable, "Fund Transfer Status:", transferStateHolder.getTransferStates().getMessage(), font);

document.add(detailsTable);
document.close();

The transaction details are added to the PDF using a details table. The addDetailRow method ensures the details are formatted correctly.

Helper Methods

private static void addDetailRow(PdfPTable table, String label, String value, Font font) {
PdfPCell labelCell = new PdfPCell(new Phrase(label, font));
labelCell.setBorder(Rectangle.NO_BORDER);
labelCell.setHorizontalAlignment(Element.ALIGN_LEFT);

PdfPCell valueCell = new PdfPCell(new Phrase(value, font));
valueCell.setBorder(Rectangle.NO_BORDER);
valueCell.setHorizontalAlignment(Element.ALIGN_RIGHT);

table.addCell(labelCell);
table.addCell(valueCell);
}

private String getTransactionType(String tnxType) {
switch (tnxType) {
case "1":return "Own Account Fund transfer";
case "2":return "Third party Fund transfer";
case "3":return "Inter bank Fund transfer";
case "4":return "Mobile Fund transfer";
case "5":return "Overseas Fund transfer";
default:return "Fund transfer";
}
}

The addDetailRow method helps in adding rows to the details table, while the getTransactionType method maps transaction type codes to human-readable strings.

Handling Exceptions

We handle exceptions for various issues that might occur during PDF generation:

} catch (DocumentException | FileNotFoundException e) {
log.error("Error in PDF generation: ", e);
throw new RuntimeException(e);
} catch (IOException e) {
log.error("IOException: ", e);
throw new RuntimeException(e);
}

The full Code of my example work can be accessed here.

Sample Receipt template with test data and sample logo

Conclusion

The ReceiptGenerator class demonstrates a practical approach to generating PDF receipts using iText in a Spring Boot application. Following this example, you can customize the receipt content and formatting to fit your specific requirements. Happy coding!

--

--

Isurie K. Liyanage
Isurie K. Liyanage

Written by Isurie K. Liyanage

Technical Writer | BSc. (Hons.) IT — UoM | Software Engineer

No responses yet