如何在程序中嵌入 FOP。FOP 是由 James Tauber 發起的一個開源項目,最初的目的是利用 xsl-fo 將 xml 文件轉換成 pdf 文件。目前最新的版本可以將 xml 文件轉換成 pdf,mif,pcl,txt 等多種格式以及直接輸出到打印機,並且支持使用 SVG 描述圖形。XML 顯然是最好的內容存儲格式,而 PDF 是目前最流行的內容載體格式,FOP 顯然希望借助這種必然的 XML to PDF 需求,來推動 xsl-fo 規范的發展。雖然 xsl-fo 規范停滯不前,但利用 FOP 能使自己的程序具有將 XML 內容輸出成 PDF 等流行格式的功能無疑是令人興奮的。
FOP使用方式
FOP有3種使用方式,分別為命令行,程序嵌入,XT 嵌入,這裡將主要介紹如何在程序中嵌入FOP功能。將XML文件轉換為PDF實際上分為2步,第1步是利用XSLT將XML轉換為XSL-FO,第2步是將XSL-FO轉換為PDF。這裡不想講述XSLT和XSL-FO有關的知識(這方面的文檔相當多),而只將講述如何進行第2步的轉換編程。
在程序中嵌入FOP
1. 范例simple.fo文件
<?XML version="1.0" encoding="utf-8"?>
<fo:root XMLns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="simple"
page-height="29.7cm"
page-width="21cm"
margin-top="1cm"
margin-bottom="2cm"
margin-left="2.5cm"
margin-right="2.5cm">
<fo:region-body margin-top="3cm"/>
<fo:region-before extent="3cm"/>
<fo:region-after extent="1.5cm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-name="simple">
<fo:flow flow-name="xsl-region-body">
<!-- Title -->
<fo:block font-size="18pt"
font-family="sans-serif"
line-height="24pt"
space-after.optimum="15pt"
background-color="blue"
color="white"
text-align="center"
padding-top="3pt">
FOP 0.20.2
</fo:block>
<!-- Normal Text -->
<fo:block font-size="12pt"
font-family="sans-serif"
line-height="15pt"
space-after.optimum="3pt"
text-align="justify">
FOP is the world's first print formatter driven by XSL formatting objects.
It is a Java application that reads a formatting object tree and then turns it into a PDF document.
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
上述是一個很簡單的fo文件,將顯示兩塊文字,具體的fo語法請讀者自己查看相應資料。
2. 簡單調用FOP
FOP提供的所有對外調用接口都在org.apache.fop.aPPS下,其下的AWTStarter.class是一個用AWT寫的轉換結果預覽程序,CommandLineStarter.class提供命令行使用方式,PrintStarter.class提供打印接口,XTDriver.class提供XT嵌入方式接口,Driver.class則是我們要在這裡討論的程序接口。下面這個程序 foptest.Java演示了FOP的最簡單也是最常用的使用方式。
import Java.io.*;
import org.XML.sax.InputSource;
import org.xml.sax.XMLReader;
import org.apache.fop.aPPS.*;
public class foptest {
public static void main(String[] args) {
try {
Driver driver = new Driver();
//設置要轉換的fo文件名
driver.setInputSource(new InputSource (args[0]));
//設置輸出文件名
driver.setOutputStream(new FileOutputStream(args[1]));
//設置轉換類型
//還可以為RENDER_PCL,RENDER_PS,RENDER_TXT,RENDER_MIF
driver.setRenderer(Driver.RENDER_PDF);
//開始轉換
driver.run();
}
catch( Exception e ){
e.printStackTrace();
}
}
}
上述程序將接受兩個命令行參數,第一個參數是需要轉換的fo文件名,第二個參數是輸出文件名。在CLASSPATH中添加下述jar文件: {FOP安裝目錄}\build\fop.jar
{FOP安裝目錄}\lib\batik.jar
{FOP安裝目錄}\lib\xalan-2.0.0.jar
{FOP安裝目錄}\lib\xerces-1.2.3.jar
{FOP安裝目錄}\lib\avalon-framework-4.0.jar
{FOP安裝目錄}\lib\logkit-1.0b4.jar
{FOP安裝目錄}\lib\jimi-1.0.jar
然後執行Java foptest simple.fo simple.pdf
執行後產生的simple.pdf文件效果如下
查看原圖(大圖)
3. 顯示中文
上面的演示程序段可以應付絕大部分轉換編程的需要,不過對於國內用戶來說,不可避免的需要產生中文PDF文件,讓我們把上述simple.fo的標題從"FOP 0.20.2"改成"支持中文的FOP 0.20.2",不改動程序的情況下執行結果顯示如下
查看原圖(大圖)
可以看到由於沒有加入相應的字體支持,漢字被顯示成了#。
東方字符的顯示在FOP的早期版本中並不被支持,最早嘗試對FOP打補丁以解決顯示東方字符的是日本人。從FOP 0.16版本開始,他們在sourceforge上建立了一個jpfop項目來解決日文字符的顯示問題,使用相同的方法也可以被用來顯示中文字符。幸運的是,當前的FOP版本已經能很好的解決中文顯示的問題,不再需要我們打補丁,下面是在FOP中使用中文的步驟:
第一步,建立font metrics文件對於後綴為ttf的TrueType字體文件,我們可以執行以下命令來產生font metrics文件 Java org.apache.fop.fonts.aPPS.TTFReader C:\WINNT\Fonts\simkai.ttf simkai.XML
這裡simkai.XML就是我們為楷體產生的font metrics文件。對於Windows下的宋體來說,存在的是後綴為ttc的TrueType Collection文件,即包含多個TrueType的文件,這時首先要做的是得到這個Collection中所有TrueType的名字,執行以下命令(這個命令其實有錯誤):
Java org.apache.fop.fonts.aPPS.TTFReader C:\WINNT\Fonts\simsun.ttc simsun.XML
產生輸出如下:
This is a TrueType collection file with2 fonts
Containing the following fonts:
SimSun
NSimSun
Java.io.IOException: Failed to read font
at org.apache.fop.fonts.TTFFile.readFont(TTFFile.Java:388)
at org.apache.fop.fonts.aPPS.TTFReader.loadTTF(TTFReader.Java:181)
at org.apache.fop.fonts.aPPS.TTFReader.main(TTFReader.Java:143)
後面的異常是由於我們給的參數不對(因為ttc不是ttf字體文件)造成的,FOP開發小組知道這個問題,但是可能覺得無關痛癢而沒去修正它。無論怎樣,我們得到了我們要得到的結果,裡面含的字體名為SimSun和NSimSun,通過以下命令為其中的SimSun字體產生font metrics文件: Java org.apache.fop.fonts.aPPS.TTFReader -ttcname "SimSun" C:\WINNT\Fonts\simsun.ttc simsun.XML -ttcname後面指定需要從ttc文件中提取的字體名稱
第二步,登記上述字體
在FOP主目錄下的conf子目錄下有一個userconfig.xml文件,為了方便,我們將它和上一步產生的 simsun.xml,simkai.xml都拷貝到與我們的演示程序同一目錄下。在userconfig.XML的最後幾行有一個<fonts></fonts>標記區,我們在其中加入以下項:
<font metrics-file="simsun.XML" kerning="yes" embed-file="c:\WINNT\fonts\simsun.ttc">
<font-triplet name="mysimsun" style="normal" weight="normal"/>
</font>
<font metrics-file="simkai.XML" kerning="yes" embed-file="c:\WINNT\fonts\simkai.ttf">
<font-triplet name="mysimkai" style="normal" weight="normal"/>
</font>
其中metrics-file裡可以設相對路徑或絕對路徑(因為我們這裡在同一目錄下,所以只需寫文件名即可),font-triplet裡的name可以自己自由設定,並不要求與字體名一樣。設定這個名字後,在fo裡就只能通過這個名字引用這個字體。
為了演示中文顯示,范例simplecn.fo文件為
<?XML version="1.0" encoding="gb2312"?>
<fo:root XMLns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="simple"
page-height="29.7cm"
page-width="21cm"
margin-top="1cm"
margin-bottom="2cm"
margin-left="2.5cm"
margin-right="2.5cm">
<fo:region-body margin-top="3cm"/>
<fo:region-before extent="3cm"/>
<fo:region-after extent="1.5cm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-name="simple">
<fo:flow flow-name="xsl-region-body">
<fo:block font-size="18pt"
font-family="mysimsun"
line-height="24pt"
text-align="center"
padding-top="3pt">
這是宋體
</fo:block>
<fo:block font-size="18pt"
font-family="mysimkai"
line-height="24pt"
text-align="center"
padding-top="3pt">
這是楷體
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
上述fo文件使用兩種字體分別顯示一行文字,注意在<fo:block>的屬性中的font-family被我們設成userconfig.XML中相應的名字。
由於需要讀入userconfig.XML來得到字體信息,程序主體修改如下:
Driver driver = new Driver();
driver.setInputSource(new InputSource (args[0]));
driver.setOutputStream(new FileOutputStream(args[1]));
driver.setRenderer(Driver.RENDER_PDF);
//讀入配置(在Options的構造函數中完成)
Options options = new Options(new File("userconfig.XML"));
driver.run();
這裡在run之前一行讀入配置(產生的Options的實例在以後沒有用處),執行結果為:
總結
FOP從技術上說無疑是一個非常優秀的產品,但是目前它對用戶的開發支持顯然很欠缺。FOP的開發小組也意識到了這個問題,他們允諾在將來會建立一個專門的Web站點以及豐富它的文檔。如果現在就想使用更方便的產品的話,你也可以考慮一些商用產品,如RenderX和X2P。
另外需要注意的是,今年8月推出的FOP 0.20.1版本非常的不好,不但一些范例無法運行,而且附帶的源代碼有若干錯誤,而這個9月29日推出的0.20.2RC版本有極大的改進,建議大家盡快升級到這個版本。