Spring MVC PDF View Example

This tutorial demonstrates how to create a PDF view using Spring MVC and itext. Out of the box spring only supports the older com.lowagie:itext library. The newest com.itextpdf:itext library is not supported by default. But there is a work around, which we’ll show you in the following example.

Maven Dependencies

We manage our application using Apache Maven. Add the following dependencies to your pom.xml and maven will automatically manage and resolve the dependencies. Note: we added two dependencies for creating PDF documents. The first com.lowagie:itext is the older open source library, which is by default supported by Spring. The second library com.itextpdf:itext is not supported by default and you’ll need a licence to run this in production.

<?xml version="1.0" encoding="UTF-8"?>
<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>com.memorynotfound.spring.mvc</groupId>
    <artifactId>pdf-content-negotiation</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>SPRING-MVC - ${project.artifactId}</name>
    <url>http://memorynotfound.com</url>
    <packaging>war</packaging>

    <properties>
        <encoding>UTF-8</encoding>
        <spring.version>4.2.6.RELEASE</spring.version>
    </properties>

    <dependencies>
        <!-- spring libraries -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- Needed for Lowagie PDF View -->
        <dependency>
            <groupId>com.lowagie</groupId>
            <artifactId>itext</artifactId>
            <version>2.1.7</version>
        </dependency>

        <!-- Needed for IText PDF View -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.9</version>
        </dependency>

        <!-- servlet api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Spring MVC PDF View Configuration

The ServletInitializer is a replacement for the web.xml servlet descriptor. This class extends from the AbstractAnnotationConfigDispatcherServletInitializer. By overriding the getServletConfigClasses() we instruct where the spring configuration files are located.

package com.memorynotfound.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class ServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { WebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }
}

Next, we show how to configure spring using Java Configuration. Annotate the class with @Configuration to mark this class as a configuration file. We can override the configureContentNegotiation(..) method and use the ContentNegotiationConfigurer to configure the content negotiation preferences.

The configureViewResolvers(..) method is where we register the views for our application. You can either enable the ItextPdfView() or the LowagiePdfView which we’ll create later on in this tutorial.

package com.memorynotfound.config;

import com.memorynotfound.view.ItextPdfView;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.config.annotation.*;

@EnableWebMvc
@Configuration
@ComponentScan("com.memorynotfound")
public class WebConfig extends WebMvcConfigurerAdapter{

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer
                .defaultContentType(MediaType.TEXT_HTML)
                .parameterName("type")
                .favorParameter(true)
                .ignoreUnknownPathExtensions(false)
                .ignoreAcceptHeader(false)
                .useJaf(true);
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/", ".jsp");
        registry.enableContentNegotiation(
                new ItextPdfView()
                // Use either ItextPdfView or LowagiePdfView
                // new LowagiePdfView()
        );
    }
}

The equivalent Spring MVC XML Configuration is located below:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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="com.memorynotfound" />

    <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
        <property name="defaultContentType" value="TEXT_HTML"/>
        <property name="parameterName" value="type"/>
        <property name="favorParameter" value="true"/>
        <property name="ignoreUnknownPathExtensions" value="false"/>
        <property name="ignoreAcceptHeader" value="false"/>
        <property name="useJaf" value="true"/>
    </bean>

    <mvc:view-resolvers>
        <mvc:content-negotiation>
            <mvc:default-views>
                <bean class="com.memorynotfound.view.ItextPdfView"/>
            </mvc:default-views>
        </mvc:content-negotiation>
        <mvc:jsp prefix="/WEB-INF/views/" suffix=".jsp"/>
    </mvc:view-resolvers>

</beans>

Creating the controller

The CourseController adds some data to the Model which we’ll display on the views.

package com.memorynotfound.controller;

import com.memorynotfound.model.Course;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

@Controller
public class CourseController {

    List<Course> documents = Arrays.asList(
            new Course(1, "Spring MVC PDF IText Example", new Date()),
            new Course(3, "Spring MVC PDF Lowagie Example", new Date())
    );

    @RequestMapping(method = RequestMethod.GET)
    public String getDocuments(Model model) {
        model.addAttribute("courses", documents);
        return "index";

    }
}
package com.memorynotfound.model;

import java.util.Date;

public class Course {

    private Integer id;
    private String name;
    private Date date;

    public Course() {
    }

    public Course(Integer id, String name, Date date) {
        this.id = id;
        this.name = name;
        this.date = date;
    }

    public Integer getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public Date getDate() {
        return date;
    }
}

