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>
?
這個改動必需被應用到
xhtml2f0.xsl
中的所有模板,因為
JTidy
生成的
Document
對象以
<html>
標簽作為根,如:
?
<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