(最新)itext7 freemarker动态模板转pdf

1.引入依赖

复制代码
<!--PDF导出POM-->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext7-core</artifactId>
    <version>8.0.3</version>
    <type>pom</type>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>html2pdf</artifactId>
    <version>5.0.3</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>

2.编写FTL文件

复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
</head>

<style>
    .pic {
        width: 100%;
        height: 100%;
    }
</style>

<body>
<div id="app">
    <#--封面图-->
    <#if show>
        <img class="pic" src="${coverImageFile!''}"/>
    </#if>
</div>
</body>
</html>

3.后台代码

复制代码
@Resource
Configuration configuration;

/**
     * 导出
     */
    @Override
    public void export(HttpServletResponse response){
         //获取文件byte[]
        byte[] pdfBytes=getPdfBytes();
        try{
            //输出pdf文件
            String chineseName="中文文件名";
            response.setCharacterEncoding("utf-8");
            String fileName=URLEncoder.encode(chineseName, StandardCharsets.UTF_8).replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=" + fileName + ".pdf");
            //设置响应头(导出为pdf文件)
            response.setContentType("application/pdf");
            //设置响应头(返回流给前端预览使用)
            //response.setContentType("application/octet-stream;charset=UTF-8");
            // 获取HttpServletResponse的OutputStream
            OutputStream os=response.getOutputStream();
            // 写入字节流数组
            os.write(pdfBytes);
            os.flush();
            os.close();
        }catch(IOException e){
            throw new InvalidCmdException(e.getMessage());
        }
    }

private byte[] getPdfBytes(){
        //获取模板替换数据
        Map<String, Object> map=new HashMap<>();
        map.put("show", true);
        map.put("coverImageFile", "base64图片");
        try{
            //读取freemarker模板
            Template template=configuration.getTemplate("FTL文件路径");
            StringWriter out=new StringWriter();
            // 将数据模型合并到模板中生成HTML
            template.process(map, out);
            String htmlContent=out.toString();
            // 初始化字节输出流用于保存PDF
            ByteArrayOutputStream pdfOutput=new ByteArrayOutputStream();
            // 设置中文字体
            ConverterProperties converterProperties=new ConverterProperties();
            FontProvider fontProvider=new DefaultFontProvider(true, true, false);
            PdfDocument pdfDoc;
            FontProgram fontProgram=FontProgramFactory.createFont("static/simhei.ttf");
            // 添加中文字体到字体提供者
            fontProvider.addFont(fontProgram);
            converterProperties.setFontProvider(fontProvider);
            // 转换HTML到PDF
            try(ByteArrayOutputStream outputStream=pdfOutput){
                // 初始化文档
                PdfWriter writer=new PdfWriter(outputStream);
                pdfDoc=new PdfDocument(writer);
                //设置pdf纸张大小
                pdfDoc.setDefaultPageSize(PageSize.A4);
                // 将HTML转换为PDF
                HtmlConverter.convertToPdf(htmlContent, pdfDoc, converterProperties);
            }
            // 从字节输出流中获取PDF的字节数组
            byte[] pdfBytes=pdfOutput.toByteArray();
            // 关闭流
            pdfOutput.close();
            pdfDoc.close();
            out.close();
            return pdfBytes;
        }catch(IOException e){
            throw new InvalidCmdException("读取候选人freemarker模板错误");
        }catch(TemplateException e){
            throw new InvalidCmdException("替换模板值错误");
        }
    }

4.注意事项

itext7本身不支持中文 所以需要在项目中引入字体文件。

freemarker图片需要使用base64格式图片

byte\[\] 数组转base64

复制代码
 /**
     * 图片转base64
     */
    private String convertBase64(String ossFileId){
        if(String2Utils.isNotBlank(ossFileId)){
            //这里使用的是阿里的oss查询
            OssUrlDto ossUrlDto=ossFileService.findDfsById(ossFileId).orElse(null);
            try{
                byte[] adviserBytes=ossClientService.getContentByteByKey(ossUrlDto.getBucketName(), ossUrlDto.getPath());
                return "data:image/jpeg;base64," + Base64.getEncoder().encodeToString(adviserBytes);
            }catch(Exception e){
                throw new InvalidCmdException("图片转base64失败");
            }
        }
        return "";
    }

给导出的PDF设置页面边距,网上搜索了很多中办法,只有一种成功了

再FTL页面中添加

复制代码
<style>
    @page {
        size: A4;
        margin: 10mm;
    }
</style>
相关推荐
我是人✓16 分钟前
Java核心语法:作用域、循环与大数值处理
java·开发语言·后端
夜之眷属36 分钟前
PriorityQueue:for-each 遍历和 poll 取出,顺序一样吗?
java·服务器·数据结构·后端·算法
蜗牛互联网1 小时前
Java标准HTTP调用多模态模型的票据抽取前置校验
java·人工智能·python·http
工具派1 小时前
Agent 不是玄学:用 Java 状态机思维理解 LangGraph
java·人工智能·python
十年Java程序媛2 小时前
String / StringBuilder / StringBuffer|别再乱用字符串拼接,底层原理一次讲透
java·spring boot·后端
傲世仙尊2 小时前
类和对象中-构造函数与析构函数把初始化清理自动化
java·运维·自动化
她的男孩2 小时前
权限通了、列表有数,一点分页就炸Forge Admin 用 COUNT(1) 扛住复杂待办
java·spring boot·mybatis
骑着蜗牛撵大象3272 小时前
豆包 Seed 驱动的记账智能体:从拍票抽取到月报生成的全链路落地记录
java·前端·javascript·结构化输出·豆包seed·多模态识别·记账系统
憧憬成为java架构高手的小白2 小时前
langchain4j手敲理解
java·langchain
10mAh3 小时前
【Java】ArrayList 遍历删除为什么报错?三种写法与漏删复现
java·开发语言·windows