Generate PDF document using JasperReports and Spring boot
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.
- 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.
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.
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.
Demo of the final version
Find the source code of this example in GitHub.
Software Craftsmanship, Stackextend author and Full Stack developer with 6+ years of experience in Java/Kotlin, Java EE, Angular and Hybris…
I’m Passionate about Microservice architectures, Hexagonal architecture, Event Driven architecture, Event Sourcing and Domain Driven design (DDD)…
Huge fan of Clean Code school, SOLID, GRASP principles, Design Patterns, TDD and BDD.
most insightful tutorial on this topic! thanks
how to call deafault method our run method in commandline runner interface as rest controller in requestmapping
thx , just this method doesnt works on my eclipse , final JasperReport report = loadTemplate();
Comment ajouter le fichier. Jrxml dans sts, mon sts ne lut pas le fichier
test
The Render pdf (JasperReportsUtils) with Spring framework 5 doesn’t work more. How can I change ?
You are directly use JasperFillManager and JasperExportManager as shared below:
final JasperPrint jasperPrint = JasperFillManager.fillReport(report, parameters, new JREmptyDataSource());
JasperExportManager.exportReportToPdfFile(jasperPrint, pdfFile.getAbsolutePath());
How to load custom font in Jasper ?
You may use iReport or Jasper studio and install the fonts and generate their extensions and use them in your project.
I got this error
net.sf.jasperreports.engine.util.JRFontNotFoundException: Font “Arial Rounded MT Bold” is not available to the JVM. See the Javadoc for more details.
at net.sf.jasperreports.engine.fonts.FontUtil.checkAwtFont(FontUtil.java:604) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.SimpleTextLineWrapper.loadFont(SimpleTextLineWrapper.java:384) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.SimpleTextLineWrapper.getGeneralFontInfo(SimpleTextLineWrapper.java:354) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.SimpleTextLineWrapper.createFontInfo(SimpleTextLineWrapper.java:294) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.SimpleTextLineWrapper.start(SimpleTextLineWrapper.java:256) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.TextMeasurer.measure(TextMeasurer.java:543) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.JRFillTextElement.chopTextElement(JRFillTextElement.java:665) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.JRFillTextField.prepare(JRFillTextField.java:784) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.JRFillElementContainer.prepareElements(JRFillElementContainer.java:542) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.JRFillFrame.prepare(JRFillFrame.java:241) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.JRFillElementContainer.prepareElements(JRFillElementContainer.java:542) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.JRFillBand.fill(JRFillBand.java:453) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.JRFillBand.fill(JRFillBand.java:428) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.JRVerticalFiller.fillBandNoOverflow(JRVerticalFiller.java:448) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.JRVerticalFiller.fillColumnHeader(JRVerticalFiller.java:496) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.JRVerticalFiller.fillReportStart(JRVerticalFiller.java:260) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.JRVerticalFiller.fillReport(JRVerticalFiller.java:110) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.JRBaseFiller.fill(JRBaseFiller.java:615) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.BaseReportFiller.fill(BaseReportFiller.java:432) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.JRFillSubreport.fillSubreport(JRFillSubreport.java:818) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.JRSubreportRunnable.run(JRSubreportRunnable.java:61) ~[jasperreports-6.6.0.jar:6.6.0]
at net.sf.jasperreports.engine.fill.AbstractThreadSubreportRunner.run(AbstractThreadSubreportRunner.java:221) ~[jasperreports-6.6.0.jar:6.6.0]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_191]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_191]
Download and place the font in jre/lib/fonts folder to fix.
How to attach new Jasper report without modifying Spring Boot application to call new jasper repot. I mean to make the Report dynamic ,