iText經驗總結(轉)

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的時候自己最好抽象一下,可能會省不少力氣。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章