很多时候,我们在项目中会碰到报表用pdf形式的方式展示,特别是在企业应用中,碰到的比较多的PDF的需求,可能是针对某个比较典型的具备文档特性的内容,导出成为PDF进行存档。由于我们在实际开发过程中往往使用一些开源框架,诸如ssh,ssi来构建我们的应用,所以我们相对熟悉的方案是针对具体的业务逻辑设计实体,使用开源框架来实现我们的业务逻辑。而PDF的导出,最好不要破坏现有的程序框架,甚至能复用我们业务逻辑层的代码,尽量做到高内聚低耦合的java编程规范。因为如果把PDF作为一种特殊的表现形式的话,实际上它有点类似模板。最佳的情况,是我们能够通过编写某种模板,把PDF的大概样子确定下来,然后把数据和模板做一次整合,得到最后的结果,此文就是在这样的背景下提出的解决方案。
flying sauser,一种基于iText自身的问题,让我们开始寻求可以真正意义上模版技术,那么flying sauser就是为了这个问题出现的产品,能解析HTML和CSS,而且能输出成image,PDF等格式。
那么我们如何去做呢,很简单,只需要如下几步:
1.引入所需要的jar包;
2.通过配置将velocity托管给spring;
3.使用velocity生成html代码;
4. 利用flying sauser生成pdf;
下面就让我们来看看代码:
1.在spring配置文件中配置模版对象:<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean"> <property name="velocityProperties"> <props> <prop key="resource.loader">class</prop> <prop key="class.resource.loader.class">org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader</prop> <prop key="velocimacro.library"></prop> </props> </property> </bean>
2.使用velocity生成html源码;
/** * * @Title: getHtmlCodeByVelocity * @Description: 使用velocity生成HTML源码 * @param templatePath 模版存放路径(建议在WebRoot下创建一个目录,传递如../../template/sjy_zjcj_pdf.vm) * @param dataMap 组装数据 * @param velocityEnginespring托管对象,请注入到servicd * @return * String * @throws * * @Author dyp * @Create Date: 2014-4-17 下午04:18:48 */ private String getHtmlCodeByVelocity(String templatePath,Map<String, Object> dataMap) throws Exception{ String htmlCode = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, templatePath, "UTF-8", dataMap); return htmlCode; }
3.WebRoot/ template/jdy_cjtz_pdf.vm 模版;<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <style type="text/css" mce_bogus="1">body {font-family: SimSun;}</style> <title>无标题文档</title> <style type="text/css"> @page{ size:A4; #** @top-center { width:650px; height:100px; background:url(header1.png) no-repeat center 2px ; /*border-bottom: solid 0.01mm #000; width:650px; height:100px; padding-bottom:50px; background:url(header1.png) center no-repeat;*/ } @bottom-right{ width:500px; height:45px; background:url(footer.png) no-repeat right; } *# } body {font-size:12px;background: #fff;color: #888888;} html,body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;} table{border-collapse:collapse;border-spacing:0;} .content{ width:650px; margin:0 auto; margin-top:20px; background:#fff;} h2{ height:56px; line-height:56px; font-size:22px; text-align:center; color:#000; font-weight:normal;} h3{ text-align:center; font-weight:normal;} .hello{ padding:10px; color:#000; font-size:12px; line-height:32px;} .hello p span{text-decoration:underline;} .table-t{ text-align:center;} .table-t tr td{ border:1px solid #000;} .company{ width:260px; height:170px; margin:30px 0 0 400px;} #** 印章 .company{ width:260px; height:170px; margin:30px 0 0 400px; background:url(yz.png) no-repeat} #* .footer{ text-align:right; padding:10px;} .c-name{ color:#000; font-size:14px; font-weight:bold;} </style> </head> <body> <div class="content"> <h2>XXXXXX单</h2> <div class="hello"> <p>尊敬的 <span> ${user_name} </span>先生/女士,您好!<br/></p> <p style="text-indent: 2em;">XXXXXX有限公司(以下简称“XXXXXX”)XXXXXXXXXXXXXXXXX<br/>让获取了如下XXXXXXX,XXXXXXXXX信息如下:<br/></p> <p>货币单位:人民币(元)</p> <table class="table-t" width="630" border="0"> <tr> <td>XXXXXXXX</td> <td>XXXXXXXX</td> <td>XXXXXXXX</td> <td>XXXXXXXX</td> <td>XXXXXXXX</td> <td>XXXXXXXX</td> <td>XXXXXXXX</td> </tr> <tr> <td>${agreement_no}</td> <td>${loan_num}</td> <td>${initial_loan_date}</td> <td>${product}</td> <td>${real_loan_amount}</td> <td>${close_period_on_amount}</td> <td>${close_period_end_date}</td> </tr> </table> <p>注:XXXXXXXX最后一自然日。XXXXXXXX将于XXXXXXX后5个工作日内为您寄送或发送《XXXXXXXXXX报告》。<br/><br/></p> <h3>XXXXXXXX列表</h3> <table class="table-t" width="630" border="0"> <tr> <td>XXXXXXXXXX编号</td> <td>XXXXX<br/>姓名</td> <td>XXXXXXX份证号码</td> <td>XXXXX金额</td> <td>XXXX职业<br/>情况</td> <td>XXXX<br/>用途</td> <td>备注</td> <td>XXXXX期限<br/>(月)</td> <td>XXXXX<br/>月数</td> </tr> #foreach($condition in $borrower_list) <tr> <td>$condition.get("loan_agreement_num")</td> <td>$condition.get("real_name")</td> <td>$condition.get("id_card_no")</td> <td>$condition.get("loan_amount")</td> <td>$condition.get("occupation")</td> <td>$condition.get("loan_purpose")</td> <td>$condition.get("remark")</td> <td>$condition.get("repayment_period")</td> <td>$condition.get("surplus_repayment_period")</td> </tr> #end #if($borrower_list.size() > 0) <tr> <td colspan="3">合计</td> <td>$borrower_list.get(0).get("total_amount")</td> <td colspan="5"> </td> </tr> #else <tr> 无 </tr> #end </table> <p class="company">XXXXXXXXXXXXXXXXX有限公司<br/>盖章:<br/>日期:${stamp_date}</p> </div> </div> </body> </html>
4.根据html源码通过flying sauser生成pdf;
/** * * @Title: doPdfByFS * @Description: 使用Flying sauser生成PDF * @param htmlCode HTML源码 * @param outPath 文件保存路径 * @param fileName 文件保存名称 * void * @throws * * @Author dyp * @Create Date: 2014-4-17 下午04:19:36 */ private void doPdfByFS(String htmlCode,String outPath, String fileName) throws Exception{ String fontFamily = PropertyUtil.getInfo("FONT_FAMILY"); //字体存放路径 String imgPath = PropertyUtil.getInfo("IMG_PATH");; //图片存放路径 OutputStream os = new FileOutputStream(outPath + fileName); ITextRenderer renderer = new ITextRenderer(); ITextFontResolver fontResolver = renderer.getFontResolver(); fontResolver.addFont(fontFamily, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); // DOCTYPE 必需写否则类似于 这样的字符解析会出现错误 renderer.setDocumentFromString(htmlCode); // 解决图片的相对路径问题 renderer.getSharedContext().setBaseURL(imgPath); renderer.layout(); renderer.createPDF(os); os.close(); <span style="font-size:14px;">}</span>
5.在这里我们也将使用velocity生成静态页面的方法放置于此:
以上就是生成pdf的源码, jar在此下载,如有披露请多多指教,对了,velocity引入css文件本人还没找到好的方法,如果那位大神有用过,请留言。private String doHtmlByVelocity(String htmlCode,String outPath, String fileName){ String path = outPath + fileName; File file = new File(path); if(file.exists()) file.mkdirs(); try { Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path), "UTF-8")); // BufferedWriter o = new BufferedWriter(new FileWriter(file),""); out.write(htmlCode); out.close(); } catch (Exception e) { e.printStackTrace(); return ""; } return ""; }