flyingSauser是一个非常好的生成PDF的开源项目,在文章中http://tommy-lu.iteye.com/admin/blogs/2224190也有提到,下面就使用 maven + freemarker + flyingSauser来搭建一个生成PDF的一个web项目。
一、搭建maven项目
使用过maven的人对这个应该不陌生,我这里是使用的是MyEclipse搭建maven项目,MyEclipse很好的集成了maven,使用起来非常的方便。在本例中我的项目名称是:flyingSauser-generatePDF,项目结构如下:
二、编写初始化freemarker的类
新建完毕项目之后,下面就开始干活了,这里我新建了一个类叫WrapFreemarkerHttpServlet,这个类主要就是用来实例化freemarker的Configuration,该类还集成了HttpServlet,这样如果我们的servlet的需要使用freemarker功能,只要继承这个类就可以了,然后子类只需要在init的方法里面调用super.init();方法即可,这样就达到了再servlet的生命周期里面只需要进行一次初始化freemarker。WrapFreemarkerHttpServlet类如下:
package com.cn.luqingyun.common; import java.io.File; import java.io.IOException; import java.io.Writer; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import freemarker.core.ParseException; import freemarker.template.Configuration; import freemarker.template.DefaultObjectWrapper; import freemarker.template.MalformedTemplateNameException; import freemarker.template.Template; import freemarker.template.TemplateException; import freemarker.template.TemplateNotFoundException; public class WrapFreemarkerHttpServlet extends HttpServlet { private static final long serialVersionUID = 1L; private Configuration cfg; public void init() throws ServletException { try { //获取模板所在文件夹参数,这个参数在web.xml里面配置。 String freemarkerTemplatesFolder = this.getServletContext().getInitParameter("freemarkerTemplatesFolder"); /* 在整个应用的生命周期中,这个工作你应该只做一次。 */ /* 创建和调整配置。 */ cfg = new Configuration(Configuration.getVersion()); //获取模板文件夹路径 String freemarkerTemplatesUrl = this.getServletContext().getRealPath("/" + freemarkerTemplatesFolder); //让freemarker从指定文件夹加载模板文件 cfg.setDirectoryForTemplateLoading(new File(freemarkerTemplatesUrl)); cfg.setObjectWrapper(new DefaultObjectWrapper(Configuration .getVersion())); } catch (IOException e) { e.printStackTrace(); } } /** * 输出数据到模板,子类只需要调用该方法,即可将数据输出到模板中 * @param templateFileName 模板名称 * @param root 数据模型 * @param os * @throws IOException * @throws ParseException * @throws MalformedTemplateNameException * @throws TemplateNotFoundException * @throws TemplateException */ protected void process(String templateFileName, Map root,Writer os) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException, TemplateException { /* 获取或创建模板 */ Template temp = cfg.getTemplate(templateFileName,"UTF-8"); /* 将模板和数据模型合并 */ // 注意: // 为了简单起见,这里压制了异常(在方法签名中声明了异常,译者注),而在正式运行的产品中不要这样做。; temp.process(root, os); os.flush(); } }
三、编写生成PDF的servlet
该类继承了WrapFreemarkerHttpServlet,在init()方法里面调用了super.init();主要就是初始化 freemarker,这个类有一行代码非常重要,fontResolver.addFont("fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);这行代码是为了使flyingSauser支持中文,文件simsun.ttc需要放在类路径下面即可。该类的代码如下:
package com.cn.luqingyun.web; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.xhtmlrenderer.pdf.ITextFontResolver; import org.xhtmlrenderer.pdf.ITextRenderer; import org.xml.sax.SAXException; import com.cn.luqingyun.common.WrapFreemarkerHttpServlet; import com.itextpdf.text.DocumentException; import com.itextpdf.text.pdf.BaseFont; import freemarker.template.TemplateException; /** * 注意这里继承的是WrapFreemarkerHttpServlet,该类除了具有HttpServlet的功能<br> * 还有初始化freemarker的功能 * @author Tommy * */ public class PDFController extends WrapFreemarkerHttpServlet { private static final long serialVersionUID = 1L; public void init() throws ServletException { //初始化freemarker super.init(); } protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/pdf"); try { // 1.freemarker处理 // 创建数据模型 Map root = new HashMap(); root.put("user", "Flying Saucer介绍"); root.put("text", "Flying Saucer is an XML/CSS renderer, which means it takes XML files as input, applies formatting and styling using CSS, and generates a rendered representation of that XML as output. The output may go to the screen (in a GUI), to an image, or to a PDF file. Because we believe most people will be interested in re-using their knowledge of web layout, our main target for content is XHTML 1.0 (strict), an XML document format that standardizes HTML."); // 指定模板文件,因为在web.xml中已经指定freemarker加载的文件夹 ,所以这里只需要指定文件名即可 String templateFile = "pdfGenerate.ftl"; // 输出到/WEB-INF/PDF/pdfFile.html String pdfFileUrl = getServletContext().getRealPath( "/WEB-INF/PDF/pdfFile.html"); FileOutputStream fos = new FileOutputStream(new File(pdfFileUrl)); Writer out = new OutputStreamWriter(fos, "UTF-8"); // 将数据加载到模板文件 process(templateFile, root, out); // 2.从freemarker产生的html文件生产PDF DocumentBuilder builder = DocumentBuilderFactory.newInstance() .newDocumentBuilder(); InputStream is = new FileInputStream(new File(pdfFileUrl)); Document doc = builder.parse(is); ITextRenderer renderer = new ITextRenderer(); // 解决中文支持问题 ITextFontResolver fontResolver = renderer.getFontResolver(); fontResolver.addFont("fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); renderer.setDocument(doc, null); renderer.layout(); OutputStream os = response.getOutputStream(); renderer.createPDF(os); os.close(); } catch (TemplateException e) { e.printStackTrace(); } catch (DocumentException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } } }
这里的模板文件的文件也非常简单,代码如下:
<html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta> <title>flying sauser</title> <!--加这行代码的原因是,因为flyingSauser需要中文支持就必须要加,具体原因还不太清楚--> <style type="text/css"> body { font-family: SimSun; } </style> </head> <body> <h1> ${user} </h1> <p style="text-indent: 2em;"> ${text} </p> </body> </html>
四、实现效果
注意事项:编写模板的时候一定要有结束标签,例如<meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>这一行代码不能写成<meta http-equiv="content-type" content="text/html; charset=UTF-8">否则会报The element type "meta" must be terminated by the matching end-tag "</meta>".
需要完整项目的可以在评论区里面提供QQ号。很少写博客,有不足之处请多多之处,附件中提供了几个使用到的类,以及web.xml、simsun.ttc 以及pom.xml。