因?yàn)榍靶┤兆釉谝粋€(gè)項(xiàng)目中用到了iText,稍有收獲,便總結(jié)于此,以供他人所需。
iText是一個(gè)比較底層的pdf庫(kù),很多項(xiàng)目的pdf操作都是以它為基礎(chǔ)的。像spring,以及另一個(gè)比較有名的報(bào)表工具jasperreports。簡(jiǎn)單的pdf報(bào)表輸出用它比較合適,比較復(fù)雜的話使用起來(lái)就比較困難了,你要手工編寫太多的代碼。
比較好的是iText網(wǎng)站上提供相當(dāng)多的示例代碼,比較容易入門。我這里只說(shuō)一些在它的文檔里并沒(méi)有直接講到的東西。
1 關(guān)于Document
Document的幾種構(gòu)造函數(shù):
public Document();
public Document(Rectangle pageSize);
public Document(Rectangle pageSize,
int marginLeft,
int marginRight,
int marginTop,
int marginBottom);
下面兩種比較有用,如果是你想定義紙張大小和邊緣的時(shí)候。對(duì)于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). ”不過(guò),對(duì)于table似乎并不好使。table并不會(huì)了理會(huì)你設(shè)定的margin,如果想改變它的magin還是需要去改變它的寬度(setWidth)。
2 pdf表單
使用PdfStamper是可以填充pdf表單的,這樣就給出了一種很好的報(bào)表生成思路。
word制作報(bào)表樣式-->acrobat轉(zhuǎn)pdf-->itext填充數(shù)據(jù)-->輸出pdf
這做非常簡(jiǎn)單,因?yàn)榭梢员容^容易的控制pdf的樣式。我對(duì)于Java的報(bào)表工具了解的并不多,不過(guò)在jasperreports,即使用GUI工具做一個(gè)樣式比較復(fù)雜的報(bào)表也不是怎么容易。比如有那種斜線的表頭,比較花哨的嵌套表格。這樣的情況還是比較多見的,客戶不會(huì)關(guān)系你實(shí)現(xiàn)起來(lái)是否困難。不過(guò)想要使用這種方式也有不足的地方。首先是acrobat把word轉(zhuǎn)化成pdf的時(shí)候,格式總是保持不好,特別的是字體。然后是文件的體積這樣生成的pdf會(huì)比直接用iText生成的pdf文件大很多,acrobat在pdf里加入了太多無(wú)用的信息。初次使用iText填充Adobe Designer生成的pdf表單時(shí)會(huì)有點(diǎn)小麻煩。在Designer中設(shè)計(jì)了一個(gè)name的text文本框的綁定名為name。照著iText中例子使用使用PdfStamper的setField方法去這樣寫form.setField("name", "XXXX");并不會(huì)成功。原因是Adobe Designer生成的表單名都是具有層次的,它可能是這個(gè)樣子form1[0].#subform[0].name[0]。不過(guò)我們可以用一個(gè)方法把它們列出來(lái),只要做一次就知道結(jié)構(gòu)了,可以使用類似下面的代碼:
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編程生成的表單就不會(huì)有這樣的問(wèn)題,設(shè)定的什么名字就是什么名字。
3 表單元素
pdf并不像html那樣具有良好清晰的結(jié)構(gòu),而是一個(gè)有層次的文檔類型。在它的maillist里,作者說(shuō)明了iText雖然可以操作現(xiàn)存的pdf文件但是沒(méi)辦法去還原它的結(jié)構(gòu)的。沒(méi)辦法像html一樣,能從一個(gè)pdf文件獲得一個(gè)清晰的“源文件”的。關(guān)于層次,可以從iText上得到詳細(xì)的講述,獲取去看看pdf規(guī)范。表單和普通文本是不在一個(gè)層上的。沒(méi)辦法適用對(duì)待文本表各一樣把它們簡(jiǎn)單的add進(jìn)Document對(duì)象。獲取一個(gè)cb直接去用絕對(duì)定位的方法可以加入表單元素,不過(guò)很多的時(shí)候因?yàn)榕虐娌⒉荒苣敲春?jiǎn)單的去做。就是在html中布局一樣可以使用表格定位。想把一個(gè)表單元素加入cell,要借助cell的setCellEvent方法。以一個(gè)checkbox為例。新建一個(gè)類CheckBoxForm,實(shí)現(xiàn)PdfPCellEvent接口。需要實(shí)現(xiàn)一個(gè)cellLayout的方法。
? public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases)
position可以好好利用,它包含當(dāng)前cell的位置信息,你可以用它來(lái)確定自己checkbox的位置。
position.top()-position.bottom()就能得到高position.right()-position.left()可以得到長(zhǎng),如果需要這兩個(gè)值得花可以如此計(jì)算。下面的代碼就是定義一個(gè)寬度為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
說(shuō)不上哪種更好用,有時(shí)候不能不使用PdfPTable。可惜它只有setColspan方法,沒(méi)有setRowspan。嵌套的時(shí)候也有區(qū)別,PdfPTable是用addcell()加入嵌套表的,table則有一個(gè)更明了的方法insertTable()。PdfPTable想進(jìn)行設(shè)置border之類的操作要先獲得一個(gè)默認(rèn)cell,
pdfPTableName.getDefaultCell().setBorder(Rectangle.NO_BORDER);//設(shè)置無(wú)框的表
另外在PdfPTable中,一些修飾屬性會(huì)因?yàn)樵O(shè)置的時(shí)機(jī)不正確而沒(méi)有效果。如,適用cell的構(gòu)造函數(shù)加入了文本,在cell的setVerticalAlignment()fangfa去設(shè)定垂直對(duì)齊方式就不會(huì)有效。還有一個(gè)有意思的不同是table默認(rèn)外邊框是加粗的,而PdfPTable則一樣粗細(xì)。
5 字體
iText的例子有很多足夠用,給出一些pdf的字體名稱和編碼,如果想使用內(nèi)嵌字體的話。
語(yǔ)言 PDF 字體名
簡(jiǎn)體中文 STSong-Light
繁體中文 MHei-Medium
MSung-Light
日語(yǔ) HeiseiKakuGo-W5
HeiseiMin-W3
韓語(yǔ) HYGoThic-Medium
HYSMyeongJo-Medium
字符集 編碼
簡(jiǎn)體中文 UniGB-UCS2-H
UniGB-UCS2-V
繁體中文 UniCNS-UCS2-H
UniCNS-UCS2-V
日語(yǔ) UniJIS-UCS2-H
UniJIS-UCS2-V
UniJIS-UCS2-HW-H
UniJIS-UCS2-HW-V
韓語(yǔ) UniKS-UCS2-H
UniKS-UCS2-H
必須要有Asian的包才可以用,也可以使用TrueType字體。