Reputation: 1477
I am using thymleaf html template. When I preview the page the styling looks good. When I download the pdf, I don't see any CSS styles applied. The pdf contains content only not the style which I have applied.
// download generation code
Pdf generation code link which I referred and used the same
// sample code
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"></meta>
<title>Profile Preview</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.min.css"></link>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/paper-css/0.4.1/paper.css"></link>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"></link>
<style>
@page { size: A4 }
table {
border-collapse: collapse;
width: 100%;
margin-bottom: 20px;
}
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 5px;
}
tr:nth-child(even) {
background-color: #dddddd
}
</style>
</head>
<body class="A4">
<div class ="preview">
<section class="sheet">
<div class="logo">
<img th:src="@{/images/logo.png}" />
</div>
<h4 style="font-size: 1em; font-weight: bold; line-height: 1em">
<p>Age: <span th:text="${profile.basicInfo.age}"></span></p>
<p>D.O.B: <span th:text="${profile.basicInfo.birthDate}"></span></p>
<p>Gender: <span th:text="${profile.basicInfo.gender.toString()}"></span></p>
<p>Education: <span th:text="${profile.professionalInfo.educationDetail}"></span></p>
</h4>
<table>
<tr>
<th colspan="4" style="text-align:center; background-color: #6c3c81; color:white; font-weight: bold">Partner Preference Information</th>
</tr>
</table>
</div>
</section>
</div>
<button class="button" onClick="window.print();this.style.display='none'">Print</button>
</body>
</html>
// server side code
GetMapping("/{id}/download")
public void download(@PathVariable("id") String id, HttpServletResponse response) {
try {
Path file = Paths.get(profilePdfService.generatePdf(id).getAbsolutePath());
if (Files.exists(file)) {
response.setContentType("application/pdf");
response.addHeader("Content-Disposition", "attachment; filename=" + file.getFileName());
Files.copy(file, response.getOutputStream());
response.getOutputStream().flush();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
Upvotes: 4
Views: 3422
Reputation: 53441
It seems you are using Flying Saucer for rendering in PDF format the generated Thymeleaf template HTML.
There can be several issues here.
First, you need to provide a proper XHTML document to Flying Source. For this task, you can use JTidy to convert the rendered Thymeleaf template to XHTML: it maybe does not work in a very complicated HTML, but it very likely will in your use case.
There are a lot of versions of JTidy. Sorry, in a previous version of the answer I provided you a reference to an outdated one I previously used, that maybe does not work for HTML5 as your need. Please, use the following dependency instead, based on a brand new JTidy project:
<dependency>
<groupId>com.github.jtidy</groupId>
<artifactId>jtidy</artifactId>
<version>1.0.2</version>
</dependency>
For example, in the source code of the PdfService
class you indicated in the question, modify the generatePdf
method as follows:
public File generatePdf() throws IOException, DocumentException {
Context context = getContext();
String html = loadAndFillTemplate(context);
String xhtml = convertToXhtml(html);
return renderPdf(xhtml);
}
Where convertToXhtml
can look like (is an updated version of the previous one adapted for the new JTidy library version):
// Necessary imports, among others
import org.w3c.dom.Document;
import org.w3c.tidy.Tidy;
//...
private String convertToXhtml(String html) throws UnsupportedEncodingException {
Tidy tidy = new Tidy();
tidy.setXHTML(true);
tidy.setIndentContent(true);
tidy.setPrintBodyOnly(true);
tidy.setInputEncoding("UTF-8");
tidy.setOutputEncoding("UTF-8");
tidy.setSmartIndent(true);
tidy.setShowWarnings(false);
tidy.setQuiet(true);
tidy.setTidyMark(false);
Document htmlDOM = tidy.parseDOM(new ByteArrayInputStream(html.getBytes()), null);
OutputStream out = new ByteArrayOutputStream();
tidy.pprint(htmlDOM, out);
return out.toString();
}
See how the page looks like after performing this process: you are applying a lot of inline styles and the PDF should look better.
Also, instead of using external CDN distributed stylesheets, use a local copy of them, accessible to your application as the resource identified as PDF_RESOURCES
in the source code of the PdfService
class.
As you can see in the implementation of the renderPdf
of that class, it is being used as the base URL to look for the different resources referenced in the page.
Finally, pay attention to your logo: perhaps you will need to provide some custom implementation of ReplacedElementFactory for that purpose. Please, consider read this or this other SO questions, I think they can be helpful.
Following these steps, with minor tweaks, you should be able to obtain something like the following PDF:
If Flying Saucer does not fulfill your requirements, please, take a look at any headless browser, for instance, PhantomJS, to perform the conversion.
Upvotes: 1