Javahtml
CSSC#C++Cfreemarker java
在Java世界,要想生成PDF,方案很多。最近一直在和這個東西打交道,因此簡單作一個小結吧。
在此以前,先來勾畫一下我心中比較理想的一個解決方案。在企業應用中,碰到的比較多的PDF的需求,多是針對某個比較典型的具有文檔特性的內容,導出成爲PDF進行存檔。因爲咱們如今每每使用一些開源框架,諸如ssh來構建咱們的應用,因此咱們相對熟悉的方案是針對具體的業務邏輯設計實體,使用開源框架來實現咱們的業務邏輯。而PDF的導出,最好不要破壞現有的程序框架,甚至能複用咱們業務邏輯層的代碼。由於若是把PDF做爲一種特殊的表現形式的話,實際上它有點相似模板。最佳的狀況,是咱們可以經過編寫某種模板,把PDF的大概樣子肯定下來,而後把數據和模板作一次整合,獲得最後的結果
帶着這個目標,開始在網上搜索解決方案。也找到了一些方案,下面簡單小結一下:
Jasper Report
看到的市面上採用的最多的方案,是Jasper Report。相關的文檔也不少,不過很雜,須要徹底掌握,我認爲仍是有些坡度和時間的。這個時間和坡度我認爲主要來自於對iReport這個IDE的反覆嘗試,對裏面的每一個屬性的摸索。
Jasper Report的設計思路,自己是不違反我上面所說的初衷的。由於咱們的努力方向是先生成模板,而後獲得數據,最後將二者整合獲得結果。可是Jasper Report的問題在於,其生成模板的方式過於複雜,即便有IDE的幫助,咱們仍是須要對其中的衆多規則有所瞭解才行,不然就會給調試帶來極大的麻煩。
因此,我認爲Jasper Report是一個半調子方案,這種強依賴於IDE進行可視化編輯的方式令我很不爽。同時,由此帶來的諸多的限制,相信也讓不少使用者頗爲頭疼。在經歷了一番痛苦的掙扎後,決定放棄使用這種方案。
iText
其實Jasper Report是基於iText的。因而有的人會說,那麼直接使用iText不是一種倒退麼?的確,直接使用iText彷佛就須要直接使用原生的API進行編程了。不過幸虧iText其實提供了一些方便的API,經過使用這些API,咱們能夠直接將HTML代碼轉化成iText可識別的Document對象,從而導出PDF文檔。
程序員
import java.io.FileOutputStream; import java.io.FileReader; import java.util.ArrayList; import com.lowagie.text.Document; import com.lowagie.text.Element; import com.lowagie.text.html.simpleparser.HTMLWorker; import com.lowagie.text.html.simpleparser.StyleSheet; import com.lowagie.text.pdf.PdfWriter; public class MainClass { public static void main(String[] args) throws Exception { Document document = new Document(); StyleSheet st = new StyleSheet(); st.loadTagStyle("body", "leading", "16,0"); PdfWriter.getInstance(document, new FileOutputStream("html2.pdf")); document.open(); ArrayList p = HTMLWorker.parseToList(new FileReader("example.html"), st); for (int k = 0; k < p.size(); ++k) document.add((Element) p.get(k)); document.close(); } }
import java.io.FileOutputStream; web
import java.io.FileReader; 編程
import java.util.ArrayList; app
import com.lowagie.text.Document; 框架
import com.lowagie.text.Element; ssh
import com.lowagie.text.html.simpleparser.HTMLWorker; 字體
import com.lowagie.text.html.simpleparser.StyleSheet; google
import com.lowagie.text.pdf.PdfWriter;
public class MainClass {
public static void main(String[] args) throws Exception {
Document document = new Document();
StyleSheet st = new StyleSheet();
st.loadTagStyle("body", "leading", "16,0");
PdfWriter.getInstance(document, new FileOutputStream("html2.pdf"));
document.open();
ArrayList p = HTMLWorker.parseToList(new FileReader("example.html"), st);
for (int k = 0; k < p.size(); ++k)
document.add((Element) p.get(k));
document.close();
}
}
這是從網上找到的一個例子。從代碼中,咱們能夠看到,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代碼(代碼醜陋,不過已經能說明問題了):
/* * ITextRendererTest.java * * Copyright 2009 Shanghai TuDou. * All rights reserved. */ package itext; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; import org.xhtmlrenderer.pdf.ITextFontResolver; import org.xhtmlrenderer.pdf.ITextRenderer; import com.lowagie.text.pdf.BaseFont; /** * TODO class description * * * @author pcwang * * @version 1.0, 上午11:03:26 create $Id$ */ public class ITextRendererTest { public static void main(String[] args) throws Exception { String inputFile = "conf/template/test.html"; String url = new File(inputFile).toURI().toURL().toString(); String outputFile = "firstdoc.pdf"; OutputStream os = new FileOutputStream(outputFile); ITextRenderer renderer = new ITextRenderer(); renderer.setDocument(url); // 解決中文支持問題 ITextFontResolver fontResolver = renderer.getFontResolver(); fontResolver.addFont("C:/Windows/Fonts/arialuni.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); // 解決圖片的相對路徑問題 renderer.getSharedContext().setBaseURL("file:/D:/Work/Demo2do/Yoda/branch/Yoda%20-%20All/conf/template/"); renderer.layout(); renderer.createPDF(os); os.close(); } }
/*
* ITextRendererTest.java *
* Copyright 2009 Shanghai TuDou.
* All rights reserved.
*/
package itext;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import com.lowagie.text.pdf.BaseFont;
/**
* TODO class description *
*
* @author pcwang
*
* @version 1.0, 上午11:03:26 create $Id$
*/
public class ITextRendererTest {
public static void main(String[] args) throws Exception {
String inputFile = "conf/template/test.html";
String url = new File(inputFile).toURI().toURL().toString();
String outputFile = "firstdoc.pdf";
OutputStream os = new FileOutputStream(outputFile);
ITextRenderer renderer = new ITextRenderer();
renderer.setDocument(url);
// 解決中文支持問題
ITextFontResolver fontResolver = renderer.getFontResolver();
fontResolver.addFont("C:/Windows/Fonts/arialuni.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
// 解決圖片的相對路徑問題
renderer.getSharedContext().setBaseURL("file:/D:/Work/Demo2do/Yoda/branch/Yoda%20-%20All/conf/template/");
renderer.layout();
renderer.createPDF(os);
os.close();
}
}
運行,成功!實在太簡單了!API幫你完成了一切!
有了這個東西,咱們就能夠將PDF的生成流程變成這樣:
1) 編寫Freemarker或者Velocity模板,打造HTML,勾畫PDF的樣式(請任意使用CSS)
2) 在你的業務邏輯層引入Freemarker的引擎或者Velocity的引擎,並將業務邏輯層中能夠獲取的數據和模板,使用引擎生成最終的內容
3) 將我上面的sample代碼作簡單封裝後,調用,生成PDF
這樣,我想做爲一個web程序員來講,上面的3點,都不會成爲你的絆腳石。你能夠輕鬆駕馭PDF了。
在Flying Saucer的官方文檔中,有一些Q&A,能夠解決讀者們大部分的問題。包括PDF的字體、PDF的格式、Image如何處理等等。你們能夠嘗試着去閱讀。
還有一篇文章,好像是做者寫的,很是不錯:http://today.java.net/pub/a/today/2007/06/26/generating-pdfs-with-flying-saucer-and-itext.html