Create PDF using Angular + Spring Boot + Thymeleaf

Chinthaka Jayatilake
LinkIT
Published in
4 min readOct 8, 2019
Photo by J-S Romeo on Unsplash

Angular and Spring Boot have become popular among developers because of the ease of programming and the efficiency that they have. Today we will be looking at how we could simply create a PDF using these two technologies which would be really helpful as this could be integrated into any project where a software requirement is there to create a PDF.

Here we will be having an Angular front-end from which we will be collecting data using a simple form and along with it a Spring Boot back-end to create the PDF.

Lets first start with the Angular project. Use the below command to create a new project.

ng new project_name

It is time to create the HTML form to collect data. So let us move to the app.component.html file.

<h1>Enter the following details</h1><table> <tr>
<td><span>Name</span></td>
<td><input type="text" [(ngModel)]="dataset.name" name="name"> </td>
</tr>
<tr>
<td><span>Age(Integer)</span></td>
<td><input type="number" [(ngModel)]="dataset.age" name="age"></td>
</tr>
<tr>
<td><span>Country</span></td>
<td><input type="text" [(ngModel)]="dataset.country" name="country"></td>
</tr>
<tr>
<td></td>
<td><button (click)="onSubmit()">Submit</button></td>
</tr>
</table>

Next we need to edit the app.component.ts file to send the collected data to the back-end. We will be using the HttpClient to communicate with the Spring Boot back-end.

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent { title = 'CreatePDF'; dataset: Details = {
name:'',
age:null,
country:''
};
constructor(private https: HttpClient) { } onSubmit()
{
this.https.post<Details>('http://localhost:8080/testapp/getdetails', this.dataset).subscribe(
res => {
this.dataset = res;
alert('PDF created successfully');
this.dataset.age = null;
this.dataset.name = '';
this.dataset.country = '';
});
}
}
interface Details
{
name:string;
age:number;
country:string;
}

It is important to make sure that all the imports which are mentioned at the beginning of the component.ts files are present in the project node modules folder. If not they must be installed as below.

npm install imported_file_name

Now let us move to the Spring Boot project. You can create a Spring Boot project easily by using the below link.

First we need to edit our pom.xml file in order to import the dependencies that we will be needing in the project.

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>

<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>

<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<version>2.1.2</version>
</dependency>

<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-itext5</artifactId>
<version>9.1.6</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>net.sf.jtidy</groupId>
<artifactId>jtidy</artifactId>
<version>r938</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-core</artifactId>
<version>9.1.6</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

</dependencies>

Second we need to create a class for us to take the data passed by the Angular front-end to the Spring Boot back-end.

public class Details {

private String name;

private int age;

private String country;

public String getName() {
return name;
}

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

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getCountry() {
return country;
}

public void setCountry(String country) {
this.country = country;
}
}

Now we need to create the controller class to get the data from the front-end, pass that data to the pdf-templates and also to initialize the pdf-templates.

import com.blog.example.CreatePDF.dto.Details;
import com.itextpdf.text.DocumentException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.w3c.tidy.Tidy;
import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.*;
import java.nio.file.FileSystems;

@RequestMapping("/testapp")
@Controller
public class PDFCreator {

@Autowired
SpringTemplateEngine templateEngine;

@RequestMapping("/getdetails")
public @ResponseBody Details savePDF(@RequestBody Details details) throws IOException, DocumentException {
Context context = new Context();

context.setVariable("name",details.getName());
context.setVariable("age",details.getAge());
context.setVariable("country",details.getCountry());

String htmlContentToRender = templateEngine.process("pdf-template", context);
String xHtml = xhtmlConvert(htmlContentToRender);

ITextRenderer renderer = new ITextRenderer();

String baseUrl = FileSystems
.getDefault()
.getPath("src", "main", "resources","templates")
.toUri()
.toURL()
.toString();
renderer.setDocumentFromString(xHtml, baseUrl);
renderer.layout();

OutputStream outputStream = new FileOutputStream("src//test.pdf");
renderer.createPDF(outputStream);
outputStream.close();

return details;

}

private String xhtmlConvert(String html) throws UnsupportedEncodingException {
Tidy tidy = new Tidy();
tidy.setInputEncoding("UTF-8");
tidy.setOutputEncoding("UTF-8");
tidy.setXHTML(true);
ByteArrayInputStream inputStream = new ByteArrayInputStream(html.getBytes("UTF-8"));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
tidy.parseDOM(inputStream, outputStream);
return outputStream.toString("UTF-8");
}
}

We will be using Thymeleaf to configure the pdf-templates. Let us now create the configuration class to configure the SpringTemplateEngine and the SpringResourceTemplateResolver.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.templatemode.StandardTemplateModeHandlers;

import java.nio.charset.StandardCharsets;

@Configuration
public class PDFThymeleafConfiguration {


@Bean
public SpringTemplateEngine springTemplateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(htmlTemplateResolver());
return templateEngine;
}

@Bean
public SpringResourceTemplateResolver htmlTemplateResolver() {
SpringResourceTemplateResolver pdfTemplateResolver = new SpringResourceTemplateResolver();
pdfTemplateResolver.setPrefix("classpath:/templates/");
pdfTemplateResolver.setSuffix(".html");
pdfTemplateResolver.setTemplateMode(StandardTemplateModeHandlers.HTML5.getTemplateModeName());
pdfTemplateResolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
return pdfTemplateResolver;
}
}

Next we need to design the PDF view using HTML syntax in the Spring Boot project.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/xhtml">

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Template</title>
</head>
<body>

<h1>This is just a sample html page</h1>

<h2>Below are the details that were passed to the html page</h2>

<p th:text="${'Name : ' + name}"></p>
<p th:text="${'Age :' + age}"></p>
<p th:text="${'Country : ' + country}"></p>

<p><b>You can create the page the way you wish to have your PDF file using HTML tags</b></p>

<p><b>Bootstrap can be added to design. Images can be added. All according to your preference</b></p>

</body>
</html>

With that we have reached the end of our implementation and now you can try to work with it. Not just that, feel free to use it in your software when required.

Hope this could help!! The project source code can be downloaded from GitHub.

Cheers!! Have a pleasant day!!

Visit my Official Blog Site to send in your queries directly to me and to read more articles by me….

--

--