FreeMark生成pdf的升级----------使用“动态“模板

       在之前的一篇文章中写到过使用FreeMark生成pdf的案例,但是之前的写法使用的都是静态模板,今天我们来给它升级一下

        使用静态模板的地址:https://blog.csdn.net/winerpro/article/details/121189977

        在实际场景中我们使用的模板不是一成不变的,如果使用的是静态模板每次更换模板就需要把项目停了,把模板更换然后重启项目,这样一来并不是很友好,所以我们是不是可以考虑把它提出来,让它动态获取!

        那么问题来了,文件又怎么动态获取,一个简单的方式,我们可以把它放进数据库,然后给它设置类型,想要获取什么模板就获取什么类型的模板,在复杂一点还可以加上版本号等等,这样操作起来对日后的维护非常的赞!理论有了,实际搞起来吧!

        具体怎么配置的不在多数,大家可以回去看我之前的一篇,这里着重向说一下html文件的设计,我们知道使用offic可以把一个word文件转成html的格式,但是转完之后会出现一对见都没见过的样式,而且不但你不认识,FreeMark也不认识!所以最后的方式就是自己来写!其中样式我已标注,文章的最后我回把整个源码上传!

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <style>
        @page {

            @bottom-left {
                content: element(footer);
                vertical-align: top;
                padding-top: 10px;
            }

            @top-right {
                content: element(header);
                vertical-align: bottom;
                padding-bottom: 10px;
            }

            /*页面布局*/
            margin-top: 2.3cm;
            margin-left: 2.3cm;
            margin-right: 2.3cm;
            margin-bottom: 2.3cm;
            size: A4 portrait;
        }
        /*默认字体*/
        body{
            font-family: 宋体;
            font-size: 14pt;
        }

        div.header {
            position: running(header);
            font-size: 14px;
        }

        div.footer {
            display: block;
            margin-top: 0.5cm;
            position: running(footer);
        }

        #pagenumber:before {
            content: counter(page);
        }

        #pagecount:before {
            content: counter(pages);
        }

        div.header {
            position: running(header);
            font-size: 14px;
        }

        div.footer {
            display: block;
            margin-top: 0.5cm;
            position: running(footer);
        }

        /*换页(在这一行之前分页 / 之后分页是page-break-after: always)*/
        .next-page {
            page-break-before: always
        }

        /*宋体四号字*/
        .four{
            font-family: 宋体;
            font-size: 14pt;
        }
        /*宋体四号加粗字*/
        .fourb{
            font-family: 宋体;
            font-size: 14pt;
            font-weight: bold;
        }
        /*分页*/
        .next-page {
            page-break-before: always
        }
        /*左对齐*/
        .right {
            text-align: right;
        }
        /*右对齐*/
        .left {
            text-align: left;
        }
        /*首行缩进*/
        .indent{
            text-indent:2em;
        }
        /*居中*/
        .center{
            text-align: center;
        }
        /*默认1.5倍行高*/
        p{
            line-height: 150%;
        }

        table {
            width: 100%;
            border-collapse:collapse;
            margin-left:0pt;border:solid;
        }
        th{
            text-align:center;
            border:solid windowtext 1.0pt;
            height:26pt;

        }
        td{
            text-align:center;
            border:solid windowtext 1.0pt;
            height:26pt;

        }

        hr {
            background-color: #000000;
            border: dashed #000000 0.5px;
            height: 1px;
        }
    </style>
</head>
<page_footer footer="page">
    <div class='footer'>
        <div th:fragment="footer">
            <!-- <hr> -->
            <div class="page-count">
                <span id="pagenumber"></span>
            </div>
        </div>
    </div>
</page_footer>
<body>
<page>
    <p class="center"><span class="fourb">考试成绩</span> </p>
    <p>&nbsp;</p>
    <table>
        <thead>
        <tr>
            <td style="width: 25%"><span>姓名</span></td>
            <td style="width: 25%"><span>语文</span></td>
            <td style="width: 25%"><span>数学</span></td>
            <td style="width: 25%"><span>英语</span></td>
        </tr>
        </thead>
        <tbody>
        <#if data?? && (data?size > 0)>
        <#list data as p>
        <tr>
            <td><span>${p.name!""}</span></td>
            <td><span>${p.language!"0"}</span></td>
            <td><span>${p.math!""}</span></td>
            <td><span>${p.english!""}</span></td>
        </tr>
        </#list>
        </#if>
        </tbody>
    </table>

    <p class="indent"><span>在这段时间中,孩子的进步比较大。这些都离不开老师的精心栽培,孩子的进步我们家长都看在眼里,作为家长我们也希望,老师可以关注孩子更多一些。</span></p>
    <p class="indent"><span>另外,希望老师可以多多与我们家长沟通,这样可以及时反映孩子的不足或是在校的一些情况,这样我们也可以更加了解孩子,孩子的缺点我们也会加以指导改正。</span></p>
    <p>&nbsp;</p>
    <p>&nbsp;</p>
    <p>&nbsp;</p>
    <p style="margin-left: 300pt"><span>家长签字:___________</span></p>
