java PDF 生成方案

iText

其实Jasper Report是基于iText的。于是有的人会说,那么直接使用iText不是一种倒退么?的确,直接使用iText似乎就需要直接使用原生的API进行编程了。不过幸好iText其实提供了一些方便的API,通过使用这些API,我们可以直接将HTML代码转化成iText可识别的Document对象,从而导出PDF文档。

Java代码 复制代码

  1. import java.io.FileOutputStream;   
  2. import java.io.FileReader;   
  3. import java.util.ArrayList;   
  4.   
  5. import com.lowagie.text.Document;   
  6. import com.lowagie.text.Element;   
  7. import com.lowagie.text.html.simpleparser.HTMLWorker;   
  8. import com.lowagie.text.html.simpleparser.StyleSheet;   
  9. import com.lowagie.text.pdf.PdfWriter;   
  10.   
  11. public class MainClass {   
  12.   public static void main(String[] args) throws Exception {   
  13.     Document document = new Document();   
  14.     StyleSheet st = new StyleSheet();   
  15.     st.loadTagStyle("body""leading""16,0");   
  16.     PdfWriter.getInstance(document, new FileOutputStream("html2.pdf"));   
  17.     document.open();   
  18.     ArrayList p = HTMLWorker.parseToList(new FileReader("example.html"), st);   
  19.     for (int k = 0; k < p.size(); ++k)   
  20.       document.add((Element) p.get(k));   
  21.     document.close();   
  22.   }   
  23. }  

Java代码  收藏代码

  1. import java.io.FileOutputStream;  
  2. import java.io.FileReader;  
  3. import java.util.ArrayList;  
  4.   
  5. import com.lowagie.text.Document;  
  6. import com.lowagie.text.Element;  
  7. import com.lowagie.text.html.simpleparser.HTMLWorker;  
  8. import com.lowagie.text.html.simpleparser.StyleSheet;  
  9. import com.lowagie.text.pdf.PdfWriter;  
  10.   
  11. public class MainClass {  
  12.   public static void main(String[] args) throws Exception {  
  13.     Document document = new Document();  
  14.     StyleSheet st = new StyleSheet();  
  15.     st.loadTagStyle("body", "leading", "16,0");  
  16.     PdfWriter.getInstance(document, new FileOutputStream("html2.pdf"));  
  17.     document.open();  
  18.     ArrayList p = HTMLWorker.parseToList(new FileReader("example.html"), st);  
  19.     for (int k = 0; k < p.size(); ++k)  
  20.       document.add((Element) p.get(k));  
  21.     document.close();  
  22.   }  
  23. }  



这是从网上找到的一个例子。从代码中,我们可以看到,iText本身提供了一个简单的HTML的解析器,它可以把HTML转化成我们需要的PDF的document。

有了这个东西,基本上我的目标就能达成一大半了。接下来我的任务就是根据实际情况去编写HTML代码,然后扔进这个方法,就OK了。而真正的HTML代码,我们则可以在这里使用真正的模板技术,Freemarker或者Velocity去生成我们所需要的内容。当然,这已经是我们熟门熟路的东西了。

正当我觉得这个方案基本能符合我的要求的时候,我也同样找到了它的很多弱项:

1. 无法识别很多HTML的tag和attribute(应该是iText的HTMLParser不够强大)
2. 无法识别CSS


如果说第一点我还可以勉强接受的话,那么第二点我就完全不能接受了。无法识别简单的CSS,就意味着HTML失去了最基本的活力,也无法根据实际要求调整样式。

所以这种方案也必然无法成为我的方案。

flying sauser

在这种情况下,我几乎已经燃起了自己编写一个支持CSS解析的HTML Parser的想法。幸好,在一个非常偶然的情况下,我在google中搜到了这样一个开源项目,它能够满足我的一切需求。这就是flying sauser,项目主页是:https://xhtmlrenderer.dev.java.net/

项目的首页非常吸引人:An XML/XHTML/CSS 2.1 Renderer。这不正是我要的东西么?

仔细再看里面的文档:

引用

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.



完美了。这东西能解析HTML和CSS,而且能输出成image,PDF等格式。哇!我们来看看sample代码(代码丑陋,不过已经能说明问题了):

