Java
程序
?
通過使用上述步驟中用過的三個(gè)工具的
DOM API
,我接下來會(huì)展示一個(gè)
JAVA
程序。它在運(yùn)行時(shí)需要提供兩個(gè)命令行參數(shù),會(huì)自動(dòng)生成相應(yīng)的
PDF
文檔,并且不會(huì)產(chǎn)生任何臨時(shí)文件。
?
第一個(gè)程序新建一個(gè)
HTML
文件的
InputStream
對(duì)象,然后此對(duì)象被傳給
JTidy
。
?
JTidy
有個(gè)方法叫
parseDOM()
,可以用來生成輸出的
XHTML
文檔的
Document
對(duì)象。
?
???
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
實(shí)現(xiàn)并不支持
XML
命名空間。因此,我們必需修改
Antenna House
的樣式表,讓它使用默認(rèn)的命名空間。比如,原來是:
?
???
<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>
?
這個(gè)改動(dòng)必需被應(yīng)用到
xhtml2f0.xsl
中的所有模板,因?yàn)?/span>
JTidy
生成的
Document
對(duì)象以
<html>
標(biāo)簽作為根,如:
?
<html
xmlns=quot;http://www.w3.org/1999/xhtml">
?
修改后的
xhtml2fo.xsl
包含在這篇文章附帶的源代碼中。
?
接著,
xml2FO()
方法調(diào)用
Xalan
,使樣式表應(yīng)用于
JTidy
生成的
DOM
對(duì)象:
?
???
Document foDoc = xml2FO(xmlDoc, args[1]);?
?
方法
xml2FO()
首先調(diào)用
getTransformer()
來獲得一個(gè)指定的樣式表的
Transformer
對(duì)象。然后,代表著轉(zhuǎn)換結(jié)果的那個(gè)
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
輸入文件相同的前綴來打開一個(gè)
FileOutputStream
。然后調(diào)用
fo2PDF()
方法所獲得的結(jié)果被寫入
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()
會(huì)使用在轉(zhuǎn)換中產(chǎn)生的
XSL-FO Document
和一個(gè)
ByteArrayOutputStream
來生成一個(gè)
FOP driver
對(duì)象。通過調(diào)用
Driver.run
可以生成
PDF
文件。結(jié)果被作為一個(gè)
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
來完成這整個(gè)過程,速度要比使用命令行界面快得多,因?yàn)樗恍枰疟P中寫入任何中間文件。這種方法可以集成到服務(wù)器里,來處理并發(fā)的
HTML-PDF
轉(zhuǎn)換請(qǐng)求。
?
以前我曾以這里展示的這個(gè)程序?yàn)榛A(chǔ)把生成
PDF
的功能集成到一個(gè)
WEB
應(yīng)用。而生成
PDF
的過程是動(dòng)態(tài)的,因此不需要考慮
WEB
頁面和相應(yīng)
PDF
同步的問題,因?yàn)樯傻?/span>
PDF
文件并不是存放在服務(wù)器上。
?
結(jié)論
?
綜述,在本文里我描述了怎樣利用開源組件來實(shí)現(xiàn)
HTML
到
PDF
的轉(zhuǎn)換。雖然這種實(shí)現(xiàn)方法在價(jià)格和源碼方面很有吸引力,但同時(shí)也有一定的折衷。一些商業(yè)組件可以提供更完整的標(biāo)準(zhǔn)實(shí)現(xiàn)。
?
比如說,
FOP
目前的版本是
.91
,不完全支持
XSL-FO
標(biāo)準(zhǔn)。盡管如此,相對(duì)其它的格式而言,對(duì)
PDF
提供了更多的支持。
?
在開始一個(gè)文檔轉(zhuǎn)換的項(xiàng)目之前,你必需考慮對(duì)文檔格式的需求,并把它們與已有組件所實(shí)現(xiàn)的功能做個(gè)對(duì)比。這將有助于做出正確的決定。
?
?
資源
?
#
下載本文中的源碼
:
http://www.javaworld.com/javaworld/jw-04-2006/html/jw-0410-html.zip
# Adobe's Document Server
產(chǎn)品
:
http://www.adobe.com/products/server/documentserver/main.html
# Antenna House (
出售商業(yè)的格式化程序
):
http://www.antennahouse.com
# xhtml2fo.xsl
把
XHTML
轉(zhuǎn)化為
XSL-FO
的樣式表
:
http://www.antennahouse.com/XSLsample/XSLsample.htm
# Apache FOP formatter
把
XSL-FO
翻譯為
PDF:
http://xmlgraphics.apache.org/fop
# FOP
對(duì)
XSL-FO
標(biāo)準(zhǔn)的兼容性
:
http://xmlgraphics.apache.org/fop/compliance.html
# JTidy
,把
HTML
轉(zhuǎn)化為
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
部分可以找到更多關(guān)于
Java
開發(fā)工具的文章
:
http://www.javaworld.com/channel_content/jw-tools-index.shtml
# JavaWorld
的
Java
與
XML
部分的文章索引
:
http://www.javaworld.com/channel_content/jw-xml-index.shtml