In this post we’ll see how to generate a PDF in Spring MVC using the fields from a view page (JSP).
Technologies used
Following is the list of tools used for the Spring MVC PDF generation example.
- Spring 5.0.8 Release (Spring core, spring web, spring webmvc).
- Java 10
- Tomcat server V 9.0.10
- Eclipse Photon 4.8.0 for Java EE development (This Eclipse version supports Java 10)
- iText 7.1.3
- OpenPDF 1.2.3
Options for generating PDF in Spring MVC
- One of the option for generating PDF is iText. In Spring framework iText support is for a very old version so requires some work on your behalf to get it work with the current versions.
- Another option is OpenPDF which is a fork from iText. Spring framework class AbstractPdfView can directly be used to generate PDF using OpenPDF. Spring framework recommends OpenPDF.
Reference - https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/view/document/AbstractPdfView.html
Spring MVC PDF generation example using iText
First we’ll see how to generate a PDF using iText.
Maven dependenciesMaven is used for managing dependencies in this Spring MVC PDF generation example.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>spring-mvc</groupId>
<artifactId>spring-mvc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>Spring MVC</name>
<description>Spring MVC example</description>
<properties>
<spring.version>5.0.8.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
<!-- For JSTL tags -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- For iText -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>7.1.3</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>7.1.3</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<release>10</release>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.1</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
</configuration>
</plugin>
</plugins>
</build>
</project>
In the pom.xml apart from Spring framework dependencies, iText dependencies are also added which adds the following jars.
kernel-7.1.3.jar
io-7.1.3.jar
layout-7.1.3.jar
Spring MVC PDF generation example flow
In the example there is a JSP that shows a list of Users and there is a button “ViewPDF”. In the JSP that list of users is bound to a Model.
When the button is clicked, the request is mapped to the appropriate controller method and from there the logical view name and Model where the list of users is set as attribute is transferred to the view which creates a PDF. To resolve a view to a PDF another view resolver has to be added.
Spring MVC PDF generation – iText related classes
With in Spring framework there is an abstract class AbstractPdfView which acts as a superclass for PDF views. But the AbstractPdfView class works with the original iText 2.1.7 version (with com.lowagie.* package) where as the current version is iText 7.x (with com.itextpdf package).
That requires some work on your behalf to make the Spring MVC work with the current iText version. AbstractPdfView class extends AbstractView class and adds PDF specific functionality. So, you need to do that yourself, creating a class that extends AbstractView and uses the new com.itextpdf package. You can still copy most of the functionality from the AbstractPdfView class, difference is now you are pointing to the com.itextpdf.* classes where as AbstractPdfView class uses the older com.lowagie.* classes. Also code for PDF generation in iText 7.x is a little different from iText 5.x version.
import java.io.ByteArrayOutputStream;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.view.AbstractView;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
public abstract class NetJSAbstractViewPDF extends AbstractView {
public NetJSAbstractViewPDF() {
setContentType("application/pdf");
}
@Override
protected boolean generatesDownloadContent() {
return true;
}
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// IE workaround: write into byte array first.
ByteArrayOutputStream baos = createTemporaryOutputStream();
// Apply preferences and build metadata.
PdfWriter writer = new PdfWriter(baos);
Document document = new Document(new PdfDocument(writer), PageSize.A4);
buildPdfMetadata(model, document, request);
// Build PDF document.
buildPdfDocument(model, document, writer, request, response);
document.close();
// Flush to HTTP response.
writeToResponse(response, baos);
}
protected void buildPdfMetadata(Map<String, Object> model, Document document, HttpServletRequest request) {
}
protected abstract void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
Now NetJSAbstractViewPDF is the abstract class which provides the PDF specific functionality and you need to extend this class to implement abstract method buildPdfDocument() as per your PDF document structure requirements.
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.netjs.model.User;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.property.UnitValue;
public class PDFView extends NetJSAbstractViewPDF {
@Override
protected void buildPdfDocument(Map<String, Object> model, Document document,
PdfWriter writer, HttpServletRequest request, HttpServletResponse response) throws Exception {
// List of users that will be displayed in the PDF
List<User> users = (List<User>)model.get("Users");
// Create table with 3 columns of similar length
Table table = new Table(new float[]{4, 4, 4});
table.setWidth(UnitValue.createPercentValue(100));
PdfFont bold = PdfFontFactory.createFont("Times-Bold");
// adding header
table.addHeaderCell(new Cell().add(new Paragraph("First Name").setFont(bold)));
table.addHeaderCell(new Cell().add(new Paragraph("Last Name").setFont(bold)));
table.addHeaderCell(new Cell().add(new Paragraph("Email").setFont(bold)));
// adding rows
for(User user : users) {
table.addCell(user.getFirstName());
table.addCell(user.getLastName());
table.addCell(user.getEmail());
}
document.add(table);
}
}
Spring MVC PDF generation – Controller Class
import java.util.ArrayList;
import java.util.List;
import org.netjs.model.User;
import org.netjs.model.UserListContainer;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class UserController {
@RequestMapping(value = "/getUsers", method = RequestMethod.GET)
public String getUsers(Model model) throws Exception{
List<User> users = getListOfUsers();
UserListContainer userList = new UserListContainer();
userList.setUsers(users);
model.addAttribute("Users", userList);
return "showUsers";
}
@RequestMapping(value = "/viewPDF", method = RequestMethod.POST)
public ModelAndView viewPDF(@ModelAttribute("Users") UserListContainer userList) throws Exception{
List<User> users = userList.getUsers();
return new ModelAndView("viewPDF", "Users", users);
}
// Dummy method for adding List of Users
private List<User> getListOfUsers() {
List<User> users = new ArrayList<User>();
users.add(new User("Jack", "Reacher", "abc@xyz.com"));
users.add(new User("Remington", "Steele", "rs@cbd.com"));
users.add(new User("Jonathan", "Raven", "jr@sn.com"));
return users;
}
}
In the Controller class there are two handler methods. Method getUsers() displays the list of users in a JSP page (showUsers.jsp). In that JSP there is a “View PDF” button, the request created on clicking that button is handled by the handler method viewPDF() which passes the list of users and the logical view name to be resolved to a PDF view.
Spring MVC PDF generation – Views
showUsers.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Spring MVC List of objects display</title>
</head>
<body>
<form:form method="POST" action="viewPDF" modelAttribute="Users">
<table>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
<c:forEach items="${Users.users}" var="user" varStatus="tagStatus">
<tr>
<td><form:input path="users[${tagStatus.index}].firstName" value="${user.firstName}" readonly="true"/></td>
<td><form:input path="users[${tagStatus.index}].lastName" value="${user.lastName}" readonly="true"/></td>
<td><form:input path="users[${tagStatus.index}].email" value="${user.email}" readonly="true"/></td>
</tr>
</c:forEach>
</table>
<input type="submit" value="View PDF" />
</form:form>
</body>
</html>
This JSP displays the users in the List by iterating the List and again bind list to the Model. Clicking “View PDF” button generates the PDF using the List bound to the Model.
We have already seen the PDF view class PDFView.java.
Spring MVC PDF generation – Model Classes
List will have objects of the User class.
public class User {
private String firstName;
private String lastName;
private String email;
public User() {
}
public User(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Following class acts a container for the List of User objects.
public class UserListContainer {
private List<User> users;
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
Spring MVC PDF generation – Configuration
The Spring configuration file is as follows.
mvcexample-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven />
<context:component-scan base-package="org.netjs.controller" />
<bean id="PDFResolver" class=
"org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="order" value="1"/>
<property name="basename" value="pdf-view"/>
</bean>
<bean id="JSPViewResolver" class=
"org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="2"/>
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
As you can see two view resolver classes are configured here.
ResourceBundleViewResolver resolves the views from the properties file. ResourceBundleViewResolver is the Implementation of ViewResolver that uses bean definitions in a ResourceBundle, specified by the bundle base name, and for each view it is supposed to resolve, it uses the value of the property [viewname].(class) as the view class and the value of the property [viewname].url as the view url.
Here “basename” property has the value pdf-view which means properties file is named pdf-view.properties file.
pdf-view.properties
viewPDF.(class)=org.netjs.config.PDFView
Controller class handler method viewPDF returns logical view name as “viewPDF” which is used to resolve the view class using this properties file.
Another view resolver InternalResourceViewResolver is used to reolve the view name to JSPs.
Here note that both the Resolvers have a property order too which decides the priority. Lower order value means higher priority. Here ResourceBundleViewResolver has order set as 1 which means Spring framework will first try to resolve view using this class.
As a rule InternalResourceViewResolver should always have higher order value because it will always be resolved to view irrespetive of value returned giving no chance to any other Resolver class.
Spring MVC PDF generation using OpenPDF
If you are using OpenPDF to generate PDF in your Spring MVC application then you need to make following changes.
Maven dependency
For OpenPDF you need to add following dependency in your pom.xml
<dependency>
<groupId>com.github.librepdf</groupId>
<artifactId>openpdf</artifactId>
<version>1.2.3</version>
</dependency>
PDF view class
When using OpenPDF you can directly subclass AbstractPdfView as OpenPDF uses com.lowagie.* classes, no need to create your own Abstract class by extending AbstractView class.
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.netjs.model.User;
import org.springframework.web.servlet.view.document.AbstractPdfView;
import com.lowagie.text.Document;
import com.lowagie.text.Font;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
public class OpenPDFView extends AbstractPdfView{
@Override
protected void buildPdfDocument(Map<String, Object> model, Document document,
PdfWriter writer, HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("buildPdfDocument in OpenPDFView");
// List of users that will be displayed in the PDF
List<User> users = (List<User>)model.get("Users");
Font font = new Font(Font.HELVETICA, 18, Font.BOLDITALIC);
PdfPTable table = new PdfPTable(3);
table.setWidthPercentage(100.0f);
table.setWidths(new float[] {4.0f, 4.0f, 4.0f});
PdfPCell cell = new PdfPCell();
cell.setPhrase(new Phrase("First Name", font));
table.addCell(cell);
cell.setPhrase(new Phrase("Last Name", font));
table.addCell(cell);
cell.setPhrase(new Phrase("Email", font));
table.addCell(cell);
// adding rows
for(User user : users) {
table.addCell(user.getFirstName());
table.addCell(user.getLastName());
table.addCell(user.getEmail());
}
// adding table to document
document.add(table);
}
}
Properties class change
In properties class referred by ResourceBundleViewResolver, now the view class should be this new PDF view class.
pdf-view.properties
viewPDF.(class)=org.netjs.config.OpenPDFView
Deploying and testing the application
Once the application is deployed to Tomcat server it can be accessed using the URL- http://localhost:8080/spring-mvc/getUsers
Generated PDF
On clicking the View PDF button PDF is generated. Screen shot shows how it looks like in Chrome browser.
That's all for this topic Spring MVC PDF Generation Example. If you have any doubt or any suggestions to make please drop a comment. Thanks!
Related Topics
- Spring MVC File Download Example
- Spring MVC Redirect Example
- Spring MVC Exception Handling Example Using @ExceptionHandler And @ControllerAdvice
- Spring MVC Example With @PathVaribale - Creating Dynamic URL
- Spring Transaction Management JDBC Example Using @Transactional Annotation
You may also like -
>>>Go to Spring Tutorial Page