1. 程式人生 > >java PDF 生成方案

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如何處理等等。大家可以嘗試著去閱讀。