javaspring-bootthymeleafflying-saucer

White border around my background when generating a PDF certificate from an HTML template with FlyingSaucer


I need to generate PDF certificates after completing courses. These pdf's generating by HTML template with Thymeleaf. Everything is OK through converting, but in result appear white border around the background image. Resolution of Background image is 1920x1440. There is my code on Spring Boot 3.1.0:

public String parseThymeleafTemplate(String verificationCode, Long certificateId) {
        ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
        templateResolver.setSuffix(".html");
        templateResolver.setTemplateMode(TemplateMode.HTML);

        TemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);

        User student = User.builder()
                .surname("Test_surname").name("Test_name").build();
        User mentor = User.builder()
                .surname("Test_mentor").name("Test_mentor").position("Java dev").build();
        Course course = Course.builder()
                .mentor(mentor)
                .name("Java Developer for 3 weeks").build();
        }

        Certificate certificate = Certificate.builder()
                .id(54L)
                .course(course)
                .student(student)
                .verificationCode(verificationCode)
                .issuedDate(LocalDate.now())
                .build();
        Context context = new Context();
        context.setVariable("company_name", "test_company");
        context.setVariable("student_name", certificate.getStudent().getSurname() + " " + certificate.getStudent().getName());
        context.setVariable("course_name", certificate.getCourse().getName());
        context.setVariable("mentor_name", certificate.getCourse().getMentor().getSurname() + " " + certificate.getCourse().getMentor().getName());
        context.setVariable("issued_date", certificate.getIssuedDate());
        context.setVariable("verification_code", certificate.getVerificationCode());
        String generatedFileName = this.generatePdfFromHtml(templateEngine.process("pdf_cert_temp", context),
                "Java", certificate);
        certificate.setFileName(generatedFileName);
        return generatedFileName;
    }

    public String generatePdfFromHtml(String html, String courseTag, Certificate certificate) {
        String fileName = courseTag + "_" +
                transliterateUtil.transliterateTextFromRussianToLatin(certificate.getStudent().getName()) + "_" +
                certificate.getIssuedDate().toString() + "_" + certificate.getId() +
                CertStrings.CERT_EXTENSION;
        String outputFolder = CertStrings.PATH_TO_PDF_CERTS + File.separator + fileName;
        try (OutputStream outputStream = new FileOutputStream(outputFolder)) {
            ITextRenderer renderer = new ITextRenderer();
            renderer.setDocumentFromString(html);
            renderer.layout();
            renderer.createPDF(outputStream);
        } catch (IOException e) {
            throw new RuntimeException(CertStrings.ERROR_WHILE_OUTPUT_STREAM + e);
        } catch (DocumentException e) {
            throw new RuntimeException(CertStrings.ERROR_CREATING_PDF + e);
        }
        return fileName;
    }

There is my Thymeleaf template for creating PDF

<html xmlns:th="http://www.thymeleaf.org">
<head>
    <style th:inline="text">
        @page {
            size: 1920px 1440px;
        }

        body {
            font-family: Arial, sans-serif;
            background-image: url(http://some_url/files/cert_templates/java_template_bg.jpg);
            background-size: cover;
            background-position: center;
            background-repeat: no-repeat;
            width: 100%;
            height: 100%;
            margin: 0px;
            padding: 0px;
            box-sizing: border-box;
        }

        .company-name {
            text-align: center;
            color: #D9AF58;
            font-style: italic;
            font-size: 70px;
            font-weight: 100;
            position: absolute;
            margin-top: 5%;
            left: 44%;
        }

        .student-name {
            text-align: center;
            color: #D9AF58;
            font-family: "Times New Roman", Times, serif;
            font-size: 70px;
            font-weight: 100;
            position: absolute;
            margin-top: 31%;
            left: 38.5%;
        }

        .course-completing {
            text-align: center;
            color: #D9AF58;
            font-family: "Times New Roman", Times, serif;
            font-weight: 100;
            font-size: 51px;
            position: absolute;
            margin-top: 36%;
            left: 30%;
        }

        .course-description {
            text-align: center;
            color: #D9AF58;
            font-family: "Times New Roman", Times, serif;
            font-size: 50px;
            font-weight: 100;
            position: absolute;
            margin-top: 40%;
            left: 35%;
        }

        .mentor-name {
            text-align: center;
            color: #D9AF58;
            font-family: "Times New Roman", Times, serif;
            font-size: 30px;
            font-weight: 100;
            position: absolute;
            margin-top: 44%;
            left: 39%;
        }

        .verification-code {
            text-align: center;
            color: #D9AF58;
            font-size: 25px;
            font-weight: 100;
            position: absolute;
            margin-top: 46%;
            left: 34.5%;
        }

        .issued-date {
            text-align: left;
            color: #D9AF58;
            font-size: 50px;
            font-weight: 100;
            position: absolute;
            margin-top: 55%;
            left: 21.1%;
        }

        .signature {
            text-align: left;
            color: #D9AF58;
            font-size: 50px;
            font-weight: 100;
            position: absolute;
            margin-top: 55%;
            right: 21.5%;
        }
    </style>

</head>
<body>
<div>
    <div class="company-name">
        <span th:text="${company_name}"></span>
    </div>
    <div class="student-name">
        <span th:text="${student_name}"></span>
    </div>
    <div class="course-completing">
        <span>has successfully completing course</span>
    </div>
    <div class="course-description">
        <u th:text="${course_name}"></u>
    </div>
    <div class="mentor-name">
        Taught by <span th:text="${mentor_name}"></span>
    </div>
    <div class="verification-code">
        Verification code: <span th:text="${verification_code}"></span>
    </div>
    <div>
        <div class="issued-date">
            <span th:text="${issued_date}"></span>
        </div>
        <div class="signature">
            <span th:text="'Test S.'"></span>
        </div>
    </div>
</div>
</body>
</html>

This 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>3.1.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>PdfGeneratorTest</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>PdfGeneratorTest</name>
    <description>PdfGeneratorTest</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ibm.icu</groupId>
            <artifactId>icu4j</artifactId>
            <version>69.1</version>
        </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>ognl</groupId>
            <artifactId>ognl</artifactId>
            <version>3.2.10</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

This is example, what is look like: Example of generated PDF

Tried to modify HTML tempalte, but it didn't work. Resize of image or output page also didn't work.


Solution

  • This is Normal Printing Behaviour, it is simply adding normal printer "gripper margins" (so the ink does not foul the rollers). You will need to add CSS to reduce printer Media Margins to "Borderless"

    enter image description here

    One way is to set the @Media print for the body to Zer0

    enter image description here

    This may not remove precisely all white areas (without a minor pixel or two change) since there is often some unit rounding off for scale ratios, which can vary from browser to browser, and often depend on current page scaling.

    So here are the crude values I used for reducing the printer border

    <head>
        <style th:inline="text">
            @media print {
            @page {
            size: 1920px 1440px;
            border: 0px;
            margin: 0px;
            padding: 0px;
            }
            }