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>  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容