java生成pdf

简述
生成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>  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,427评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,551评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,747评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,939评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,955评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,737评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,448评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,352评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,834评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,992评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,133评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,815评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,477评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,022评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,147评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,398评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,077评论 2 355

推荐阅读更多精彩内容