`
┿┅мīSS
  • 浏览: 95083 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Freemarker+Flying sauser +Itext 整合生成PDF

    博客分类:
  • J2EE
 
阅读更多

     Freemarker、Flying sauser 、Itext ,这三个框架的作用就不详细介绍了,google一下就知道了。

Itext提供了很多底层的API,让我们可以用java代码画一个pdf出来,但是很不灵活,布局渲染代码都hard code 进java类里面了。

当需求发生改变时,哪怕只需要更改一个属性名,我们都要重新修改那段代码,很不符合开放关闭的原则。想到用模版来做渲染,但自己实现起来比较繁琐,然后google了下,找到了用freemarker做模版,Flying sauser 照着模版做渲染,让Itext做输出生成PDF的方案。

 

   freemarker和itext都比较熟悉了,Flying sauser 第一次听说,看完官方的user guide(http://flyingsaucerproject.github.com/flyingsaucer/r8/guide/users-guide-R8.html)后,自己着手做了个demo实践:

 

   

    测试数据模型:

 

package com.jeemiss.pdfsimple.entity;

public class User {
	
	private String name;
	private int age;
	private int sex;
	
	/**
	 * Constructor with all fields
	 * 
	 * @param name
	 * @param age
	 * @param sex
	 */
	public User(String name, int age, int sex) {
		super();
		this.name = name;
		this.age = age;
		this.sex = sex;
	}
	
	///////////////////////   getter and setter   ///////////////////////////////////////////
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public int getSex() {
		return sex;
	}
	public void setSex(int sex) {
		this.sex = sex;
	}
	
	

}

 

 

package com.jeemiss.pdfsimple.freemarker;

import freemarker.template.Configuration;

public class FreemarkerConfiguration {
	
	private static Configuration config = null;
	
	/**
	 * Static initialization.
	 * 
	 * Initialize the configuration of Freemarker.
	 */
	static{
		config = new Configuration();
		config.setClassForTemplateLoading(FreemarkerConfiguration.class, "template");
	}
	
	public static Configuration getConfiguation(){
		return config;
	}

}

 

 HTML生成器:

 

package com.jeemiss.pdfsimple.generator;

import java.io.BufferedWriter;
import java.io.StringWriter;
import java.util.Map;
import com.jeemiss.pdfsimple.freemarker.FreemarkerConfiguration;
import freemarker.template.Configuration;
import freemarker.template.Template;

public class HtmlGenerator {
	
	/**
	 * Generate html string.
	 * 
	 * @param template   the name of freemarker teamlate.
	 * @param variables  the data of teamlate.
	 * @return htmlStr
	 * @throws Exception
	 */
	public static String generate(String template, Map<String,Object> variables) throws Exception{
		Configuration config = FreemarkerConfiguration.getConfiguation();
		Template tp = config.getTemplate(template);
		StringWriter stringWriter = new StringWriter();  
		BufferedWriter writer = new BufferedWriter(stringWriter);  
		tp.setEncoding("UTF-8");  
		tp.process(variables, writer);  
		String htmlStr = stringWriter.toString();
		writer.flush();  
		writer.close();
		return htmlStr;
	}

}

   PDF 生成器:

 

 

package com.jeemiss.pdfsimple.generator;

import java.io.ByteArrayInputStream;
import java.io.OutputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.xhtmlrenderer.pdf.ITextRenderer;

public class PdfGenerator {

	/**
	 * Output a pdf to the specified outputstream
	 * 
	 * @param htmlStr the htmlstr
	 * @param out the specified outputstream
	 * @throws Exception
	 */
	public static void generate(String htmlStr, OutputStream out)
			throws Exception {
		DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		Document doc = builder.parse(new ByteArrayInputStream(htmlStr.getBytes()));
		ITextRenderer renderer = new ITextRenderer();
		renderer.setDocument(doc, null);
		renderer.layout();
		renderer.createPDF(out);
		out.close();
	}

}

 

    用来做测试的ftl模版,用到部分css3.0的属性来控制pdf强制分页和输出分页信息

 

<html>
<head>
  <title>${title}</title>
  <style>
     table {
    		 width:100%;border:green dotted ;border-width:2 0 0 2
     }
     td {
    		 border:green dotted;border-width:0 2 2 0
     }
     @page {  
              size: 8.5in 11in; 
              @bottom-center {
    				content: "page " counter(page) " of  " counter(pages);
  			  }
     }
  </style>
</head>
<body>
  <h1>Just a blank page.</h1>
  <div style="page-break-before:always;">
      <div align="center">
	      <h1>${title}</h1>
	  </div>
	  <table>
	     <tr>
	        <td><b>Name</b></td>
	        <td><b>Age</b></td>
	        <td><b>Sex</b></td>
	     </tr>
	     <#list userList as user>
	        <tr>
		        <td>${user.name}</td>
		        <td>${user.age}</td>
		        <td>
		           <#if user.sex = 1>
		             	 male
		           <#else>
		              	 female
		           </#if>
		        </td>
	        </tr>
	     </#list>
	  </table>
  </div>
</body>
</html> 

 

最后写个测试用例看看:

 

package com.jeemiss.pdfsimple.test;

import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import com.jeemiss.pdfsimple.entity.User;
import com.jeemiss.pdfsimple.generator.HtmlGenerator;
import com.jeemiss.pdfsimple.generator.PdfGenerator;

public class TestCase 
{
   @Test
   public void generatePDF() {
	   try{
		   String outputFile = "C:\\sample.pdf";
		   Map<String,Object> variables = new HashMap<String,Object>();
		    
		   List<User> userList = new ArrayList<User>();
		   
		   User tom = new User("Tom",19,1);
		   User amy = new User("Amy",28,0);
		   User leo = new User("Leo",23,1);
		   
		   userList.add(tom);
		   userList.add(amy);
		   userList.add(leo);
		   
		   variables.put("title", "User List");
		   variables.put("userList", userList);
		  
		   String htmlStr = HtmlGenerator.generate("sample.ftl", variables);
		   
		   OutputStream out = new FileOutputStream(outputFile);
		   PdfGenerator.generate(htmlStr, out);
		   
	   }catch(Exception ex){
		   ex.printStackTrace();
	   }
	   
   }
}

 

到C盘下打开sample.pdf ,看看输出的结果:

 

 

分享到:
评论
7 楼 xiefuzhong 2013-10-17  
怎么解决中文问题,
运行程序,中午不能显示,请解答,谢谢
6 楼 294676001 2012-07-31  
不支持中文 
5 楼 smith789 2012-03-01  
另外你的HtmlGenerator.java里没有设置Object wrappers,怎么能解析User成功?

报错:
Java backtrace for programmers:
----------
freemarker.template.TemplateModelException: Don't know how to present an object of this type to a template: com.jeemiss.pdfsimple.entity.User

加上下面一行就行了。
config.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);

来自Freemarker官方:
The most important ObjectWrapper implementations that the FreeMarker core provides are:

ObjectWrapper.DEFAULT_WRAPPER: It replaces String with SimpleScalar, Number with SimpleNumber, List and array with SimpleSequence, Map with SimpleHash, Boolean with TemplateBooleanModel.TRUE or TemplateBooleanModel.FALSE, W3C DOM nodes with freemarker.ext.dom.NodeModel. For Jython objects, this wrapper will invoke freemarker.ext.jython.JythonWrapper. For all other objects, it will invoke BEANS_WRAPPER.

ObjectWrapper.BEANS_WRAPPER: It can expose java Bean properties and other members of arbitrary objects using Java reflection. At least in FreeMarker 2.3, it is a freemarker.ext.beans.BeansWrapper instance; there is a separated chapter about it.
4 楼 smith789 2012-03-01  
String htmlStr = stringWriter.toString();
放到writer.close();后面吧,报错:

[Fatal Error] :1:1: Premature end of file.
org.xml.sax.SAXParseException: Premature end of file.
3 楼 wuhacoffee 2011-12-06  
非常感谢楼主的提供。。。正学习着呢!!

建议楼主把代码分享出来给大家。。。
2 楼 simulatestar 2011-11-09  
你好,请问,为何我按你这个例子做出来,以后报错呢
1 楼 yindashan 2011-10-09  
谢谢分享,

相关推荐

Global site tag (gtag.js) - Google Analytics