Generate PDF document using JasperReports and Spring boot

Java featured image

1. Overview

Almost every JAVA project need to generate PDF documents for its users, for example:

  • For an e-commerce project, we generate Invoice, receipt and return…
  • For a supervision and monitoring application, we generate reports.

The JasperReports is one of the best Java libraries for generating PDF documents.

In this article, I will show you how to generate PDF documents using JasperReports, Spring Boot and Jaspersoft Studio.

2. Implementation

Let’s suppose we are working on an e-commerce application (e.g. Hybris), and we want to generate the invoice as a PDF for our customers.

This is a simplified version of how we are going to set up the generation of the invoice using JasperReports.

Overview JasperReports project

 

  • I18n-fr.properties, i18n-en.properties, i18n.properties : is an i18n resource bundles used to translate the generated pdf file.
  • Template.jrxml : is a JasportReports template created using Jaspersoft Studio.
  • Invoice.pdf : is the final pdf document generated with the help of JasperReports.

2.1. JasperReports Template.

First of all, we need to create the JasperReports template of our invoice PDF document.

1. Download and install the Jaspersoft Studio.

2. Open the Jaspersoft Studio and create a JasperReports template using the toolbox that comes with it.

I have already generate a JasperReports template, you can find it in Github.

The JasperReports template is an XML file with extension .jrxml.

Jaspersoft studio jrxml template

Let’s shed light on some of the JRXML elements :

<image>
    <reportElement x="0" y="0" width="160" height="50" uuid="e2b4ea04-ecb9-494d-a2af-63639b22b1cb"/>
    <imageExpression><![CDATA[$P{logo}]]></imageExpression>
</image>

This code snippet will add an image to the final pdf file, <![CDATA[$P{logo}]]> this parameter will be filled in Java with path to the image.

<textField>
    <reportElement x="0" y="51" width="380" height="19" uuid="76193d2c-2228-4944-b5d1-999cd7ef6168"/>
    <textElement verticalAlignment="Top">
        <font fontName="Arial" size="11" isBold="false"/>
    </textElement>
    <textFieldExpression><![CDATA[$P{order}.getAddress().getStreetName()]]></textFieldExpression>
</textField>

This code snippet adds a dynamic attribute to the pdf file, it will be filled in runtime with proper value:$P{order}.getAddress().getStreetName()

<textField>
    <reportElement x="0" y="0" width="380" height="30" uuid="932e4bce-f54c-492c-ae77-a55089c9eb9f"/>
    <textElement verticalAlignment="Middle">
        <font fontName="Arial Black" size="13" isBold="true"/>
    </textElement>
    <textFieldExpression><![CDATA[$R{jasper.invoice.address.label}]]></textFieldExpression>
</textField>

This one is similar to the last one, however this time the field value will be retrieved from the i18n resource bundle depending on the chosen languages (fr, en, it…).

2.2. Create i18n Resource Bundle

1. Create a file property for each language you need.

2. Extract all the static field from your jrxml template and add them (translated) to your files properties.

## i18n.properties (default and fullback) ##
jasper.invoice.title.label=Invoice
jasper.invoice.address.label=Shipping Address
jasper.invoice.entry.product.label=Product Name
jasper.invoice.entry.price.label=Unit Price
jasper.invoice.entry.quantity.label=Quantity
jasper.invoice.entry.total.label=Total
jasper.invoice.total.label=Order Total
## i18n_en.properties (for English) ##
jasper.invoice.title.label=Invoice
jasper.invoice.address.label=Shipping Address
jasper.invoice.entry.product.label=Product Name
jasper.invoice.entry.price.label=Unit Price
jasper.invoice.entry.quantity.label=Quantity
jasper.invoice.entry.total.label=Total
jasper.invoice.total.label=Order Total
## i18n.properties (for French) ##
jasper.invoice.title.label=Facture
jasper.invoice.address.label=Adresse de facturation
jasper.invoice.entry.product.label=Nom de produit
jasper.invoice.entry.price.label=Prix unitaire
jasper.invoice.entry.quantity.label=Quantite
jasper.invoice.entry.total.label=Total
jasper.invoice.total.label=Total de la facture

2.3. Create Spring Boot Project

1. Create a Spring Boot project using Spring Initializr.

2. Add the JasperReports and the Spring Support artifacts to your pom.xml.

  • JasperReports artifact
<dependency>
    <groupId>net.sf.jasperreports</groupId>
    <artifactId>jasperreports</artifactId>
    <version>6.6.0</version>
</dependency>
  • Spring Support artifact
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-support</artifactId>
    <version>2.0.8</version>
</dependency>

3. Add the jrxml template, the i18n files properties and images… to the resources folder of your Spring Boot project.

Jasperreport project structure

2.4. Generate PDF in Java

1. Create a Java service InvoiceService, we will use it to generate the PDF invoice.

@Service
public class InvoiceService {

	// OrderModel is a POJO contains all the data about the Invoice
	// Locale is used to localize the PDF file (French, English...)
    public void generateInvoiceFor(OrderModel order, Locale locale) throws IOException {

        // We will generate the PDF here 
        
    }
}

OrderModel is a POJO object that holds all the data about the invoice, you can find it in Github.

2. Create a PDF File and initiate a FileOutputStream.

@Service
public class InvoiceService {
	
	Logger log = LogManager.getLogger(InvoiceService.class);

