Java
程序
通過使用上述步驟中用過的三個工具的 DOM API ,我接下來會展示一個 JAVA 程序。它在運行時需要提供兩個命令行參數,會自動生成相應的 PDF 文檔,并且不會產生任何臨時文件。
第一個程序新建一個 HTML 文件的 InputStream 對象,然后此對象被傳給 JTidy 。
JTidy 有個方法叫 parseDOM() ,可以用來生成輸出的 XHTML 文檔的 Document 對象。
??? public static void main(String[] args) {
??? // 打開文件
??? if (args.length != 2) {
??????? System.out.println("Usage: Html2Pdf htmlFile styleSheet");
????? ??System.exit(1);
??? }
??? FileInputStream input = null;
??? String htmlFileName = args[0];
??? try {
??????? input = new FileInputStream(htmlFileName);
??? }
??? catch (java.io.FileNotFoundException e) {
??????? System.out.println("File not found: " + htmlFileName);
??? }
??????? Tidy tidy = new Tidy();
??? Document xmlDoc = tidy.parseDOM(input, null);
JTidy 的 DOM 實現并不支持 XML 命名空間。因此,我們必需修改 Antenna House 的樣式表,讓它使用默認的命名空間。比如,原來是:
??? <xsl:template match="html:h2">
????? <fo:block xsl:use-attribute-sets="h2">
? ?????? <xsl:call-template name="process-common-attributes-and-children"/>
????? </fo:block>
??? </xsl:template>
被修改后是:
??? <xsl:template match="h2">
????? <fo:block xsl:use-attribute-sets="h2">
??????? <xsl:call-template name="process-common-attributes-and-children"/>
????? </fo:block>
??? </xsl:template>
這個改動必需被應用到
xhtml
<html xmlns=quot;http://www.w3.org/1999/xhtml">
修改后的 xhtml2fo.xsl 包含在這篇文章附帶的源代碼中。
接著, xml2FO() 方法調用 Xalan ,使樣式表應用于 JTidy 生成的 DOM 對象:
??? Document foDoc = xml2FO(xmlDoc, args[1]);?
方法 xml2FO() 首先調用 getTransformer() 來獲得一個指定的樣式表的 Transformer 對象。然后,代表著轉換結果的那個 Document 被返回:
??? private static Document xml2FO(Document xml, String styleSheet) {
??? DOMSource xmlDomSource = new DOMSource(xml);
????????? DOMResult domResult = new DOMResult();
??? Transformer transformer = getTransformer(styleSheet);
??? if (transformer == null) {
??????? System.out.println("Error creating transformer for " + styleSheet);
??????? System.exit(1);
??? }
??? try {
??????? transformer.transform(xmlDomSource, domResult);
??? }
??? catch (javax.xml.transform.TransformerException e) {
??????? return null;
??? }
??? return (Document) domResult.getNode();
??? }
接著, main 方法用與 HTML 輸入文件相同的前綴來打開一個 FileOutputStream 。然后調用 fo2PDF() 方法所獲得的結果被寫入 OutputStream :
??? String pdfFileName = htmlFileName.substring(0, htmlFileName.indexOf(".")) + ".pdf";
??? try {
??????? OutputStream pdf = new FileOutputStream(new File(pdfFileName));
??????? pdf.write(fo2PDF(foDoc));
??? }
??? catch (java.io.FileNotFoundException e) {
??????? System.out.println("Error creating PDF: " + pdfFileName);
??? }
??? catch (java.io.IOException e) {
??????? System.out.println("Error writing PDF: " + pdfFileName);
??? }
方法 fo2PDF() 會使用在轉換中產生的 XSL-FO Document 和一個 ByteArrayOutputStream 來生成一個 FOP driver 對象。通過調用 Driver.run 可以生成 PDF 文件。結果被作為一個 byte array 返回:
??? private static byte[] fo2PDF(Document foDocument) {
??????? DocumentInputSource fopInputSource = new DocumentInputSource(
???????????????????????????????????????????????????????? foDocument);
??????? try {
??????????? ByteArrayOutputStream out = new ByteArrayOutputStream();
??????????? Logger log = new ConsoleLogger(ConsoleLogger.LEVEL_WARN);
??????????? Driver driver = new Driver(fopInputSource, out);
??????????? driver.setLogger(log);
???? ???????driver.setRenderer(Driver.RENDER_PDF);
??????????? driver.run();
??????????? return out.toByteArray();
??????? } catch (Exception ex) {
??????????? return null;
??????? }
??? }
Html2Pdf.java 的源代碼可以在這篇文章的附帶代碼中找到。
使用 DOM API 來完成這整個過程,速度要比使用命令行界面快得多,因為它不需要往磁盤中寫入任何中間文件。這種方法可以集成到服務器里,來處理并發的 HTML-PDF 轉換請求。
以前我曾以這里展示的這個程序為基礎把生成 PDF 的功能集成到一個 WEB 應用。而生成 PDF 的過程是動態的,因此不需要考慮 WEB 頁面和相應 PDF 同步的問題,因為生成的 PDF 文件并不是存放在服務器上。
結論
綜述,在本文里我描述了怎樣利用開源組件來實現 HTML 到 PDF 的轉換。雖然這種實現方法在價格和源碼方面很有吸引力,但同時也有一定的折衷。一些商業組件可以提供更完整的標準實現。
比如說, FOP 目前的版本是 .91 ,不完全支持 XSL-FO 標準。盡管如此,相對其它的格式而言,對 PDF 提供了更多的支持。
在開始一個文檔轉換的項目之前,你必需考慮對文檔格式的需求,并把它們與已有組件所實現的功能做個對比。這將有助于做出正確的決定。
資源
# 下載本文中的源碼 :
http://www.javaworld.com/javaworld/jw-04-2006/html/jw-0410-html.zip
# Adobe's Document Server 產品 :
http://www.adobe.com/products/server/documentserver/main.html
# Antenna House ( 出售商業的格式化程序 ):
http://www.antennahouse.com
# xhtml2fo.xsl 把 XHTML 轉化為 XSL-FO 的樣式表 :
http://www.antennahouse.com/XSLsample/XSLsample.htm
# Apache FOP formatter 把 XSL-FO 翻譯為 PDF:
http://xmlgraphics.apache.org/fop
# FOP 對 XSL-FO 標準的兼容性 :
http://xmlgraphics.apache.org/fop/compliance.html
# JTidy ,把 HTML 轉化為 XHTML:
http://sourceforge.net/projects/jtidy/
# Xalan:
http://xalan.apache.org/
# XSL-FO, Dave Pawson (O'Reilly Media, 2002 年 8 月 ; ISBN: 0596003552):
http://www.amazon.com/exec/obidos/ASIN/0596003552/javaworld
# XSLT 2.0 Programmer's Reference, Michael Kay (Wrox, 2004 年 8 月 ; ISBN: 0764569090):
http://www.amazon.com/exec/obidos/ASIN/0764569090/javaworld
# 瀏覽 JavaWorld 的 Development Tools 部分可以找到更多關于 Java 開發工具的文章 :
http://www.javaworld.com/channel_content/jw-tools-index.shtml
# JavaWorld 的 Java 與 XML 部分的文章索引 :
http://www.javaworld.com/channel_content/jw-xml-index.shtml