当前位置: 首页 > 工具软件 > xhtmlrenderer > 使用案例 >

基于xhtmlrenderer+freemarker的HTML转PDF方法

山越
2023-12-01

最近项目上有要求,需要生成一个可供客户下载的pdf或者图片的许可协议,这个pdf/图片里面的内容需要是用户相关的内容,也就是内容是可变的,不过其他的样式是统一的,我也因此找了相关的功能包,发现可以使用

xhtmlrenderer+freemarker

完成这个需求,此次仅制作了pdf相关教程供大家参考,至于转换图片可以参考

基于xhtmlrenderer+freemarker的HTML转图片方法_键盘满配的博客-CSDN博客

因pdf的内容是可变的,所以单纯的html是无法满足要求的,搜索了一下发现可以使用freemarker,

freemarker是一种模板引擎,可以基于模板和要改变的数据, 用来生成输出文本,这个是很适合我们这边需求的

引入freemarker

<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.28</version>
</dependency>

html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>Title</title>
    <style>
        body{
            font-family:SimHei;
        }
        .red{
            color: red;
        }
    </style>
</head>
<body>
<h1>
    许可协议
</h1>
<div class="red">
    被许可方:${company}
</div>
<div class="red">
    许可产品:${product}
</div>
<div class="red">
    产品授权类型:${type}
</div>
<div class="red">
    产品使用许可范围:${scope}
</div>
<div class="red">
    产品授权用户人数:${count}
</div>
<div class="red">
    产品授权有效期:${validity}
</div>
<div class="red">
    许可协议编号:${license}
</div>
</body>
</html>

可以看到当前html中增加了变量${},可以利用这些变量生成可变的pdf文件,用来填充对应文字

一开始时因为没有使用图片而且自己写了一个简单的html,当时使用itext,效果也是挺好的,后来在html中加上了一些css样式和图片发现无法创建在pdf中,然后找了一下,发现xhtmlrenderer可以支持html中的样式和背景图导入

xhtmlrenderer引入:

<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-pdf</artifactId>
    <version>9.1.16</version>
    <exclusions>
        <exclusion>
            <groupId>bouncycastle</groupId>
            <artifactId>bcmail-jdk14</artifactId>
        </exclusion>
        <exclusion>
            <groupId>bouncycastle</groupId>
            <artifactId>bcprov-jdk14</artifactId>
        </exclusion>
        <exclusion>
            <groupId>bouncycastle</groupId>
            <artifactId>bctsp-jdk14</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcmail-jdk14</artifactId>
    <version>1.38</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bctsp-jdk14</artifactId>
    <version>1.38</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk14</artifactId>
    <version>1.38</version>
</dependency>

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

测试类:

    private String agreementDir = "D:/java/agreement/"; //导出位置
    @Test
    public void test3() throws IOException, URISyntaxException, DocumentException {
        Path basePath = Paths.get(agreementDir);
        if (!Files.exists(basePath) || !Files.isDirectory(basePath)) {
            Files.createDirectory(basePath);
        }
        agreementDir += File.separator + "10095";
        basePath = Paths.get(agreementDir);
        if (!Files.exists(basePath) || !Files.isDirectory(basePath)) {
            Files.createDirectory(basePath);
        }

        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("company", "公司");
        paramMap.put("product", "产品名称");
        paramMap.put("type", "许可证授权");
        paramMap.put("scope", "使用范围");
        paramMap.put("count", "购买数量");
        paramMap.put("validity", "有效期");
        paramMap.put("license", "购买序列码");
        paramMap.put("createTime", DateFormatUtils.format(new Date(), "yyyy年MM月dd日"));
        //freemarker位置
        String freemarker = "D:/java/freemarker/";
        File dir = new File(freemarker);
        String content = PDFUtils.freeMarkerRender(paramMap, "license.html", dir);
        PDFEncryption pdfEncryption = new PDFEncryption();
        //加密
//        pdfEncryption.setUserPassword("123456".getBytes());
        //只读
        pdfEncryption.setAllowedPrivileges(PdfWriter.ALLOW_PRINTING);
        pdfEncryption.setEncryptionType(PdfWriter.STANDARD_ENCRYPTION_128);
        long l = System.currentTimeMillis();

        List<String> fontPaths = new ArrayList<>();
        //字体
        fontPaths.add(freemarker + File.separator+ "font" + File.separator + "msyh.ttf");

        //需要导入图片位置
        String imgPath = "D:/java/freemarker/";
        PDFUtils.createPDF(content, agreementDir + l + ".pdf",
                imgPath, fontPaths, pdfEncryption);
    }