    public void generateInvoiceFor(OrderModel order, Locale locale) throws IOException {

        // Create a temporary PDF file
        File pdfFile = File.createTempFile("my-invoice", ".pdf"); 
        
        // Initiate a FileOutputStream
        try(FileOutputStream pos = new FileOutputStream(pdfFile))
        {
			// We will generate PDF here
        }
        catch (final Exception e)
        {
            log.error(String.format("An error occured during PDF creation: %s", e));
        }
        
    }
}

3. Load the JRXML template.

@Service
public class InvoiceService {
	
	Logger log = LogManager.getLogger(InvoiceService.class);
	
	// Path to the jrxml template
	private final String invoice_template_path = "/jasper/invoice_template.jrxml";

    public void generateInvoiceFor(OrderModel order, Locale locale) throws IOException {

        File pdfFile = File.createTempFile("my-invoice", ".pdf"); 
        
        try(FileOutputStream pos = new FileOutputStream(pdfFile))
        {
			// Load the invoice jrxml template.
            final JasperReport report = loadTemplate();
            
        }
        catch (final Exception e)
        {
            log.error(String.format("An error occured during PDF creation: %s", e));
        }
    }
    
     // Load invoice jrxml template
    private JasperReport loadTemplate() throws JRException {

        log.info(String.format("Invoice template path : %s", invoice_template_path));

        final InputStream reportInputStream = getClass().getResourceAsStream(invoice_template_path);
        final JasperDesign jasperDesign = JRXmlLoader.load(reportInputStream);

        return JasperCompileManager.compileReport(jasperDesign);
    }
}

4. Fill the parameters and the DataSource.

@Service
public class InvoiceService {
	
	Logger log = LogManager.getLogger(InvoiceService.class);
	
	private static final String logo_path = "/jasper/images/stackextend-logo.png";
	private final String invoice_template_path = "/jasper/invoice_template.jrxml";

    public void generateInvoiceFor(OrderModel order, Locale locale) throws IOException {

        File pdfFile = File.createTempFile("my-invoice", ".pdf"); 
        
        try(FileOutputStream pos = new FileOutputStream(pdfFile))
        {
			// Load the invoice jrxml template.
            final JasperReport report = loadTemplate();
            
              // Create parameters map.
            final Map<String, Object> parameters = parameters(order, locale);

            // Create an empty datasource.
            final JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(Collections.singletonList("Invoice"));
            
        }
        catch (final Exception e)
        {
            log.error(String.format("An error occured during PDF creation: %s", e));
        }
    }
        
    // Fill template order parametres
    private Map<String, Object> parameters(OrderModel order, Locale locale) {
        final Map<String, Object> parameters = new HashMap<>();

        parameters.put("logo", getClass().getResourceAsStream(logo_path));
        parameters.put("order",  order);
        parameters.put("REPORT_LOCALE", locale);

        return parameters;
    }
    
    // ...
}

5. Generate the PDF using the JasperReportsUtils of the Spring Support.

@Service
public class InvoiceService {
	
	Logger log = LogManager.getLogger(InvoiceService.class);
	
	private static final String logo_path = "/jasper/images/stackextend-logo.png";
	private final String invoice_template_path = "/jasper/invoice_template.jrxml";

    public void generateInvoiceFor(OrderModel order, Locale locale) throws IOException {

        File pdfFile = File.createTempFile("my-invoice", ".pdf"); 
        
        try(FileOutputStream pos = new FileOutputStream(pdfFile))
        {
			// Load the invoice jrxml template.
            final JasperReport report = loadTemplate();
            
              // Create parameters map.
            final Map<String, Object> parameters = parameters(order, locale);

            // Create an empty datasource.
            final JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(Collections.singletonList("Invoice"));
            
            // Render the PDF file
            JasperReportsUtils.renderAsPdf(report, parameters, dataSource, pos);
            
        }
        catch (final Exception e)
        {
            log.error(String.format("An error occured during PDF creation: %s", e));
        }
    }
            
    // ...
}

6. Call your service InvoiceService.generateInvoiceFor(...) with the proper values to generate the PDF.

@SpringBootApplication
public class GeneratePdfDocumentApplication implements CommandLineRunner {

    Logger log = LogManager.getLogger(GeneratePdfDocumentApplication.class);

	@Resource
	private OrderService orderService;
	@Resource
    private InvoiceService invoiceService;

	@Override
	public void run(String... args) throws Exception {
		
        log.info("Start invoice generation...");

        OrderModel order = orderService.getOrderByCode("XYZ123456789");

        invoiceService.generateInvoiceFor(order, Locale.FRANCE);

        log.info("Invoice generated successfully...");
	}
	
	// ...
}

7. If everything goes well the invoice as a PDF file will be generated.

Generated invoice pdf using JasperReports

Find the source code of this example in GitHub.

 

3
Leave a Reply

avatar
3 Comment threads
0 Thread replies
3 Followers
 
Most reacted comment
Hottest comment thread
3 Comment authors
zakoveeraraghavabhavna Recent comment authors

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
newest oldest most voted
Notify of
bhavna
Guest
bhavna

most insightful tutorial on this topic! thanks

veeraraghava
Guest
veeraraghava

how to call deafault method our run method in commandline runner interface as rest controller in requestmapping

zako
Guest
zako

thx , just this method doesnt works on my eclipse , final JasperReport report = loadTemplate();