Java代码 复制代码

  1. /*   
  2. * ITextRendererTest.java *   
  3. * Copyright 2009 Shanghai TuDou.    
  4. * All rights reserved.   
  5. */  
  6.   
  7. package itext;   
  8.   
  9. import java.io.File;   
  10. import java.io.FileOutputStream;   
  11. import java.io.OutputStream;   
  12.   
  13. import org.xhtmlrenderer.pdf.ITextFontResolver;   
  14. import org.xhtmlrenderer.pdf.ITextRenderer;   
  15.   
  16. import com.lowagie.text.pdf.BaseFont;   
  17.   
  18. /**   
  19.  * TODO class description *   
  20.  *  
  21.  * @author pcwang  
  22.  *  
  23.  * @version 1.0, 上午11:03:26  create $Id$  
  24.  */  
  25. public class ITextRendererTest {   
  26.     public static void main(String[] args) throws Exception {   
  27.         String inputFile = "conf/template/test.html";   
  28.         String url = new File(inputFile).toURI().toURL().toString();   
  29.         String outputFile = "firstdoc.pdf";   
  30.         OutputStream os = new FileOutputStream(outputFile);   
  31.         ITextRenderer renderer = new ITextRenderer();   
  32.         renderer.setDocument(url);   
  33.   
  34.         // 解决中文支持问题   
  35.         ITextFontResolver fontResolver = renderer.getFontResolver();   
  36.         fontResolver.addFont("C:/Windows/Fonts/arialuni.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);   
  37.   
  38.         // 解决图片的相对路径问题   
  39.         renderer.getSharedContext().setBaseURL("file:/D:/Work/Demo2do/Yoda/branch/Yoda%20-%20All/conf/template/");   
  40.            
  41.         renderer.layout();   
  42.         renderer.createPDF(os);   
  43.            
  44.         os.close();   
  45.     }   
  46. }  

Java代码  收藏代码

  1. /*  
  2. * ITextRendererTest.java *  
  3. * Copyright 2009 Shanghai TuDou.   
  4. * All rights reserved.  
  5. */  
  6.   
  7. package itext;  
  8.   
  9. import java.io.File;  
  10. import java.io.FileOutputStream;  
  11. import java.io.OutputStream;  
  12.   
  13. import org.xhtmlrenderer.pdf.ITextFontResolver;  
  14. import org.xhtmlrenderer.pdf.ITextRenderer;  
  15.   
  16. import com.lowagie.text.pdf.BaseFont;  
  17.   
  18. /**  
  19.  * TODO class description *  
  20.  * 
  21.  * @author pcwang 
  22.  * 
  23.  * @version 1.0, 上午11:03:26  create $Id$ 
  24.  */  
  25. public class ITextRendererTest {  
  26.     public static void main(String[] args) throws Exception {  
  27.         String inputFile = "conf/template/test.html";  
  28.         String url = new File(inputFile).toURI().toURL().toString();  
  29.         String outputFile = "firstdoc.pdf";  
  30.         OutputStream os = new FileOutputStream(outputFile);  
  31.         ITextRenderer renderer = new ITextRenderer();  
  32.         renderer.setDocument(url);  
  33.   
  34.         // 解决中文支持问题  
  35.         ITextFontResolver fontResolver = renderer.getFontResolver();  
  36.         fontResolver.addFont("C:/Windows/Fonts/arialuni.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);  
  37.   
  38.         // 解决图片的相对路径问题  
  39.         renderer.getSharedContext().setBaseURL("file:/D:/Work/Demo2do/Yoda/branch/Yoda%20-%20All/conf/template/");  
  40.           
  41.         renderer.layout();  
  42.         renderer.createPDF(os);  
  43.           
  44.         os.close();  
  45.     }  
  46. }  



运行,成功!实在太简单了!API帮你完成了一切!

有了这个东西,我们就可以将PDF的生成流程变成这样:

1) 编写Freemarker或者Velocity模板,打造HTML,勾画PDF的样式(请任意使用CSS)

2) 在你的业务逻辑层引入Freemarker的引擎或者Velocity的引擎,并将业务逻辑层中可以获取的数据和模板,使用引擎生成最终的内容

3) 将我上面的sample代码做简单封装后,调用,生成PDF


这样,我想作为一个web程序员来说,上面的3点,都不会成为你的绊脚石。你可以轻松驾驭PDF了。

在Flying Saucer的官方文档中,有一些Q&A,可以解决读者们大部分的问题。包括PDF的字体、PDF的格式、Image如何处理等等。大家可以尝试着去阅读。

猜你喜欢

转载自blog.csdn.net/fygkchina/article/details/8861563