PDFUtils工具类

@Slf4j
public class PDFUtils {

    private static Configuration freemarkerCfg;

    static {
        freemarkerCfg =new Configuration();
    }

    /**
     * @Description pdf生成工具
     * @Date 10:42 2020/10/26
     * @Param inputFile 输入的文件模板
     * @Param outputFile 输出文件位置
     * @Param imgPath 所需图片的路径
     * @Param fontPath 字体选择
     * @Param pdfEncryption 生成的pdf权限
     * @return void
     **/
    public static void createPDF(String inputFile, String outputFile, String imgPath, List<String> fontPaths, PDFEncryption pdfEncryption) throws IOException, DocumentException {
        OutputStream os = new FileOutputStream(outputFile);
        ITextRenderer renderer = new ITextRenderer();

        renderer.setDocumentFromString(inputFile);

        //解决图片的相对路径问题
        renderer.getSharedContext().setBaseURL("file:" + imgPath);

        //pdf权限控制
//        PDFEncryption pdfEncryption = new PDFEncryption();
        //加密
//        pdfEncryption.setUserPassword("123456".getBytes());
        //只读
//        pdfEncryption.setAllowedPrivileges(PdfWriter.ALLOW_PRINTING);
//        pdfEncryption.setEncryptionType(PdfWriter.STANDARD_ENCRYPTION_128);
        renderer.setPDFEncryption(pdfEncryption);

        //解决中文支持问题
        ITextFontResolver fontResolver = renderer.getFontResolver();
        for (String fontPath : fontPaths) {
            fontResolver.addFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        }

        renderer.layout();

        renderer.createPDF(os);

        os.close();
    }

    public static String freeMarkerRender(Map<String, Object> data, String htmlTmp, File dir) throws IOException {
        freemarkerCfg.setDirectoryForTemplateLoading(dir);

        Writer out = new StringWriter();
        try {
            Template template = freemarkerCfg.getTemplate(htmlTmp, "utf-8");
            template.process(data, out);
            out.flush();
            return out.toString();
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            try {
                out.close();
            } catch (IOException ex) {
                log.error(ex.getMessage());
            }
        }
        return null;
    }
}

之后基本可以完成pdf导出功能,不过需要注意的是,字体使用需要防止侵权,导入字体时需要是本公司可用的字体,否则哪天被告了可就郁闷了

关于html的创建需要注意一点,freemarker对html的模板识别是强制格式的,不能像平时使用html时可以缺少</img>等尾符,除了<head></head>中的<meta />,其他的需要严格对称,否则会导致freemarker识别错误

还有一点,css样式需要在html中,不能使用引入方式

本次使用的pdf导出只能做简单的html转pdf,实际上我们后来也没有用这个方案,毕竟要求还是挺高的,而且每次要有一个新的html转pdf会非常麻烦,然后我就找了一下市面上有没有相关工具,然后发现了这个:

文档格式转换_永中云服务平台

可以随时调用他们的api把我们需要的页面转成各种格式,如pdf/图片之类的都行,而且也有文档的在线预览(也就是可以把当前各种格式文件转成html在浏览器中查看,这个功能感觉很实用),另一个是在线编辑,这个功能感觉很强大,可以把文档上传后直接在页面上编辑,可以实时保存和分享,如果需要更专业的功能可以上去看看,毕竟简单的xhtmlrenderer+freemarker能做的功能还是很少的

 类似资料: