Thymeleaf and flying saucer ignoring the css when converting to the pdf

156 views Asked by At

I am trying to generate pdf using html, using thymeleaf and flying saucer library. However the CSS is not being applied when pdf is generating.

Here is my 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>dev.simplesolution</groupId>
<artifactId>spring-boot-pdf</artifactId>
<version>1.0.0</version>
<name>spring-boot-pdf</name>
<description>Spring Boot Generate PDF File</description>
<properties>
    <java.version>1.8</java.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

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

    <dependency>
        <groupId>org.xhtmlrenderer</groupId>
        <artifactId>flying-saucer-pdf</artifactId>
        <version>9.1.22</version>
    </dependency>

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

</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Service class

@Service
public class PdfGeneratorServiceImpl {
    private Logger logger = LoggerFactory.getLogger(PdfGeneratorServiceImpl.class);

    @Autowired
    private TemplateEngine templateEngine;

    
    public ByteArrayInputStream generatePdfFile(String templateName, Map<String, Object> data) {
        Context context = new Context();
        context.setVariables(data);
        String htmlContent = templateEngine.process(templateName, context);
        System.out.println(htmlContent);
        ByteArrayInputStream byteArrayInputStream = null;
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ITextRenderer renderer = new ITextRenderer();
            renderer.setDocumentFromString(htmlContent);
            renderer.layout();
            renderer.createPDF(byteArrayOutputStream, false);
            renderer.finishPDF();
            byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        } catch (DocumentException e) {
            logger.error(e.getMessage(), e);
        }

        return byteArrayInputStream;
    }
    
}

HTML template

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 20px;
            background-color: white;
            margin-left: 150px;
        }

        .section {
            background-color: white;
            padding: 20px;
            margin-bottom: 20px;
        }

        .details-grid {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 20px;
        }

        .detail {
            padding: 10px 0;
        }

        .label {
            font-weight: bold;
            display: block;
        }

        .value {
            margin-top: 5px;
        }

        h2 {
            font-size: 18px;
            color: #333;
            padding-bottom: 10px;
            margin-top: 0;
        }

        @media screen and (max-width: 600px) {
            .details-grid {
                grid-template-columns: 1fr;
                gap: 10px;
            }
        }

        .table {
            border-collapse: collapse;
            width: 100%;
        }

        th,
        td {
            padding: 15px 4px 0px 50px;
            border-bottom: 1px solid gray;
        }

        tr,
        td:first-child {
            border-right: 1px solid gray;
        }

        .ratingComponent {
            margin: 20px;
            text-align: center;
        }

        .ratingComponent__heading {
            display: block;
            color: #666;
            margin-bottom: 5px;
        }

        .ratingComponent__rating {
            display: flex;
            flex-direction: row-reverse;
            align-items: center;
            align-content: center;
            justify-content: space-between;
        }

        .ratingComponent__dot {
            border: solid 2px #eb822f;
            width: 16px;
            height: 16px;
            border-radius: 50%;
            margin: 0 5px 0 0;
        }

        .ratingComponent__dot:first-of-type {
            margin: 0;
        }

        .ratingComponent__dot--filled {
            background-color: #eb822f;
        }

        .ratingComponent__dot--halfFilled {
            background-color: #eb822f;
            width: 7px;
            border-radius: 10px 0 0 10px;
            margin-right: 15px;
        }

        .ratingComponent__dot--halfFilled:after {
            content: "";
            display: block;
            position: relative;
            left: 7px;
            top: -2px;
            width: 8px;
            height: 16px;
            border-radius: 0 10px 10px 0;
            border: solid 2px #eb822f;
        }

        .ratingComponent__dot--unRated {
            background-color: transparent;
            border-color: #e0e0e0;
            cursor: pointer;
            transition: all 200ms ease-in-out;
        }

        .ratingComponent__dot--unRated:hover,
        .ratingComponent__dot--unRated:hover~.ratingComponent__dot,
        .ratingComponent__dot--unRated:focus,
        .ratingComponent__dot--unRated:focus~.ratingComponent__dot {
            border-color: #eb822f;
            background-color: #eb822f;
        }
    </style>
</head>

<body>
<div style="text-align: center;">
    <div class="image">
        <img src="assets/images/brands_logo/logo_150.png" alt="img" width="150" />
    </div>
</div>
<div class="section">
    <h2>Customer details</h2>
    <div class="details-grid">
        <div class="detail">
            <span class="label">First name</span>
            <span class="value" th:text="${customerDetails.firstName}"></span>
        </div>
        <div class="detail">
            <span class="label">Last name</span>
            <span class="value" th:text="${customerDetails.lastName}"></span>
        </div>
        <div class="detail">
            <span class="label">Phone</span>
            <span class="value" th:text="${customerDetails.cellphone}"></span>
        </div>
        <div class="detail">
            <span class="label">Email</span>
            <span class="value" th:text="${customerDetails.emailaddress}"></span>
        </div>
        <div class="detail">
            <span class="label">Regione</span>
            <span class="value" th:text="${customerDetails.region}"></span>
        </div>
        <div class="detail">
            <span class="label">Market</span>
            <span class="value" th:text="${customerDetails.market}"></span>
        </div>
        <div class="detail">
            <span class="label">Importer</span>
            <span class="value" th:text="${customerDetails.importer}"></span>
        </div>
        <div class="detail">
            <span class="label">Agent</span>
            <span class="value" th:text="${customerDetails.agent}"></span>
        </div>
        <div class="detail">
            <span class="label">Dealer</span>
            <span class="value" th:text="${customerDetails.dealer}"></span>
        </div>
    </div>
</div>
<div class="section">
    <h2>Vehicle details</h2>
    <div class="details-grid">
        <div class="detail">
            <span class="label">Brand</span>
            <span class="value" th:text="${customerDetails.brand}"></span>
        </div>
        <div class="detail">
            <span class="label">VIN</span>
            <span class="value" th:text="${customerDetails.vin}"></span>
        </div>
        <div class="detail">
            <span class="label">Model</span>
            <span class="value"></span>
        </div>
    </div>
</div>
<div class="section">
    <h2>Survey details</h2>
    <div class="details-grid">
        <div class="detail">
            <span class="label">Response channel</span>
            <span class="value" th:text="${customerDetails.responseChannel}"></span>
        </div>
        <div class="detail">
            <span class="label">Response date</span>
            <span class="value" th:text="${customerDetails.responseDate}"></span>
        </div>
        <div class="detail">
            <span class="label">Event date</span>
            <span class="value" th:text="${customerDetails.eventDate}"></span>
        </div>
    </div>
</div>
<div class="section">
    <h4 style="block-size: auto;">Domande / Risposte</h4>
</div>
<div>
    <table class="table">
        <tr th:each="qa, qaStat: ${qaList}">
            <td th:text="${qa.question}"></td>
            <td><br />
                <br />
                <div class="ratingComponent">
                    <!-- <small class="ratingComponent__heading"></small> -->
                    <div class="ratingComponent__rating" th:utext="${qa.answer}">
                        <!--                        <div th:utext="${qa.answer}"></div>-->
                    </div>
                </div>
            </td>
        </tr>
    </table>
</div>
</body>

</html>

Thymeleaf is generating correct HTML and it is rendering as expected in the browser, however, the generated pdf is completely ignoring the CSS.

Thymeleaf generated HTML: https://pastebin.com/QJKyPzqw

0

There are 0 answers