美文网首页
java生成pdf

java生成pdf

作者: 伴风听雪 | 来源:发表于2020-04-25 13:37 被阅读0次

    简述
    生成pdf思路主要是分两种情况,情况一:如果模板里面不需要数组参数,那么可以根据html模板直接生成pdf;情况二:如果模板里面需要数组参数,需要先利用freemarker生成html模板,再根据html生成pdf。
    网上文章很多,但是都是用File文件类来做演示和工具包,可用性不够广,并且伴有磁盘文件的创建与删除,影响效率,下面是用I/O流写的工具类

    1. 导入所需要的maven依赖

    <!-- html 转 pdf 所需要的依赖于-->
        <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>core-renderer</artifactId>
            <version>R8</version>
        </dependency>
        
        <!-- freemarker 所需依赖于-->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.28</version>
        </dependency>
    

    2、准备好一份html或freemarker模板文件

    在html或freemarker转pdf时,会出现中文乱码或者中文不显示的问题,故我们需要在html或freemarker中引入外部的中文字体库,这里使用的是simsun.ttc,需要在html或freemark加入相应代码,如下所示:simsun.ttc下载地址
    在html或freemark中加入:

    <style type="text/css">
       /*解决html转pdf文件中文不显示的问题*/
          body {
              font-family: SimSun;
          }
    
          /*设定纸张大小*/
          /* A4纸 */
          /* @page{size:210mm*297mm} */
          @page{size:a4}
      </style>
    

    3.PdfUtil工具类

    package com.futurehotel.cik.biz.util;
    
    import com.lowagie.text.DocumentException;
    import com.lowagie.text.pdf.BaseFont;
    import freemarker.cache.ByteArrayTemplateLoader;
    import freemarker.template.Configuration;
    import freemarker.template.Template;
    import freemarker.template.TemplateException;
    import lombok.Cleanup;
    import lombok.Data;
    import lombok.extern.slf4j.Slf4j;
    import org.w3c.dom.Document;
    import org.xhtmlrenderer.pdf.ITextFontResolver;
    import org.xhtmlrenderer.pdf.ITextRenderer;
    import org.xml.sax.SAXException;
    
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.parsers.ParserConfigurationException;
    import java.io.*;
    import java.nio.charset.StandardCharsets;
    import java.util.*;
    
    
    @Slf4j
    public class PdfUtil {
    
        public static void main(String[] args) throws Exception {
    
            //        @Cleanup InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("pdfTemplate/t.txt");
    //        @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    //        reader.lines().forEach(l -> System.out.println(l));
    
            String htmlPath = "D:/JavaCode/cik/cik-biz/src/main/resources/pdfTemplate/test.html";
            String pdfPath = "C:/Users/baishufei/Desktop/测试.pdf";
    
            String chineseFontPath = "D:/JavaCode/cik/cik-biz/src/main/resources/pdfTemplate/" + "simsun.ttc";
            @Cleanup OutputStream pdfStream = new FileOutputStream(pdfPath);
            PdfUtil.htmlToPdf(new FileInputStream(htmlPath), pdfStream, chineseFontPath);
    
            String templateName = "testFreeMarker";
            @Cleanup InputStream freemarkerTemplateStream = new FileInputStream("D:/JavaCode/cik/cik-biz/src/main/resources/pdfTemplate/testFreeMarker.ftl");
            Map<String, Object> data = initData(3);
            PdfUtil.freemarkerToPdf(templateName, freemarkerTemplateStream, pdfStream, data, chineseFontPath);
    
        }
    
        /**
         * html 转 pdf
         *
         * @param htmlStream html文件流
         * @param pdfStream  pdf文件输出流
         * @param fontPath   字体存储绝对路径,如:/home/chenmingjian/pdf/simsun.ttc
         */
        public static void htmlToPdf(InputStream htmlStream, OutputStream pdfStream, String fontPath) throws DocumentException, IOException, SAXException, ParserConfigurationException {
            //生成document文档对象
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(htmlStream);
    
            ITextRenderer renderer = new ITextRenderer();
            renderer.setDocument(document, null);
            // 解决中文不显示问题
            ITextFontResolver fontResolver = renderer.getFontResolver();
            fontResolver.addFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            renderer.layout();
            renderer.createPDF(pdfStream);
        }
    
       /**
         * freemarker 转 pdf
         *
         * @param templateName
         * @param freemarkerTemplateStream freemarker模板的绝对路径,如:/home/chenmingjian/pdf/template.ftl
         * @param pdfStream                导出pdf输出流
         * @param data                     动态装载freemarker的数据
         * @param fontPath                 字体的绝对路径,,如:/home/chenmingjian/pdf/simsun.ttc
         */
        public static <T> void freemarkerToPdf(String templateName, InputStream freemarkerTemplateStream,
                                               OutputStream pdfStream, T data, String fontPath)
                throws IOException, TemplateException, DocumentException, ParserConfigurationException, SAXException {
            @Cleanup ByteArrayOutputStream htmlOutputStream = new ByteArrayOutputStream();
            freemarkerToHtml(templateName, freemarkerTemplateStream, data, htmlOutputStream);
            @Cleanup ByteArrayInputStream htmlInputStream = new ByteArrayInputStream(htmlOutputStream.toByteArray());
            htmlToPdf(htmlInputStream, pdfStream, fontPath);
        }
    
        /**
         * 将freemarker模板转换成html文件
         *
         * @param templateName             模板名称,如果缓存中名称已存在,会从缓存中取模板
         * @param freemarkerTemplateStream 模板流,如果缓存中没有模板,会生成模板
         * @param data                     数据
         * @param htmlStream               生成的html文件流
         */
        private static void freemarkerToHtml(String templateName, InputStream freemarkerTemplateStream, Map<String, Object> data,
                                             OutputStream htmlStream) throws IOException, TemplateException {
            @Cleanup Writer out = new OutputStreamWriter(htmlStream, StandardCharsets.UTF_8);
            Template temp = getTemplate(templateName, freemarkerTemplateStream);
            temp.setEncoding("UTF-8");
            temp.process(data, out);
        }
    
        /**
         * 获取 freeMarker模板
         *
         * @param templateName   模板命名,如果模板已经存在,会取缓存中的模板
         * @param templateStream 模板流,第一次创建模板会用到
         * @return 模板
         * @throws IOException
         */
        private static Template getTemplate(String templateName, InputStream templateStream) throws IOException {
            Configuration cfg = new Configuration();
            cfg.setClassicCompatible(true);
    
            ByteArrayTemplateLoader templateLoader = new ByteArrayTemplateLoader();
            byte[] templateBytes = new byte[templateStream.available()];
            templateStream.read(templateBytes);
            templateLoader.putTemplate(templateName, templateBytes);
            cfg.setTemplateLoader(templateLoader);
            return cfg.getTemplate(templateName);
        }
    
        public static HashMap<String, Object> initData(int n) {
            HashMap<String, Object> needReplaceMapData = new HashMap<String, Object>();
            /******************************************单个属性************************************/
            needReplaceMapData.put("name", "刘亦菲");
    
            /******************************************list 属性添加***********************************/
            List<Student> students = new ArrayList<Student>();
            for (int i = 0; i < n; i++) {
                Student student = new Student();
                student.setId(i);
                student.setName(" 葫芦娃" + i + "号");
                students.add(student);
            }
            needReplaceMapData.put("students", students);
    
            /******************************************日期 ***********************************/
            needReplaceMapData.put("date", new Date());
            needReplaceMapData.put("showDate", true);
            needReplaceMapData.put("imgUrl", "http://n.sinaimg.cn/translate/344/w700h1244/20180410/B_F_-fyzeyqa1740187.jpg");
            needReplaceMapData.put("description", "描述一只猫");
            return needReplaceMapData;
        }
    
        @Data
        public static class Student implements Serializable {
            private Integer id;
            private String name;
        }
    
    }
    

    4.html所需的模板test.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title>认识table表标签</title>
        <style type="text/css">
            /*解决html转pdf文件中文不显示的问题*/
            body {
                font-family: SimSun;
            }
    
            /*设定纸张大小*/
            /* A4纸 */
            /* @page{size:210mm*297mm} */
            @page{size:a4}
    
            p {
                color: red;
            }
        </style>
    </head>
    <body>
    <table border="1px">
        <caption>我的标题</caption>
        <tbody>
        <tr>
            <th>班级</th>
            <th>学生数</th>
            <th>平均成绩</th>
            <th>班级</th>
            <th>学生数</th>
            <th>平均成绩</th>
            <th>班级</th>
            <th>学生数</th>
            <th>平均成绩</th>
        </tr>
        <tr>
            <td>一班</td>
            <td>30</td>
            <td>89</td>
            <td>一班</td>
            <td>30</td>
            <td>89</td>
            <td>一班</td>
            <td>30</td>
            <td>89</td>
        </tr>
        <tr>
            <td>一班</td>
            <td>30</td>
            <td>89</td>
            <td>一班</td>
            <td>30</td>
            <td>89</td>
            <td>一班</td>
            <td>30</td>
            <td>89</td>
        </tr>
        <tr>
            <td>一班</td>
            <td>30</td>
            <td>89</td>
            <td>一班</td>
            <td>30</td>
            <td>89</td>
            <td>一班</td>
            <td>30</td>
            <td>89</td>
        </tr>
        </tbody>
    </table>
    
    <p>
        《侠客行》<br/>
        年代: 唐 作者: 李白<br/>
        赵客缦胡缨,吴钩霜雪明。银鞍照白马,飒沓如流星。
        十步杀一人,千里不留行。事了拂衣去,深藏身与名。
        闲过信陵饮,脱剑膝前横。将炙啖朱亥,持觞劝侯嬴。
        三杯吐然诺,五岳倒为轻。眼花耳热后,意气素霓生。
        救赵挥金槌,邯郸先震惊。千秋二壮士,煊赫大梁城。
        纵死侠骨香,不惭世上英。谁能书閤下,白首太玄经。
    </p>
    </body>
    </html>
    

    5.freemarker所需的模板freemarker.ftl

    <html>
    <head>
        <title>Welcome FreeMarker!</title>
        <meta charset="utf-8"></meta>
        <meta name="viewport" content="width=device-width, initial-scale=1"></meta>
        <meta http-equiv="x-ua-compatible" content="ie=edge"></meta>
        <link rel="icon" href="https://static.jianshukeji.com/hcode/images/favicon.ico"></link>
        <script src="https://img.hcharts.cn/jquery/jquery-1.8.3.min.js"></script>
        <script src="https://img.hcharts.cn/highcharts/highcharts.js"></script>
    
        <style type="text/css">
            /*解决html转pdf文件中文不显示的问题*/
            body {
                font-family: SimSun;
            }
    
            /*设定纸张大小*/
            /* A4纸 */
            /* @page{size:210mm*297mm} */
            @page {
                size: a4
            }
        </style>
    </head>
    <body>
    
    
    <#-- 单个属性 替换   -->
    <h1>Welcome  ${name}</h1>
    
    
    <img src="${imgUrl}"
         class="shrinkToFit" height="474" width="266"/>
    
    <#-- list 属性替换 -->
    <table border="1">
        <#list students as student>
            <tr>
                <td>${student.name} </td>
                <td>${student.id}</td>
            </tr>
        </#list>
    </table>
    
    <#--常用指令-->
    
    <#--1. 赋值   =左边是要赋值的变量 , = 右边是从map 中传递值,在freemarker 标签中可以直接调用变量名获取后台传递的值,不需要+${}  -->
    <#assign showDate = showDate/>
    
    
    <#--2. 判断 -->
    <#if showDate == true> 现在时间是:${date?string('yyyy-MM-dd')}
    
    <#else>就不给你显示,来一百块
    </#if>
    
    
    <#-- 3. 处理未定义变量或值为null ,当定义的变量为空或者未定义,默认情况下  -->
    
    <#-- 3.1 设置默认值  防止未输入字段,空指针异常-->
    <h1> 葫芦玩总共有几个 ?答: ${number !8} </h1>
    
    <h1> 小明的车是神马牌子 ? 答:${(student.car.sign) !'宝马741'} </h1>
    
    
    <#-- 3.2 加if 判断-->
    <#if weather??> Hi,${weather}
    <#else> 今天天气信息没有获取到,你就看做是晴天吧,反正也没下雨
    </#if>
    <#if (user.car.sign)? exists>Hi,${user} </#if>
    
    <#-- 3.3 修改全局配置 -->
    <#setting classic_compatible=true>
    <h1> 小洋人的价钱是: ${price} 元 </h1>
    
    
    <#-- 4. 格式化输出-->
    
    <#-- 转成字符串显示 -->
    ${12.6?c} <br/>
    <#-- 按数字显示 -->
    ${ 123456 ?string.number} <br/>
    <#-- 按贷币格式显示 -->
    ${123456 ?string.currency} <br/>
    <#-- 按百分比格式显示 -->
    ${1.12 ?string.percent} <br/>
    
    <#--去空格 -->
    ${description ?trim} <br/>
    
    <#-- 格式化日期-->
    ${date?string("yyyy-MM-dd HH:mm:ss")} <br/>
    ${date?string("yyyyMMdd")} <br/>
    
    
    <#-- 5. 插入其他模块 -->
    <#--<#include "/mime.ftl">-->
    
    
    </body>
    </html>  
    

    相关文章

      网友评论

          本文标题:java生成pdf

          本文链接:https://www.haomeiwen.com/subject/xytkwhtx.html