HanG321 Blog

iText 經驗總結

http://www.blogjava.net/gf7/archive/2007/02/28/101148.html

iText經驗總結

因為前些日子在一個項目中用到了iText,稍有收獲,便總結于此,以供他人所需。

iText是一個比較底層的pdf庫,很多項目的pdf操作都是以它為基礎的。像spring,以及另一個比較有名的報表工具jasperreports。簡單的pdf報表輸出用它比較合適,比較復雜的話使用起來就比較困難了,你要手工編寫太多的代碼。

比較好的是iText網站上提供相當多的示例代碼,比較容易入門。我這里只說一些在它的文檔里並沒有直接講到的東西。

1 關于Document

Document的几種构造函數:
public Document();
public Document(Rectangle pageSize);
public Document(Rectangle pageSize,
int marginLeft,
int marginRight,
int marginTop,
int marginBottom);
下面兩種比較有用,如果是你想定義紙張大小和邊緣的時候。對于Margin,iText上提到”You can also change the margins while you are adding content. Note that the changes will only be noticed on the NEXT page. If you want the margins mirrored (odd and even pages), you can do this with this method: setMarginMirroring(true). “不過,對于table似乎並不好使。table並不會了理會你設定的margin,如果想改變它的magin還是需要去改變它的寬度(setWidth)。

2 pdf表單

使用PdfStamper是可以填充pdf表單的,這樣就給出了一種很好的報表生成思路。
word制作報表樣式–>acrobat轉pdf–>itext填充數据–>輸出pdf
這做非常簡單,因為可以比較容易的控制pdf的樣式。我對于Java的報表工具了解的並不多,不過在jasperreports,即使用GUI工具做一個樣式比較復雜的報表也不是怎麼容易。比如有那種斜線的表頭,比較花哨的嵌套表格。這樣的情況還是比較多見的,客戶不會關系你實現起來是否困難。不過想要使用這種方式也有不足的地方。首先是acrobat把word轉化成pdf的時候,格式總是保持不好,特別的是字體。然后是文件的體積這樣生成的pdf會比直接用iText生成的pdf文件大很多,acrobat在pdf里加入了太多無用的信息。初次使用iText填充Adobe Designer生成的pdf表單時會有點小麻煩。在Designer中設計了一個name的text文本框的綁定名為name。照著iText中例子使用使用PdfStamper的setField方法去這樣寫form.setField(“name”, “XXXX”);並不會成功。原因是Adobe Designer生成的表單名都是具有層次的,它可能是這個樣子form1[0].#subform[0].name[0]。不過我們可以用一個方法把它們列出來,只要做一次就知道結构了,可以使用類似下面的代碼:
PdfReader reader = new PdfReader(“form.pdf”);
PdfStamper stamp = new PdfStamper(reader, new FileOutputStream(“registered_flat.pdf”));
AcroFields form = stamp.getAcroFields();
for (Iterator it = form.getFields().keySet().iterator(); it
.hasNext();) {
System.out.println(it.next());
}
如果直接用iText編程生成的表單就不會有這樣的問題,設定的什麼名字就是什麼名字。

3 表單元素

pdf並不像html那樣具有良好清晰的結构,而是一個有層次的文檔類型。在它的maillist里,作者說明了iText雖然可以操作現存的pdf文件但是沒辦法去還原它的結构的。沒辦法像html一樣,能從一個pdf文件獲得一個清晰的”源文件”的。關于層次,可以從iText上得到詳細的講述,獲取去看看pdf規范。表單和普通文本是不在一個層上的。沒辦法適用對待文本表各一樣把它們簡單的add進Document對象。獲取一個cb直接去用絕對定位的方法可以加入表單元素,不過很多的時候因為排版並不能那麼簡單的去做。就是在html中布局一樣可以使用表格定位。想把一個表單元素加入cell,要借助cell的setCellEvent方法。以一個checkbox為例。新建一個類CheckBoxForm,實現PdfPCellEvent接口。需要實現一個cellLayout的方法。
public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases)
position可以好好利用,它包含當前cell的位置信息,你可以用它來确定自己checkbox的位置。
position.top()-position.bottom()就能得到高position.right()-position.left()可以得到長,如果需要這兩個值得花可以如此計算。下面的代碼就是定義一個寬度為a的checkbox的rectangle 。它在cell中水平居中,垂直也居中。
float bo = (position.top()-position.bottom()-a)/2;
float ao = (position.right()-position.left()-a)/2;
Rectangle rectangle = new Rectangle(position.left() + ao, position
.bottom() + bo, position.left() +ao+ a, position.bottom()+ bo + a);
然后把它加入Document
RadioCheckField tf = new RadioCheckField(writer, rectangle, fieldname,
“f”);
tf.setCheckType(RadioCheckField.TYPE_SQUARE);
tf.setBorderWidth(1);
tf.setBorderColor(Color.black);
tf.setBackgroundColor(Color.white);
try {
PdfFormField field = tf.getCheckField();
writer.addAnnotation(field);
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
其它的元素與此類似。

4 PdfPTable和Table

說不上哪種更好用,有時候不能不使用PdfPTable。可惜它只有setColspan方法,沒有setRowspan。嵌套的時候也有區別,PdfPTable是用addcell()加入嵌套表的,table則有一個更明了的方法insertTable()。PdfPTable想進行設置border之類的操作要先獲得一個默認cell,
pdfPTableName.getDefaultCell().setBorder(Rectangle.NO_BORDER);//設置無框的表
另外在PdfPTable中,一些修飾屬性會因為設置的時机不正确而沒有效果。如,適用cell的构造函數加入了文本,在cell的setVerticalAlignment()fangfa去設定垂直對齊方式就不會有效。還有一個有意思的不同是table默認外邊框是加粗的,而PdfPTable則一樣粗細。

5 字體

iText的例子有很多足夠用,給出一些pdf的字體名稱和編碼,如果想使用內嵌字體的話。
語言 PDF 字體名
簡體中文 STSong-Light
繁體中文 MHei-Medium
MSung-Light
日語 HeiseiKakuGo-W5
HeiseiMin-W3
韓語 HYGoThic-Medium
HYSMyeongJo-Medium

字符集 編碼
簡體中文 UniGB-UCS2-H
UniGB-UCS2-V
繁體中文 UniCNS-UCS2-H
UniCNS-UCS2-V
日語 UniJIS-UCS2-H
UniJIS-UCS2-V
UniJIS-UCS2-HW-H
UniJIS-UCS2-HW-V
韓語 UniKS-UCS2-H
UniKS-UCS2-H
必須要有Asian的包才可以用,也可以使用TrueType字體。

ps:因為隔了一段時間了,所以有些現在一時也想不起來了,也可能會有理解的錯誤。另外,適用iText的時候自己最好抽象一下,可能會省不少力氣。


Technorati :