Java servlet PDF tutorial

Java servlet PDF tutorial shows how to return PDF data from a Java servlet. We use iText library to work with PDF. The web application is deployed on Tomcat server.

PDF

The Portable Document Format (PDF) is a file format used to present documents in a manner independent of application software, hardware, and operating systems. Invented by Adobe, PDF is now an open standard maintained by the International Organization for Standardization (ISO).

Java Servlet

Servlet is a Java class which responds to a particular type of network request - most commonly an HTTP request. Java servlets are used to create web applications. They run in servlet containers such as Tomcat or Jetty. Modern-day Java web development uses frameworks that are built on top of servlets.

iText

iText is an open source library for creating and manipulating PDF files in Java.

Java servlet PDF application

The following web application uses a Java servlet to send a PDF file to the client. It generates PDF from a list of objects.

$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           ├── bean
    │   │           │   └── City.java
    │   │           ├── service
    │   │           │   └── CityService.java
    │   │           ├── util
    │   │           │   └── GeneratePdf.java
    │   │           └── web
    │   │               └── MyServlet.java
    │   └── webapp
    │       ├── index.html
    │       ├── META-INF
    │       │   └── context.xml
    │       └── WEB-INF
    └── test
        └── java

This is the project structure.

pom.xml
<?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.zetcode</groupId>
    <artifactId>JavaServletPDF</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>JavaServletPDF</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        
        <dependency>
            <groupId>com.lowagie</groupId>
            <artifactId>itext</artifactId>
            <version>4.2.2</version>
        </dependency>         
        
    </dependencies>

    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
 
        </plugins>
    </build>   

</project>

This is the Maven POM file. We have two artifacts: javax.servlet-api for servlets and itext for PDF generation in Java. The maven-war-plugin is responsible for collecting all artifact dependencies, classes and resources of the web application and packaging them into a web application archive (WAR).

context.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/JavaServletPdf"/>

In the Tomcat context.xml file, we define the context path. It is the name of the web application.

City.java
package com.zetcode.bean;

public class City {

    private Long id;
    private String name;
    private int population;

    public City() {
    }

    public City(Long id, String name, int population) {
        this.id = id;
        this.name = name;
        this.population = population;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPopulation() {
        return population;
    }

    public void setPopulation(int population) {
        this.population = population;
    }

    @Override
    public String toString() {
        return "City{" + "id=" + id + ", name=" + name + 
                ", population=" + population + '}';
    }
}

This is the City bean. It has three attributes: id, name, and population.

MyServlet.java
package com.zetcode.web;

import com.zetcode.bean.City;
import com.zetcode.service.CityService;
import com.zetcode.util.GeneratePdf;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(name = "MyServlet", urlPatterns = {"/MyServlet"})
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("application/pdf;charset=UTF-8");

        response.addHeader("Content-Disposition", "inline; filename=" + "cities.pdf");
        ServletOutputStream out = response.getOutputStream();

        List<City> cities = CityService.getCities();

        ByteArrayOutputStream baos = GeneratePdf.getPdfFile(cities);
        baos.writeTo(out);
    }
}

This is the MyServlet servlet. It retrieves data from a service class, generates PDF file from the data, and returns the PDF file to the client.

response.setContentType("application/pdf;charset=UTF-8");

We set the content type of the response object to application/pdf.

response.addHeader("Content-Disposition", "inline; filename=" + "cities.pdf");

The Content-Disposition response header indicates that the content is expected to be displayed inline in the browser, that is, as a Web page or as part of a Web page, or as an attachment, that is downloaded and saved locally. The optional filename directive specifies the name of the file transmitted.

ServletOutputStream out = response.getOutputStream();

We get the ServletOutputStream from the response object.

List<City> cities = CityService.getCities();

From the CityService, we get the list of cities.

List<City> cities = CityService.getCities();

From the CityService, we get the list of cities.

ByteArrayOutputStream baos = GeneratePdf.getPdfFile(cities);
baos.writeTo(out);

We generate a PDF file from the data and write the returned ByteArrayOutputStream to the ServletOutputStream.

CityService.java
package com.zetcode.service;

import com.zetcode.bean.City;
import java.util.ArrayList;
import java.util.List;

public class CityService {

    public static List<City> getCities() {

        List<City> cities = new ArrayList<>();
        
        cities.add(new City(1L, "Bratislava", 432000));
        cities.add(new City(2L, "Budapest", 1759000));
        cities.add(new City(3L, "Prague", 1280000));
        cities.add(new City(4L, "Warsaw", 1748000));
        cities.add(new City(5L, "Los Angeles", 3971000));
        cities.add(new City(6L, "New York", 8550000));
        cities.add(new City(7L, "Edinburgh", 464000));
        cities.add(new City(8L, "Berlin", 3671000));
        
        return cities;
    }
}

The CityService's getCities() method returns a list of city objects.

GeneratePdf.java
package com.zetcode.util;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontFactory;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.zetcode.bean.City;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class GeneratePdf {

    public static ByteArrayOutputStream getPdfFile(List<City> cities) {

        Document document = new Document();
        ByteArrayOutputStream bout = new ByteArrayOutputStream();

        try {

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

            Font headFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD);

            PdfPCell hcell;
            hcell = new PdfPCell(new Phrase("Id", headFont));
            hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(hcell);

            hcell = new PdfPCell(new Phrase("Name", headFont));
            hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(hcell);

            hcell = new PdfPCell(new Phrase("Population", headFont));
            hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(hcell);

            for (City city : cities) {

                PdfPCell cell;

                cell = new PdfPCell(new Phrase(city.getId().toString()));
                cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                cell.setHorizontalAlignment(Element.ALIGN_CENTER);
                table.addCell(cell);

                cell = new PdfPCell(new Phrase(city.getName()));
                cell.setPaddingLeft(5);
                cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                cell.setHorizontalAlignment(Element.ALIGN_LEFT);
                table.addCell(cell);

                cell = new PdfPCell(new Phrase(String.valueOf(city.getPopulation())));
                cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
                cell.setPaddingRight(5);
                table.addCell(cell);
            }

            PdfWriter.getInstance(document, bout);
            document.open();
            document.add(table);
            
            document.close();
            
        } catch (DocumentException ex) {
        
            Logger.getLogger(GeneratePdf.class.getName()).log(Level.SEVERE, null, ex);
        }

        return bout; 
    }
}

GeneratePdf creates PDF file from the provided data.

ByteArrayOutputStream bout = new ByteArrayOutputStream();

The data will be written to ByteArrayOutputStream. ByteArrayOutputStream implements an output stream in which the data is written into a byte array.

PdfPTable table = new PdfPTable(3);

We will put our data in a table; for this, we have the PdfPTable class. The table has three columns: Id, Name, and Population.

Font headFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD);

We use bold Helvetica font for the table header.

PdfPCell hcell;
hcell = new PdfPCell(new Phrase("Id", headFont));
hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(hcell);

The data is placed inside table cells, represented by PdfPCell. The setHorizontalAlignment() method horizontally alignes the text.

PdfWriter.getInstance(document, bout);

With PdfWriter, the document is written to the ByteArrayOutputStream.

document.open();
document.add(table);

The table is inserted into the PDF document.

document.close();

In order for the data to be written to the ByteArrayOutputStream, the document must be closed.

return bout; 

In the end, the data is returned as ByteArrayOutputStream.

In this tutorial, we have sent PDF data from a Java servlet.

You might also be interested in the following related tutorials: Java RequestDispatcher, Serving plain text from Java servlet, Java servlet check box tutorial, Java servlet image tutorial, Java Servlet HTTP headers, or Java tutorial