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

时间:2024-04-12 07:05:50

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>