• springboot基于itextpdf将html生成pdf


    需求:

    将后台的一堆数据,利用velocity模板将html格式生成pdf文件展示,通常在生成XX报告、XX公告场景下常用,毕竟pdf样式稳定,又可以下载。

    velocity语法也特别简单,不会的可以看下https://blog.csdn.net/qiaodaima0/article/details/126158419?spm=1001.2014.3001.5501

    思路:

    将文字内容生成pdf文件用itextpdf就可以很简单的完成,但是pdf一般要求特定的格式(比如报告类的就喜欢用固定的红头标题)或者一定的美观性,纯用后端渲染就不是很方便了,但是html渲染样式就很简单,两个结合就能比较简单生成美观的pdf。

    环境:

    springboot 2.7.2
    jdk1.8
    velocity 1.7
    itextpdf

    实操:

    包目录
    在这里插入图片描述

    1、让前端小姐姐搞一个漂亮的html

    注意:html里面的图片或js/css等其他的静态资源不允许用外部连接,图片就直接用base64码即可
    我们弄一个简单带表格的html,如下:

    DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8"/>
    head>
    <body>
    <div class="contianer">
        <div class="bg-img-container">
        div>
        <h1>模板pdfh1>
        <div class="flex-div">
            <p>表1p>
        div>
    
        <table border="1" cellSpacing="1">
            <tr>
                <th>姓名th>
                <td colspan="3">$!{name}td>
            tr>
            <tr>
                性别th>
                <td colspan="3">
                    #if($!{gender}==1)
                    男
                    #else
                    女
                    #end
                td>
            tr>
            <tr>
                <th>出生日期th>
                <td>$!{birthDate}td>
                <th>联系方式th>
                <td>$!{phone}td>
            tr>
        table>
        <h1>双重For循环取值h1>
        <div class="flex-div">
            <p>表2p>
        div>
        <table border="1" cellSpacing="1">
            <tr>
                <th>学历th>
                <th>学科th>
                <td class="gray-bg">分数td>
            tr>
            #foreach($item in $eduList)
            <tr>
                <th  #set($len= $item.size+1) rowspan="$len">$item.nameth>
                #foreach($result in $item.itemList)
            <tr>
                <td>$velocityCount、$result.subjecttd>
                <td #if($result.num > 60) class="green" #else class="red" #end>$result.numtd>
            tr>
            #end
            tr>
            #end
        table>
    
    div>
    
    body>
    <style>
        .blue {
            color: #244385;
            margin-left: 30px;
        }
    
        .green {
            color: #52c41a;
            margin-left: 30px;
        }
    
        .red {
            color: #c1181e;
            margin-left: 30px;
        }
    
        .flex-div {
            margin: 30px 0 15px;
            display: flex;
            align-items: center;
        }
    
        .line {
            width: 540px;
            height: 0px;
            border: dashed 1px #edf0f5;
        }
    
        table {
            width: 700px;
            border-collapse: collapse;
            /*border-spacing: 0;*/
            border-left: 1px solid #edf0f5;
            border-top: 1px solid #edf0f5;
        }
    
        th, td {
            border-right: 1px solid #edf0f5;
            border-bottom: 1px solid #edf0f5;
            padding: 5px 15px;
        }
    
        h1 {
            text-align: center;
            font-family: 'Microsoft YaHei';
            line-height: 60px;
            letter-spacing: 8px;
            color: #244385;
        }
    
        p {
            font-family: 'Microsoft YaHei';
            font-size: 20px;
            font-weight: normal;
            color: #244385;
            margin: 0 16px;
        }
    
        th {
            background-color: rgba(0, 0, 0, 0.01);
            width: 90px;
            min-width: 90px;
            max-width: 90px;
            text-align: left;
            text-indent: 10px;
            font-family: 'Microsoft YaHei';
            font-size: 16px;
            font-weight: normal;
            font-stretch: normal;
            line-height: 48px;
            letter-spacing: 2px;
            color: #666666;
        }
    
        td {
            font-size: 16px;
            color: #000000;
            text-align: left;
            text-indent: 10px;
            padding-right: 20px;
            height: 50px;
        }
    
        table,tr, th, td {
            border: solid 1px #edf0f5;
        }
    
        .gray-bg {
            background-color: rgba(0, 0, 0, 0.01);
        }
    style>
    html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155

    2、pom依赖

    
    
        4.0.0
        
            org.springframework.boot
            spring-boot-starter-parent
            2.7.2
             
        
    
        com.template.pdf
        template_pdf
        0.0.1-SNAPSHOT
        Demo project for Spring Boot
    
        
            1.8
        
        
            
                org.springframework.boot
                spring-boot-starter-web
            
    
            
                org.springframework.boot
                spring-boot-starter-test
                test
            
    
            
            
                cn.hutool
                hutool-all
                5.7.19
            
    
            
                com.alibaba
                fastjson
                1.2.76
            
    
            
            
                org.apache.velocity
                velocity
                1.7
            
    
            
                com.itextpdf
                html2pdf
                4.0.3
            
            
                com.itextpdf
                font-asian
                7.2.3
            
    
        
    
        
            
                
                    org.springframework.boot
                    spring-boot-maven-plugin
                
            
        
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    3、PdfUtil工具类

    import com.itextpdf.html2pdf.ConverterProperties;
    import com.itextpdf.html2pdf.HtmlConverter;
    import com.itextpdf.kernel.font.PdfFont;
    import com.itextpdf.kernel.font.PdfFontFactory;
    import com.itextpdf.kernel.geom.PageSize;
    import com.itextpdf.kernel.pdf.PdfDocument;
    import com.itextpdf.kernel.pdf.PdfWriter;
    import com.itextpdf.layout.font.FontProvider;
    import org.apache.velocity.Template;
    import org.apache.velocity.app.Velocity;
    import org.apache.velocity.context.Context;
    import org.apache.velocity.runtime.RuntimeConstants;
    import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
    
    import java.io.OutputStream;
    import java.io.StringWriter;
    import java.nio.charset.StandardCharsets;
    
    /**
     * PDF工具
     *
     * @author ppp
     * @date 2022/8/5
     */
    public class PdfUtil {
    
        static {
            // Velocity初始化
            Velocity.setProperty(RuntimeConstants.OUTPUT_ENCODING, StandardCharsets.UTF_8);
            Velocity.setProperty(RuntimeConstants.INPUT_ENCODING, StandardCharsets.UTF_8);
            Velocity.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
            Velocity.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
            Velocity.init();
        }
    
    
        /**
         * 据模板生成pfd格式文件
         *
         * @param context      上下文对象
         * @param template     pdf模板
         * @param outputStream 生成ofd文件输出流
         */
        public static void pdfFile(Context context, String template, OutputStream outputStream) {
            try (PdfWriter pdfWriter = new PdfWriter(outputStream)) {
                PdfDocument pdfDocument = new PdfDocument(pdfWriter);
                pdfDocument.setDefaultPageSize(PageSize.A4);
    
                ConverterProperties properties = new ConverterProperties();
                FontProvider fontProvider = new FontProvider();
                // 字体设置,解决中文不显示问题
                PdfFont sysFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H");
                fontProvider.addFont(sysFont.getFontProgram(), "UniGB-UCS2-H");
                properties.setFontProvider(fontProvider);
    
                Template pfdTemplate = Velocity.getTemplate(template, "UTF-8");
                StringWriter writer = new StringWriter();
                pfdTemplate.merge(context, writer);
                HtmlConverter.convertToPdf(writer.toString(), pdfDocument, properties);
                pdfDocument.close();
            } catch (Exception e) {
                throw new RuntimeException("PFD文件生成失败", e);
            }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    4、Controller接口测试

    /**
    * PDF生成Controller
    * @author ppp
    * @date 2022/8/4
    */
    @Controller
    @RequestMapping("/velocity")
    public class PdfController {
    
        @RequestMapping("/getPdf")
        public void get(HttpServletResponse response){
            response.reset();
            response.setContentType("application/pdf");
            String filename = System.currentTimeMillis()+".pdf";
            response.addHeader("Content-Disposition", "inline; filename=" + URLUtil.encode(filename, CharsetUtil.CHARSET_UTF_8));
            VelocityContext context = new VelocityContext();
    
            context.put("name", "彭也行");
            context.put("gender", 1);
            context.put("birthDate", DateUtil.formatDateTime(new Date()));
            context.put("phone", "13666666666");
    
            List> eduList = new ArrayList<>();
            // 小学
            Map primarySchoolMap = new HashMap<>();
            primarySchoolMap.put("name", "小学");
            List> scoreList = new ArrayList<>();
            // 语文成绩
            Map chineseScore = new HashMap<>();
            chineseScore.put("subject", "语文");
            chineseScore.put("num", 60);
            // 数学成绩
            Map mathScore = new HashMap<>();
            mathScore.put("subject", "数学");
            mathScore.put("num", 99);
            scoreList.add(chineseScore);
            scoreList.add(mathScore);
            primarySchoolMap.put("itemList", scoreList);
            primarySchoolMap.put("size", scoreList.size());
    
            // 初中
            Map middleSchoolMap = new HashMap<>();
            middleSchoolMap.put("name", "初中");
            List> middleScoreList = new ArrayList<>();
            // 语文成绩
            Map middleChineseScore = new HashMap<>();
            middleChineseScore.put("subject", "语文");
            middleChineseScore.put("num", 60);
            // 数学成绩
            Map middleMathScore = new HashMap<>();
            middleMathScore.put("subject", "数学");
            middleMathScore.put("num", 99);
            // 英语
            Map middleEnScore = new HashMap<>();
            middleEnScore.put("subject", "英语");
            middleEnScore.put("num", 55);
    
            middleScoreList.add(middleChineseScore);
            middleScoreList.add(middleMathScore);
            middleScoreList.add(middleEnScore);
            middleScoreList.add(middleEnScore);
            middleScoreList.add(middleEnScore);
            middleScoreList.add(middleEnScore);
            middleScoreList.add(middleEnScore);
            middleScoreList.add(middleEnScore);
            middleSchoolMap.put("itemList", middleScoreList);
            middleSchoolMap.put("size", middleScoreList.size());
    
            eduList.add(primarySchoolMap);
            eduList.add(middleSchoolMap);
            context.put("eduList", eduList);
            try(ServletOutputStream outputStream = response.getOutputStream()){
                PdfUtil.pdfFile(context, "demo.html", outputStream);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    5、效果

    还是很好看的,用户访问连接就可以直接预览,也可以自己下载
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    6、如果不想预览,直接转换

    在这里插入图片描述

  • 相关阅读:
    2023华为杯研究生数学建模E题思路代码分析
    【JavaEE】多线程(二)
    js中数组去重(数组中元素是对象)
    【工具分享】typora-beta-0.11.18版本又提示过期的【神级】解决方案
    打电话用蓝牙耳机什么牌子好?打电话清晰的蓝牙耳机推荐
    win10 LTSC无损升级 win11专业版 记录
    spring cloud alibaba之nacos
    MySQL之函数
    antd vue 组件 使用下拉框的层级来显示后面的输入框
    debian无法使用reboot 等系统命令解决
  • 原文地址:https://blog.csdn.net/qiaodaima0/article/details/126271272