Spring MVC PDF Lowagie View

The older com.lowagie:itext library is supported by default. Create a new class and extend from the AbstractPdfView. We can create the PDF Document using the buildPdfDocument(..) method.

package com.memorynotfound.view;

import com.lowagie.text.Document;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
import com.memorynotfound.model.Course;
import org.springframework.web.servlet.view.document.AbstractPdfView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.DateFormat;
import java.util.List;
import java.util.Map;

public class LowagiePdfView extends AbstractPdfView {

    private static final DateFormat DATE_FORMAT = DateFormat.getDateInstance(DateFormat.SHORT);

    @Override
    protected void buildPdfDocument(Map<String, Object> model,
                                    Document document, PdfWriter writer,
                                    HttpServletRequest request,
                                    HttpServletResponse response) throws Exception {

        @SuppressWarnings("unchecked")
        List<Course> courses = (List<Course>) model.get("courses");

        PdfPTable table = new PdfPTable(3);

        table.addCell("ID");
        table.addCell("Name");
        table.addCell("Date");

        for (Course course : courses){
            table.addCell(String.valueOf(course.getId()));
            table.addCell(course.getName());
            table.addCell(DATE_FORMAT.format(course.getDate()));
        }

        document.add(table);
    }
}

Spring MVC PDF IText Example

The new com.itextpdf:itextpdf library is not supported by default. We can resolve this by first creating an abstract class and extending from the AbstractView. In this class we set the correct content type for our document. Next, we create an abstract method that’ll be used for creating the PDF document. Finally, we override and implement the renderMergedOutputModel(..) method that’ll write the pdf document to the response.

package com.memorynotfound.view;

import com.itextpdf.text.Document;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfWriter;
import org.springframework.web.servlet.view.AbstractView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.util.Map;

public abstract class AbstractITextPdfView extends AbstractView {

    public AbstractITextPdfView() {
        setContentType("application/pdf");
    }

    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;

    @Override
    protected void renderMergedOutputModel(Map<String, Object> model,
                                           HttpServletRequest request,
                                           HttpServletResponse response) throws Exception {

        ByteArrayOutputStream out = createTemporaryOutputStream();
        Document document = new Document(PageSize.A4);
        PdfWriter writer = PdfWriter.getInstance(document, out);
        writer.setViewerPreferences(PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage);
        buildPdfMetadata(model, document, request);

        document.open();
        buildPdfDocument(model, document, writer, request, response);
        document.close();
        writeToResponse(response, out);
    }

    @Override
    protected boolean generatesDownloadContent() {
        return true;
    }
}

Create a new class and extend from the previously created AbstractItextPdfView and override the buildPdfDocument(..) method to create your PDF document.

package com.memorynotfound.view;

import java.text.DateFormat;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.itextpdf.text.Document;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.memorynotfound.model.Course;

public class ItextPdfView extends AbstractITextPdfView {

    private static final DateFormat DATE_FORMAT = DateFormat.getDateInstance(DateFormat.SHORT);

    @Override
    protected void buildPdfDocument(Map<String, Object> model,
                                    Document document, PdfWriter writer, HttpServletRequest request,
                                    HttpServletResponse response) throws Exception {

        @SuppressWarnings("unchecked")
        List<Course> courses = (List<Course>) model.get("courses");

        PdfPTable table = new PdfPTable(3);
        table.setWidths(new int[]{10, 60, 30});

        table.addCell("ID");
        table.addCell("Name");
        table.addCell("Date");

        for (Course course : courses){
            table.addCell(String.valueOf(course.getId()));
            table.addCell(course.getName());
            table.addCell(DATE_FORMAT.format(course.getDate()));
        }



        document.add(table);
    }

}

HTML View

To demonstrate the content negotiation we also created a simple HTML view to display the courses attributes which we added to the Model.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"  pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<html>
<head>
    <title>Spring MVC PDF View Example</title>
</head>
<body>

<table>
    <tr>
        <th>ID</th>
        <th>Name</th>
        <th>Date</th>
    </tr>
    <c:forEach var="c" items="${courses}">
        <tr>
            <td>${c.id}</td>
            <td>${c.name}</td>
            <td><fmt:formatDate value="${c.date}" pattern="MM/dd/yy"/></td>
        </tr>
    </c:forEach>
</table>

</body>
</html>

Demo

URL: http://localhost:8081/spring-mvc-pdf/

spring mvc pdf view html example

URL: http://localhost:8081/spring-mvc-pdf/index.pdf

spring mvc pdf view example

References

Download

You may also like...