Spring Web MVC with PDF View Example (using iText 5.x)
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.
Subclassing AbstractView Class to Work with iText 5.x
Spring 3.x provides an AbstractPdfView abstract class which can be subclassed to create a helper class for generating PDF documents. However, it has a big drawback which the AbstractPdfView class 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 tocom.itextpdf.* (iText version >= 5.x)
The old iText version is no longer available nor supported, so subclassing AbstractPdfView class (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 AbstractPdfView class, except it uses the package com.itextpdf.*instead of the com.lowagie.*. If curious, you can look at source code of the AbstractPdfView class 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:
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:
Recommended Book: Spring in Practice
|
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:
Recommended Book: Pro Spring 3
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:
Thanks for sharing this informative content , Great work
ReplyDeleteLeanpitch provides online training in Product prototyping during this lockdown period everyone can use it wisely.
icp-cat training