Spring Web MVC with PDF View Example (using iText 5.x)
- Details
- Written by Nam Ha Minh
- Last Updated on 04 September 2020   |   Print Email
This tutorial is going to show you how to leverage Spring MVC’s view technology to build a sample application that generates a PDF document dynamically in order to be downloaded/opened by the user. For PDF generation, we will use the popular, open source PDF library called iText.
NOTE: for Spring Boot, I recommend you to follow this article: Spring Boot Export Data to PDF Example
The application will provide a download link as follows:
Clicking on the “Download PDF Document” link will cause the browser to download or open the generated PDF document. The following screenshot shows Chrome browser opens the document using its built-in PDF viewer:
About iText library
The iText PDF library was originally written by Bruno Lowagie, then later is developed and managed by iText Software Corp. The project is hosted on SourceForge.net and can be downloaded from the following link:
As of this writing, the latest version of iText is 5.4.2. So after extracting the distribution archive, put the itextpdf-5.4.2.jar file to the project’s classpath.
1. Subclassing AbstractView Class to Work with iText 5.x
Spring 3.x provides an AbstractPdfViewabstract class which can be subclassed to create a helper class for generating PDF documents. However, it has a big drawback which the AbstractPdfViewclass only supports old API version of iText i.e. it is using the package com.lowagie.* (iText version <= 2.1.7) while the recent iText’s package changes to com.itextpdf.* (iText version >= 5.x)
The old iText version is no longer available nor supported, so subclassing AbstractPdfViewclass (as of Spring 3.x) is discouraged. Instead, we recommend to subclass the AbstractView class to create an iText 5.x-compatible version. Here is code of the subclass (AbstractITextPdfView.java):
package net.codejava.spring; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.view.AbstractView; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.PageSize; import com.itextpdf.text.pdf.PdfWriter; /** * This class is a work around for working with iText 5.x in Spring. * The code here is almost identical to the AbstractPdfView class. * */ public abstract class AbstractITextPdfView extends AbstractView { public AbstractITextPdfView() { 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. Document document = newDocument(); PdfWriter writer = newWriter(document, baos); prepareWriter(model, writer, request); buildPdfMetadata(model, document, request); // Build PDF document. document.open(); buildPdfDocument(model, document, writer, request, response); document.close(); // Flush to HTTP response. writeToResponse(response, baos); } protected Document newDocument() { return new Document(PageSize.A4); } protected PdfWriter newWriter(Document document, OutputStream os) throws DocumentException { return PdfWriter.getInstance(document, os); } protected void prepareWriter(Map<String, Object> model, PdfWriter writer, HttpServletRequest request) throws DocumentException { writer.setViewerPreferences(getViewerPreferences()); } protected int getViewerPreferences() { return PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage; } 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; }
Note that code of this class is almost identical to the AbstractPdfViewclass, except it uses the package com.itextpdf.* instead of the com.lowagie.*. If curious, you can look at source code of the AbstractPdfViewclass which can be found inside the spring-webmvc-VERSION-sources.jar file.
2. Coding Model Class
Create a model class (Book.java) as follows:
package net.codejava.spring; public class Book { private String title; private String author; private String isbn; private String publishedDate; private float price; public Book(String title, String author, String isbn, String publishedDate, float price) { this.title = title; this.author = author; this.isbn = isbn; this.publishedDate = publishedDate; this.price = price; } // getters and setters }
The application will generate a PDF document that contains a list of books, thus this model class is needed.
3. Coding Entry JSP Page
Create home.jsp file under WEB-INF\jsp directory (you have to create the jsp directory first) with the following HTML code:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Spring MVC PDF View Demo (iText)</title> </head> <body> <div align="center"> <h1>Spring MVC PDF View Demo (using iText library)</h1> <h3><a href="/downloadPDF">Download PDF Document</a></h3> </div> </body> </html>
As you see, this page simply displays a hyper link “Download PDF Document” that points to a relative URL which will be handled by a Spring controller class described below.
4. Coding Spring Controller Class
Create MainController class that acts as the Spring controller class as follows:
package net.codejava.spring; import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; /** * A Spring controller that allows the users to download a PDF document * generated by the iText library. * * @author www.codejava.net * */ @Controller public class MainController { /** * Handle request to the default page */ @RequestMapping(value = "/", method = RequestMethod.GET) public String viewHome() { return "home"; } /** * Handle request to download a PDF document */ @RequestMapping(value = "/downloadPDF", method = RequestMethod.GET) public ModelAndView downloadExcel() { // create some sample data List<Book> listBooks = new ArrayList<Book>(); listBooks.add(new Book("Spring in Action", "Craig Walls", "1935182358", "June 29th 2011", 31.98F)); listBooks.add(new Book("Spring in Practice", "Willie Wheeler, Joshua White", "1935182056", "May 16th 2013", 31.95F)); listBooks.add(new Book("Pro Spring 3", "Clarence Ho, Rob Harrop", "1430241071", "April 18th 2012", 31.85F)); listBooks.add(new Book("Spring Integration in Action", "Mark Fisher", "1935182439", "September 26th 2012", 28.73F)); // return a view which will be resolved by an excel view resolver return new ModelAndView("pdfView", "listBooks", listBooks); } }
This controller handles two URLs by two methods:
- viewHome(): handles the URL request “/” which will redirect the user to the home.jsp page when the view name “home” is resolved by Spring’s InternalResourceViewResolver.
- downloadPDF(): handles the URL request “/downloadPDF” which will be sent to the server when the user clicks on the hyperlink in the home.jsp page. This method creates some dummy data, e.g. a list of Spring framework books which will be passed to the model associates with the view name “pdfView”.
5. Coding PDF View Class
Create PDFBuilder class that is a subclass of the AbstractITextPdfView class which we have created previously, with the following code:
package net.codejava.spring; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.itextpdf.text.BaseColor; import com.itextpdf.text.Document; import com.itextpdf.text.Font; import com.itextpdf.text.FontFactory; import com.itextpdf.text.Paragraph; import com.itextpdf.text.Phrase; import com.itextpdf.text.pdf.PdfPCell; import com.itextpdf.text.pdf.PdfPTable; import com.itextpdf.text.pdf.PdfWriter; /** * This view class generates a PDF document 'on the fly' based on the data * contained in the model. * @author www.codejava.net * */ public class PDFBuilder extends AbstractITextPdfView { @Override protected void buildPdfDocument(Map<String, Object> model, Document doc, PdfWriter writer, HttpServletRequest request, HttpServletResponse response) throws Exception { // get data model which is passed by the Spring container List<Book> listBooks = (List<Book>) model.get("listBooks"); doc.add(new Paragraph("Recommended books for Spring framework")); PdfPTable table = new PdfPTable(5); table.setWidthPercentage(100.0f); table.setWidths(new float[] {3.0f, 2.0f, 2.0f, 2.0f, 1.0f}); table.setSpacingBefore(10); // define font for table header row Font font = FontFactory.getFont(FontFactory.HELVETICA); font.setColor(BaseColor.WHITE); // define table header cell PdfPCell cell = new PdfPCell(); cell.setBackgroundColor(BaseColor.BLUE); cell.setPadding(5); // write table header cell.setPhrase(new Phrase("Book Title", font)); table.addCell(cell); cell.setPhrase(new Phrase("Author", font)); table.addCell(cell); cell.setPhrase(new Phrase("ISBN", font)); table.addCell(cell); cell.setPhrase(new Phrase("Published Date", font)); table.addCell(cell); cell.setPhrase(new Phrase("Price", font)); table.addCell(cell); // write table row data for (Book aBook : listBooks) { table.addCell(aBook.getTitle()); table.addCell(aBook.getAuthor()); table.addCell(aBook.getIsbn()); table.addCell(aBook.getPublishedDate()); table.addCell(String.valueOf(aBook.getPrice())); } doc.add(table); } }
The method buildPdfDocument() uses the iText API to generate a simple PDF document that contains a list of books in tabular format. We will configure Spring to pick up this view class as described below.
6. Configuring PDF View Class
Create views.properties file under the project’s classpath (which is under src directory in the Eclipse project), with the following line:
pdfView.(class)=net.codejava.spring.PDFBuilder
That tells the Spring’s view resolver to pick the net.codejava.spring.PDFBuilder class to render response for the view name “pdfView”.
7. Writing Spring Configuration File
Configure Spring MVC and view resolvers in spring-mvc.xml file under WEB-INF directory as follows:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="net.codejava.spring" /> <bean id="viewResolver1" class="org.springframework.web.servlet.view.ResourceBundleViewResolver"> <property name="order" value="1"/> <property name="basename" value="views"/> </bean> <bean id="viewResolver2" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="order" value="2"/> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> </beans>
Here we configure two view resolvers:
- ResourceBundleViewResolver: to resolve view names specified in the views.properties file.
- InternalResourceViewResolver: to resolve view names to JSP pages.
Note that the ResourceBundleViewResolver has higher priority (order=”1”) than the InternalResourceViewResolver (order=”2”) so the view names specified in the views.properties are processed first.
Finally we would have the following project structure in Eclipse IDE:
8. Testing the Application
To test this sample application, deploy the project on Tomcat server under a context named SpringMvcPdfViewDemo. Type the following URL in browser:
http://localhost:8080/SpringMvcPdfViewDemo
The default page (home.jsp) gets displayed as follows:
Click on the “Download PDF Document” hyperlink, depending on the browser type and setup, it will opens the document inside the browser or ask for downloading. Here the document is opened by Chrome’s built-in PDF viewer:
NOTE: this article may be out of date with Spring technologies. I recommend you to follow this latest one: Spring Boot Export Data to PDF Example
Related Spring View Tutorials:
- Spring MVC URL-based View Resolution with UrlFilenameViewController Example
- Parameterize View Name with ParameterizableViewController in Spring MVC
- Spring MVC XstlView and XsltViewResolver Example
- Spring MVC with Excel View Example
Other Spring Tutorials:
- Understand the core of Spring framework
- Understand Spring MVC
- Understand Spring AOP
- Spring MVC beginner tutorial with Spring Tool Suite IDE
- Spring MVC Form Handling Tutorial
- Spring MVC Form Validation Tutorial
- 14 Tips for Writing Spring MVC Controller
- Spring Web MVC Security Basic Example (XML Configuration)
- Understand Spring Data JPA with Simple Example
Comments
First thanks for developing knowledge.
Above program compiles and runs
Output is not showing in d browser
For debug control goes to pdfbuilder class as well as parent class finally browser not showing open or save dialogue
--------------------------------------------------------------------------------