</page>
</body>
</html>

搞完模板算是成功了一大半,因为真正费时的就是模板的绘制,要一点一点扣,真正的java代码配置差异不大,把我主要的PdfUtil贴到这里参考一下

package com.pdf.generate_pdf.common;

import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.io.font.FontProgram;
import com.itextpdf.io.font.FontProgramFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.font.FontProvider;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * PDF工具类
 *
 * @author zzt
 * @date 2021/12/8
 */
@Component
@Slf4j
public class PdfUtil {

    private static Configuration configuration;

    static {
        synchronized (PdfUtil.class) {
            if (ObjectUtils.isEmpty(configuration)) {
                configuration = new Configuration(Configuration.VERSION_2_3_28);
                configuration.setTemplateLoader(new StringTemplateLoader());
                configuration.setDefaultEncoding("UTF-8");
            }
        }
    }

    /**
     * 生成pdf工具
     *
     * @param bytes:模板
     * @param obj:数据
     * @param outPutPath:输出路径
     */
    public static void generatePdfByByteArrays(byte[] bytes, Object obj, String outPutPath) {
        File file = new File(outPutPath);
        try (OutputStream fos = new FileOutputStream(file);
             PdfWriter pdfWriter = new PdfWriter(fos);
             PdfDocument pdfDocument = new PdfDocument(pdfWriter);
             StringWriter templateOut = new StringWriter();
             InputStreamReader inputStreamReader = new InputStreamReader(new ByteArrayInputStream(bytes), StandardCharsets.UTF_8)) {

            Template template = new Template("template", inputStreamReader, configuration);
            template.process(obj, templateOut);
            String html = templateOut.toString();
            if (StringUtils.isEmpty(html)) {
                log.error("模板存在错误!");
            }
            HtmlConverter.convertToPdf(html, pdfDocument, buildConvertProperties());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 构建字体
     *
     * @return
     */
    private static ConverterProperties buildConvertProperties() {
        try {
            ConverterProperties converterProperties = new ConverterProperties();
            File fontsDirectory = getClassPathStaticFile("static/fonts");
            File[] fontFiles = fontsDirectory.listFiles();
            FontProvider fontProvider = new FontProvider();
            for (File file : fontFiles) {
                FontProgram fontProgram = FontProgramFactory.createFont(file.getAbsolutePath(), true);
                fontProvider.addFont(fontProgram);
            }
            converterProperties.setFontProvider(fontProvider);
            converterProperties.setBaseUri(getClassPathStaticFile("static").getAbsolutePath());
            return converterProperties;
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        }
    }

    /**
     * 获取字体
     *
     * @param classPath
     * @return
     */
    public static File getClassPathStaticFile(String classPath) {
        try {
            File file = null;
            if (StringUtils.isEmpty(classPath)) {
                file = new File(ResourceUtils.getURL("classpath:").getPath());
            } else {
                file = new File(ResourceUtils.getURL("classpath:" + classPath).getPath());
            }

            return file;
        } catch (Exception e) {
            log.error("获取路径错误:" + classPath, e);
            return null;
        }
    }
}

注意:有时候我们一次性不止会生成一个模板,可能会是多个,这就导致前台响应特别的慢,这时有的小伙伴会使用线程池来提高响应效率,那么这里就该注意一下此处我们的configuration使用的是单例模式创建的,使用多线程的时候会出问题

 ,所以如果用多线程,请把此处的配置改为下图

 这是需要注意的点,然后来测试一下结果如何,

这里把文件上传 保存到了表中然用其中一条的id来生成一个pdf

 

pdf默认是输出到了D盘的根目录,大家可以根据需要修改

以下是测试的源码链接,resources下有建表语句和html模板,-----提取密码:wtwt

百度网盘 请输入提取码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值