简述
生成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>
网友评论