java使用html模板导出pdf(使用thymeleaf进行渲染)

  • 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <!-- ALWAYS required. -->
    <groupId>com.openhtmltopdf</groupId>
    <artifactId>openhtmltopdf-core</artifactId>
    <version>1.0.0</version>
</dependency>
<dependency>
    <!-- Required for PDF output. -->
    <groupId>com.openhtmltopdf</groupId>
    <artifactId>openhtmltopdf-pdfbox</artifactId>
    <version>1.0.0</version>
</dependency>
  • 工具类
package com.shell.common.utils.pdf;

import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import org.springframework.util.ResourceUtils;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import java.io.OutputStream;
import java.util.Map;

public class PdfUtil {
    /**
     * @param os
     * @param pdfFileName  pdf文件名称(不包含pdf后缀)
     * @param templateName 模板名称
     * @param variables    模板变量
     */
    public static void buildPdf(OutputStream os, String pdfFileName, String templateName, Map<String, Object> variables) throws Exception {
        //构造模板引擎
        ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
        resolver.setPrefix("templates/");//模板所在目录,相对于当前classloader的classpath。
        resolver.setSuffix(".html");//模板文件后缀
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(resolver);
        //构造上下文(Model)
        Context context = new Context();
        context.setVariable("templateName", templateName);
        context.setVariable("pdfFileName", pdfFileName);
        context.setVariables(variables);
        //渲染模板
        String example = templateEngine.process(templateName, context);
        PdfRendererBuilder builder = new PdfRendererBuilder();
        builder.useFont(ResourceUtils.getFile("classpath:templates/fonts/simsun.ttf"), "simsun");
        builder.useFastMode();
        builder.withHtmlContent(example, ResourceUtils.getURL("classpath:templates/img/").toString());
        builder.toStream(os);
        builder.run();

    }
}
  • 测试代码
package com.step.pdf.demo.controller;
import com.step.pdf.demo.util.PdfUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@RestController
public class DemoController {
    @GetMapping("pdf")
    public void test(HttpServletResponse response) throws Exception {
        Map<String, Object> variables = new HashMap<>();
        variables.put("assessmentTypeName", "测试");
        variables.put("staffName", "staffName");
        variables.put("functions", "functions");
        variables.put("assessmentDate", "assessmentDate");
        variables.put("roles", "roles");
        variables.put("score", "score");
        variables.put("signature", "signature");
        PdfUtil.buildPdf(response, "用于测试的pdf", "parent", variables);
    }
}
  • 模板代码
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
    <meta charset="UTF-8" />
    <title th:text="${pdfFileName}"></title>
    <style>
        @page {
            size: A4;
            @top-center {
                content: element(header);
            }
            @bottom-center {
                font-family: 'simsun', serif;
            }
        }
        html,
        body {
            font-family: 'simsun', serif;
            font-size: 14px;
        }
        table {
            -fs-table-paginate: paginate;
            border-collapse: collapse;
            width: 100%;
        }
        .headingBox {
            font-size: 18px;
            text-align: center;
            font-style: italic;
            font-weight: bolder;
        }
        table,
        td,
        th {
            border: 0px solid #555;
            padding: 3px 5px;
        }
        .box1,
        .box2,
        .box3 {
            padding: 10px 20px;
            border-top: 1px solid #999;
        }
        .trBox {
            display: flex;
        }
        .tdBox {
            width: 50%;
            display: flex;
        }
        .titleBox {
            display: inline-block;
            width: 32%;
            font-weight: bold;
        }
        .contentBox {
            width: 68%;
            color: cornflowerblue;
        }
        .titleOverviewBox {
            display: inline-block;
            width: 64%;
            font-weight: bold;
        }
        .contentOverViewBox {
            width: 36%;
            color: cornflowerblue;
        }
        .functionTitleBox,
        .commentsTitleBox {
            font-style: italic;
            font-weight: bolder;
            margin: 0 0 0 2%;
        }
    </style>
    <style>
        #app {
            border: 1px solid #999;
            width: 550px;
            box-sizing: border-box;
            margin: 0px auto;
        }
        #comm {
            height: 100px;
        }
        #content {
            white-space: pre-line;
        }
    </style>
</head>
<body>
<div id="app">
    <h3 th:text="'Competence Assessment_'+${assessmentTypeName}" class="headingBox"></h3>
    <div class="box1">
        <table border="0px;" class="baseInfoBox">
            <tr class="trBox">
                <td class="tdBox"><span class="titleBox">Name:</span><span class="contentBox" th:text="${staffName}"></span></td>
                <td class="tdBox"></td>
            </tr>
            <tr class="trBox">
                <td class="tdBox"><span class="titleBox">Function:</span><span class="contentBox" th:text="${functions}"></span></td>
                <td class="tdBox"><span class="titleOverviewBox">Date:</span><span class="contentOverViewBox"
                                                                                   th:text="${assessmentDate}"></span></td>
            </tr>
            <tr class="trBox">
                <td class="tdBox"><span class="titleBox">Role:</span><span class="contentBox" th:text="${roles}"></span></td>
                <td class="tdBox"><span class="titleOverviewBox">Agreegate Source:</span>
                <span class="contentOverViewBox" th:text="${agreegateSource}"></span></td>
            </tr>
        </table>
    </div>
    <div th:each="item:${questionList}">
        <div class="box1">
            <h4 class="functionTitleBox" th:text="${item.functionName}+':'"></h4>
            <div class="w">
                <tbody th:border="0">
                <tr>
                    <td th:text="${item.questionContent}" id="content"></td>
                </tr>
                </tbody>
            </div>
        </div>
        <div class="box1">
            <h4 class="commentsTitleBox">Comments:</h4>
            <div class="c" th:height="500px" id="comm">
            </div>
            <div style="display: flex;">
                <div style="padding-left: 18em">
                <span th:text="'Score:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'"></span><span
                        th:text="'Signature:'"></span></div>
            </div>
        </div>
    </div>
</div>
</body>
</html>
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值