java根据模板HTML动态生成PDF
原文:https://segmentfault.com/a/1190000009160184
一、需求说明:根据业务需要,需要在服务器端生成可动态配置的PDF文档,方便数据可视化查看。
二、解决方案:
iText+FreeMarker+JFreeChart生成可动态配置的PDF文档
iText有很强大的PDF处理能力,但是样式和排版不好控制,直接写PDF文档,数据的动态渲染很麻烦。
FreeMarker能配置动态的html模板,正好解决了样式、动态渲染和排版问题。
JFreeChart有这方便的画图API,能画出简单的折线、柱状和饼图,基本能满足需要。
三、实现功能:
1、能动态配置PDF文档内容
2、支持中文字体显示的动态配置
3、设置自定义的页眉页脚信息
4、能动态生成业务图片
5、完成PDF的分页和图片的嵌入
四、主要代码结构说明:
1、component包:PDF生成的组件 对外提供的是PDFKit工具类和HeaderFooterBuilder接口,其中PDFKit负责PDF的生成,HeaderFooterBuilder负责自定义页眉页脚信息。
2、builder包:负责PDF模板之外的额外信息填写,这里主要是页眉页脚的定制。
3、chart包:JFreeChart的画图工具包,目前只有一个线形图。
4、test包:测试工具类
5、util包:FreeMarker等工具类。
五、关键代码说明:
1、模板配置
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta http-equiv="Content-Style-Type" content="text/css"/>
<title></title>
<style type="text/css">
body {
font-family: pingfang sc light;
}
.center{
text-align: center;
width: 100%;
}
</style>
</head>
<body>
<!--第一页开始-->
<div class="page" >
<div class="center"><p>${templateName}</p></div>
<div><p>iText官网:${ITEXTUrl}</p></div>
<div><p>FreeMarker官网:${freeMarkerUrl}</p></div>
<div><p>JFreeChart教程:${JFreeChartUrl}</p></div>
<div>列表值:</div>
<div>
<#list scores as item>
<div><p>${item}</p></div>
</#list>
</div>
</div>
<!--第一页结束-->
<!---分页标记-->
<span style="page-break-after:always;"></span>
<!--第二页开始-->
<div class="page">
<div>第二页开始了</div>
<!--外部链接-->
<p>百度图标</p>
<div>
<img src="${imageUrl}" alt="百度图标" width="270" height="129"/>
</div>
<!--动态生成的图片-->
<p>气温变化对比图</p>
<div>
<img src="${picUrl}" alt="我的图片" width="500" height="270"/>
</div>
</div>
<!--第二页结束-->
</body>
</html>
2、获取模板内容并填充数据
/**
* @description 获取模板
*/
public static String getContent(String fileName,Object data){
String templatePath=getPDFTemplatePath(fileName);//根据PDF名称查找对应的模板名称
String templateFileName=getTemplateName(templatePath);
String templateFilePath=getTemplatePath(templatePath);
if(StringUtils.isEmpty(templatePath)){
throw new FreeMarkerException("templatePath can not be empty!");
}
try{
Configuration config = new Configuration(Configuration.VERSION_2_3_25);//FreeMarker配置
config.setDefaultEncoding("UTF-8");
config.setDirectoryForTemplateLoading(new File(templateFilePath));//注意这里是模板所在文件夹,不是文件
config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
config.setLogTemplateExceptions(false);
Template template = config.getTemplate(templateFileName);//根据模板名称 获取对应模板
StringWriter writer = new StringWriter();
template.process(data, writer);//模板和数据的匹配
writer.flush();
String html = writer.toString();
return html;
}catch (Exception ex){
throw new FreeMarkerException("FreeMarkerUtil process fail",ex);
}
}
3、导出模板到PDF文件
/**
* @description 导出pdf到文件
* @param fileName 输出PDF文件名
* @param data 模板所需要的数据
*
*/
public String exportToFile(String fileName,Object data){
String htmlData= FreeMarkerUtil.getContent(fileName, data);//获取FreeMarker的模板数据
if(StringUtils.isEmpty(saveFilePath)){
saveFilePath=getDefaultSavePath(fileName);//设置PDF文件输出路径
}
File file=new File(saveFilePath);
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
FileOutputStream outputStream=null;
try{
//设置输出路径
outputStream=new FileOutputStream(saveFilePath);
//设置文档大小
Document document = new Document(PageSize.A4);//IText新建PDF文档
PdfWriter writer = PdfWriter.getInstance(document, outputStream);//设置文档和输出流的关系
//设置页眉页脚
PDFBuilder builder = new PDFBuilder(headerFooterBuilder,data);
builder.setPresentFontSize(10);
writer.setPageEvent(builder);
//输出为PDF文件
convertToPDF(writer,document,htmlData);
}catch(Exception ex){
throw new PDFException("PDF export to File fail",ex);
}finally{
IOUtils.closeQuietly(outputStream);
}
return saveFilePath;
}
4、测试工具类
public String createPDF(Object data, String fileName){
//pdf保存路径
try {
//设置自定义PDF页眉页脚工具类
PDFHeaderFooter headerFooter=new PDFHeaderFooter();
PDFKit kit=new PDFKit();
kit.setHeaderFooterBuilder(headerFooter);
//设置输出路径
kit.setSaveFilePath("/Users/fgm/Desktop/pdf/hello.pdf”);//设置出书路径
String saveFilePath=kit.exportToFile(fileName,data);
return saveFilePath;
} catch (Exception e) {
log.error("PDF生成失败{}", ExceptionUtils.getFullStackTrace(e));
return null;
}
}
public static void main(String[] args) {
ReportKit360 kit=new ReportKit360();
TemplateBO templateBO=new TemplateBO();//配置模板数据
templateBO.setTemplateName("Hello iText! Hello freemarker! Hello jFreeChart!");
templateBO.setFreeMarkerUrl("http://www.zheng-hang.com/chm/freemarker2_3_24/ref_directive_if.html");
templateBO.setITEXTUrl("http://developers.itextpdf.com/examples-itext5");
templateBO.setJFreeChartUrl("http://www.yiibai.com/jfreechart/jfreechart_referenced_apis.html");
templateBO.setImageUrl("https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png");
List<String> scores=new ArrayList<String>();
scores.add("90");
scores.add("95");
scores.add("98");
templateBO.setScores(scores);
List<Line> lineList=getTemperatureLineList();
TemperatureLineChart lineChart=new TemperatureLineChart();
String picUrl=lineChart.draw(lineList,0);//自定义的数据画图
templateBO.setPicUrl(picUrl);
String path= kit.createPDF(templateBO,"hello.pdf");
System.out.println(path);
}
六、生成效果图:
七、项目完整代码
1、github地址:https://github.com/superad/pdf-kit
2、项目git地址:git@github.com:superad/pdf-kit.git
八、遇到的坑:
1、FreeMarker配置模板文件样式,在实际PDF生成过程中,可能会出现一些不一致的情形,目前解决方法,就是换种方式调整样式。
2、字体文件放在resource下,在打包时会报错,运行mvn -X compile 会看到详细错误:
这是字体文件是二进制的,而maven项目中配置了资源文件的过滤,不能识别二进制文件导致的,
plugins中增加下面这个配置就好了:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<!--增加的配置,过滤ttf文件的匹配-->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<configuration>
<encoding>UTF-8</encoding>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>ttf</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
</plugins>
</build>
3、PDF分页配置:
在ftl文件中,增加分页标签: <span style="page-break-after:always;"></span>
九、 完整maven配置:
<!--pdf生成 itext-->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.4.2</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.4.1</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.0.3</version>
</dependency>
<!--freemarker-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.26-incubating</version>
</dependency>
<!--jfreechart-->
<dependency>
<groupId>jfreechart</groupId>
<artifactId>jfreechart</artifactId>
<version>1.0.0</version>
</dependency>
<!--log-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.0.13</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>1.0.13</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>
<!--util-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.14.8</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
java根据模板HTML动态生成PDF的更多相关文章
- 利用Java动态生成 PDF 文档
利用Java动态生成 PDF 文档,则需要开源的API.首先我们先想象需求,在企业应用中,客户会提出一些复杂的需求,比如会针对具体的业务,构建比较典型的具备文档性质的内容,一般会导出PDF进行存档.那 ...
- Java 动态生成 PDF 文件
每片文章前来首小诗: 今日夕阳伴薄雾,印着雪墙笑开颜.我心仿佛出窗前,浮在半腰望西天. --泥沙砖瓦浆木匠 需求: 项目里面有需要java动态生成 PDF 文件,提供下载.今天我找了下有关了,系 ...
- java根据pdf模版动态生成pdf
java根据pdf模版动态生成pdf package com.utils; import java.io.ByteArrayOutputStream; import java.io.File; imp ...
- django 动态生成PDF文件
可以通过开源的Python PDF库ReportLab来实现PDF文件的动态生成. 一.安装ReportLab ReportLab库在PyPI上提供,可以使用pip来安装: $ pip install ...
- 第二章:视图层 - 10:动态生成PDF文件
可以通过开源的Python PDF库ReportLab来实现PDF文件的动态生成. 一.安装ReportLab ReportLab库在PyPI上提供,可以使用pip来安装: $ pip install ...
- 在Java代码中使用iTextPDF生成PDF
1. 生成PDF 载入字体 static { FontFactory.register("/fonts/msyh.ttf"); FontFactory.register(" ...
- 使用VTemplate模板引擎动态生成订单流程图
1.VTemplate模板引擎的简介 VTemplate模板引擎也简称为VT,是基于.NET的模板引擎,它允许任何人使用简单的类似HTML语法的模板语言来引用.NET里定义的对象.当VTemplate ...
- java应用maven插件动态生成webservice代码
pom.xml如下 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www ...
- freemarker动态生成word并将生成的word转为PDF,openoffice转换word乱码
之前项目有个需求,需要先动态生成word内容,然后再预览生成word的内容(不能修改).整理一下,方便以后使用. 网上参考了好多大神的博客.具体也忘了参考谁的了,如有侵权,请告知修改. 思路一: 将目 ...
随机推荐
- WPF的EventAggregator的发布和订阅
EventAggregator是Prism中专门处理ViewModel与ViewModel之间事件传递的类对象,它提供了针对事件的发布方法和订阅方法,所以可以非常方便的来管理事件.下面分几步来实现相关 ...
- Java中获取包含变量的配置文件config.properties内容
应用场景 有些时候项目中会用到很多路径,并且很可能多个路径在同一个根目录下,那为了方便配置的修改,达到只修改根目录即可达到一改全改的效果,此时就会想到要是有变量就好了: 另外有时候路径中的文件名是不确 ...
- ntpdate时间同步
为什么需要网络同步时间: Linux服务器运行久时,系统时间就会存在一定的误差,一般情况下可以使用date命令进行时间设置,但在做数据库集群分片等操作时对多台机器的时间差是有要求的,此时就需要使用nt ...
- JS跨域设置和取Cookie
在Javascript脚本里,一个cookie 实际就是一个字符串属性.当你读取cookie的值时,就得到一个字符串,里面当前WEB页使用的所有cookies的名称和值.每个cookie除了 name ...
- BZOJ 1086 王室联邦 | BFS
BZOJ 1086 王室联邦 题意 把一棵树分块,每块大小在[B, 3B]之间(B由输入数据给出),每个块需要对应一个核心点,核心点可以在块内,这个点要满足块内每个点到核心点的路径上的点都属于这个块( ...
- WebView图片点击查看
需求:WebView中的图片点击后放大全屏查看 this.setWebViewClient(new WebViewClient() { @Override public boolean shouldO ...
- ZOJ Monthly, March 2018 题解
[题目链接] A. ZOJ 4004 - Easy Number Game 首先肯定是选择值最小的 $2*m$ 进行操作,这些数在操作的时候每次取一个最大的和最小的相乘是最优的. #include & ...
- 标签传播算法(llgc 或 lgc)
动手实践标签传播算法 复现论文:Learning with Local and Global Consistency1 lgc 算法可以参考:DecodePaper/notebook/lgc 初始化算 ...
- jstat命令总结
jvm统计信息监控工具 一. jstat是什么 jstat是JDK自带的一个轻量级小工具.全称"Java Virtual Machine statistics monitoring tool ...
- [转]c++优先队列(priority_queue)用法详解
既然是队列那么先要包含头文件#include <queue>, 他和queue不同的就在于我们可以自定义其中数据的优先级, 让优先级高的排在队列前面,优先出队 优先队列具有队列的所有特性, ...