java根据模板动态生成PDF实例
一、需求说明:
根据业务需要,需要在服务器端生成可动态配置的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地址:
八、遇到的坑:
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>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。