java生成pdf檔案的說明
一、前言
前幾天,做ASN條碼收貨模組,需要實現列印下載收貨報表,經一番查詢,選定iText--用於生成PDF文件的一個Java類庫。廢話不多說,進入正題。
二、iText簡介
iText是著名的開放原始碼的站點sourceforge一個專案,是用於生成PDF文件的一個java類庫。通過iText不僅可以生成PDF或rtf的文件,而且可以將XML、Html檔案轉化為PDF檔案。
iText的安裝非常方便,在http://itextpdf.com/ 網站上下載iText.jar檔案後,只需要在系統的CLASSPATH中加入iText.jar的路徑,在程式中就可以使用iText類庫了。
三、建立第一個PDF文件
用iText生成PDF文件需要5個步驟:
①建立com.lowagie.text.Document物件的例項。
Document document = new Document();
②建立一個書寫器(Writer)與document物件關聯,通過書寫器(Writer)可以將文件寫入到磁碟中。
PDFWriter.getInstance(document, new FileOutputStream("Helloworld.PDF"));
③開啟文件。
document.open();
④向文件中新增內容。
document.add(new Paragraph("Hello World"));
⑤關閉文件。
document.close();
通過上面的5個步驟,就能產生一個Helloworld.PDF的檔案,檔案內容為"Hello World"。
建立com.lowagie.text.Document物件的例項
com.lowagie.text.Document物件的構建函式有三個,分別是:
public Document();
public Document(Rectangle pageSize);
public Document(Rectangle pageSize,
int marginLeft,
int marginRight,
int marginTop,
int marginBottom);
構建函式的引數pageSize是文件頁面的大小,對於第一個構建函式,頁面的大小為A4,同Document(PageSize.A4)的效果一樣;對於第三個構建函式,引數marginLeft、marginRight、marginTop、marginBottom分別為左、右、上、下的頁邊距。
通過引數pageSize可以設定頁面大小、面背景色、以及頁面橫向/縱向等屬性。iText定義了A0-A10、AL、LETTER、HALFLETTER、_11x17、LEDGER、NOTE、B0-B5、ARCH_A-ARCH_E、FLSA 和FLSE等紙張型別,也可以通過Rectangle pageSize = new Rectangle(144, 720);自定義紙張。通過Rectangle方法rotate()可以將頁面設定成橫向。
書寫器(Writer)物件
一旦文件(document)物件建立好之後,需要建立一個或多個書寫器(Writer)物件與之關聯。通過書寫器(Writer)物件可以將具體文件存檔成需要的格式,如com.lowagie.text.PDF.PDFWriter可以將文件存成PDF檔案,com.lowagie.text.html.HtmlWriter可以將文件存成html檔案。
設定文件屬性
在文件開啟之前,可以設定文件的標題、主題、作者、關鍵字、裝訂方式、建立者、生產者、建立日期等屬性,呼叫的方法分別是:
public boolean addTitle(String title)
public boolean addSubject(String subject)
public boolean addKeywords(String keywords)
public boolean addAuthor(String author)
public boolean addCreator(String creator)
public boolean addProducer()
public boolean addCreationDate()
public boolean addHeader(String name, String content)
其中方法addHeader對於PDF文件無效,addHeader僅對html文件有效,用於新增文件的頭資訊。
當新的頁面產生之前,可以設定頁面的大小、書籤、腳註(HeaderFooter)等資訊,呼叫的方法是:
public boolean setPageSize(Rectangle pageSize)
public boolean add(Watermark watermark)
public void removeWatermark()
public void setHeader(HeaderFooter header)
public void resetHeader()
public void setFooter(HeaderFooter footer)
public void resetFooter()
public void resetPageCount()
public void setPageCount(int pageN)
如果要設定第一頁的頁面屬性,這些方法必須在文件開啟之前呼叫。
對於PDF文件,iText還提供了文件的顯示屬性,通過呼叫書寫器的setViewerPreferences方法可以控制文件開啟時Acrobat Reader的顯示屬性,如是否單頁顯示、是否全屏顯示、是否隱藏狀態條等屬性。
另外,iText也提供了對PDF檔案的安全保護,通過書寫器(Writer)的setEncryption方法,可以設定文件的使用者口令、只讀、可列印等屬性。
新增文件內容
所有向文件新增的內容都是以物件為單位的,如Phrase、Paragraph、Table、Graphic物件等。比較常用的是段落(Paragraph)物件,用於向文件中新增一段文字。
四、文字處理
iText中用文字塊(Chunk)、短語(Phrase)和段落(paragraph)處理文字。
文字塊(Chunk)是處理文字的最小單位,有一串帶格式(包括字型、顏色、大小)的字串組成。如以下程式碼就是產生一個字型為HELVETICA、大小為10、帶下劃線的字串:
Chunk chunk1 = new Chunk("This text is underlined", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.UNDERLINE));
短語(Phrase)由一個或多個文字塊(Chunk)組成,短語(Phrase)也可以設定字型,但對於其中以設定過字型的文字塊(Chunk)無效。通過短語(Phrase)成員函式add可以將一個文字塊(Chunk)加到短語(Phrase)中,如:phrase6.add(chunk);
段落(paragraph)由一個或多個文字塊(Chunk)或短語(Phrase)組成,相當於WORD文件中的段落概念,同樣可以設定段落的字型大小、顏色等屬性。另外也可以設定段落的首行縮排、對齊方式(左對齊、右對齊、居中對齊)。通過函式setAlignment可以設定段落的對齊方式,setAlignment的引數1為居中對齊、2為右對齊、3為左對齊,預設為左對齊。
五、表格處理
iText中處理表格的類為:com.lowagie.text.Table和com.lowagie.text.PDF.PDFPTable,對於比較簡單的表格處理可以用com.lowagie.text.Table,但是如果要處理複雜的表格,這就需要com.lowagie.text.PDF.PDFPTable進行處理。這裡就類com.lowagie.text.Table進行說明。
類com.lowagie.text.Table的建構函式有三個:
①Table (int columns)
②Table(int columns, int rows)
③Table(Properties attributes)
引數columns、rows、attributes分別為表格的列數、行數、表格屬性。建立表格時必須指定表格的列數,而對於行數可以不用指定。
建立表格之後,可以設定表格的屬性,如:邊框寬度、邊框顏色、襯距(padding space 即單元格之間的間距)大小等屬性。下面通過一個簡單的例子說明如何使用表格,程式碼如下:
1 Table table = new Table(3); 2 table.setBorderWidth(1); 3 table.setBorderColor(new Color(0, 0, 255)); 4 table.setPadding(5); 5 table.setSpacing(5); 6 Cell cell = new Cell("header"); 7 cell.setHeader(true); 8 cell.setColspan(3); 9 table.addCell(cell); 10 table.endHeaders(); 11 cell = new Cell("example cell with colspan 1 and rowspan 2"); 12 cell.setRowspan(2); 13 cell.setBorderColor(new Color(255, 0, 0)); 14 table.addCell(cell); 15 table.addCell("1.1"); 16 table.addCell("2.1"); 17 table.addCell("1.2"); 18 table.addCell("2.2"); 19 table.addCell("cell test1"); 20 cell = new Cell("big cell"); 21 cell.setRowspan(2); 22 cell.setColspan(2); 23 table.addCell(cell); 24 table.addCell("cell test2");
執行結果如下:
header cell test2
程式碼1-5行用於新建一個表格,如程式碼所示,建立了一個列數為3的表格,並將邊框寬度設為1,顏色為藍色,襯距為5。
程式碼6-10行用於設定表格的表頭,第7行cell.setHeader(true);是將該單元格作為表頭資訊顯示;第8行cell.setColspan(3);指定了該單元格佔3列;為表格新增表頭資訊時,要注意的是一旦表頭資訊新增完了之後,必須呼叫endHeaders()方法,如第10行,否則當表格跨頁後,表頭資訊不會再顯示。
程式碼11-14行是向表格中新增一個寬度佔一列,長度佔二行的單元格。
往表格中新增單元格(cell)時,按自左向右、從上而下的次序新增。如執行完11行程式碼後,表格的右下方出現2行2列的空白,這是再往表格新增單元格時,先填滿這個空白,然後再另起一行,15-24行程式碼說明了這種新增順序。
六、影象處理
iText中處理表格的類為com.lowagie.text.Image,目前iText支援的影象格式有:GIF, Jpeg, PNG, wmf等格式,對於不同的影象格式,iText用同樣的建構函式自動識別影象格式。通過下面的程式碼分別獲得gif、jpg、png影象的例項。
Image gif = Image.getInstance("vonnegut.gif");
Image jpeg = Image.getInstance("myKids.jpg");
Image png = Image.getInstance("hitchcock.png");
影象的位置
影象的位置主要是指影象在文件中的對齊方式、影象和文字的位置關係。IText中通過函式public void setAlignment(int alignment)進行處理,引數alignment為Image.RIGHT、Image.MIDDLE、Image.LEFT分別指右對齊、居中、左對齊;當引數alignment為Image.TEXTWRAP、Image.UNDERLYING分別指文字繞圖形顯示、圖形作為文字的背景顯示。這兩種引數可以結合以達到預期的效果,如setAlignment(Image.RIGHT|Image.TEXTWRAP)顯示的效果為影象右對齊,文字圍繞影象顯示。
影象的尺寸和旋轉
如果影象在文件中不按原尺寸顯示,可以通過下面的函式進行設定:
public void scaleAbsolute(int newWidth, int newHeight)
public void scalePercent(int percent)
public void scalePercent(int percentX, int percentY)
函式public void scaleAbsolute(int newWidth, int newHeight)直接設定顯示尺寸;函式public void scalePercent(int percent)設定顯示比例,如scalePercent(50)表示顯示的大小為原尺寸的50%;而函式scalePercent(int percentX, int percentY)則影象高寬的顯示比例。
如果影象需要旋轉一定角度之後在文件中顯示,可以通過函式public void setRotation(double r)設定,引數r為弧度,如果旋轉角度為30度,則引數r= Math.PI / 6。
七、中文處理
預設的iText字型設定不支援中文字型,需要下載遠東字型包iTextAsian.jar,否則不能往PDF文件中輸出中文字型。通過下面的程式碼就可以在文件中使用中文了:
BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
com.lowagie.text.Font FontChinese = new com.lowagie.text.Font(bfChinese, 12, com.lowagie.text.Font.NORMAL);
Paragraph pragraph=new Paragraph("你好", FontChinese);
八、分頁處理
如果只是簡單的顯示當前頁碼,使用以下程式碼即可(設定了頁面的大小後,會自動分頁)。
1 HeaderFooter footer = new HeaderFooter(new Phrase("頁碼:",keyfont), true); 2 footer.setBorder(Rectangle.NO_BORDER); 3 document.setHeader(footer);
如果要顯示當前頁碼以及總頁碼。
則需要計算總頁數,設定每頁大小,使用pdf.newPage( )手動分頁。
詳見一下程式碼:
1 package com.foster; 2 3 import java.io.File; 4 import java.io.FileOutputStream; 5 import java.util.ArrayList; 6 import java.util.List; 7 8 import com.lowagie.text.Cell; 9 import com.lowagie.text.Document; 10 import com.lowagie.text.DocumentException; 11 import com.lowagie.text.Font; 12 import com.lowagie.text.HeaderFooter; 13 import com.lowagie.text.Image; 14 import com.lowagie.text.Paragraph; 15 import com.lowagie.text.Table; 16 import com.lowagie.text.pdf.BaseFont; 17 import com.lowagie.text.pdf.PdfPCell; 18 import com.lowagie.text.pdf.PdfWriter; 19 20 public class PDFReport { 21 22 23 public static void main(String[] args) throws Exception, DocumentException { 24 25 List<String> ponum=new ArrayList<String>(); 26 add(ponum, 26); 27 List<String> line=new ArrayList<String>(); 28 add(line, 26); 29 List<String> part=new ArrayList<String>(); 30 add(part, 26); 31 List<String> description=new ArrayList<String>(); 32 add(description, 26); 33 List<String> origin=new ArrayList<String>(); 34 add(origin, 26); 35 36 //Create Document Instance 37 Document document=new Document(); 38 39 //add Chinese font 40 BaseFont bfChinese=BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); 41 42 //Font headfont=new Font(bfChinese,10,Font.BOLD); 43 Font keyfont=new Font(bfChinese,8,Font.BOLD); 44 Font textfont=new Font(bfChinese,8,Font.NORMAL); 45 46 //Create Writer associated with document 47 PdfWriter.getInstance(document, new FileOutputStream(new File("D:\\POReceiveReport.pdf"))); 48 49 document.open(); 50 51 //Seperate Page controller 52 int recordPerPage=10; 53 int fullPageRequired=ponum.size()/recordPerPage; 54 int remainPage=ponum.size()%recordPerPage>1?1:0; 55 int totalPage=fullPageRequired+remainPage; 56 57 for(int j=0;j<totalPage;j++){ 58 document.newPage(); 59 60 //create page number 61 String pageNo=leftPad("頁碼: "+(j+1)+" / "+totalPage,615); 62 Paragraph pageNumber=new Paragraph(pageNo, keyfont) ; 63 document.add(pageNumber); 64 65 //create title image 66 Image jpeg=Image.getInstance("D:\\title.JPG"); 67 jpeg.setAlignment(Image.ALIGN_CENTER); 68 jpeg.scaleAbsolute(530, 37); 69 document.add(jpeg); 70 71 //header information 72 Table tHeader=new Table(2); 73 float[] widthsHeader={2f,3f}; 74 tHeader.setWidths(widthsHeader); 75 tHeader.setWidth(100); 76 tHeader.getDefaultCell().setBorder(PdfPCell.NO_BORDER); 77 78 79 String compAdd="河源市高新技術開發區興業大道中66號"; 80 String company="豐達音響(河源)有限公司"; 81 String vendor="V006"; 82 String vendorName="中山市盧氏五金有限公司"; 83 String ccn="FHH"; 84 String mas_loc="FHH"; 85 String delivery_note="20130718001"; 86 String receive_date="20130718"; 87 String dept="H11"; 88 String asn="0123456789"; 89 90 91 Cell c1Header=new Cell(new Paragraph("地址:"+compAdd,keyfont)); 92 tHeader.addCell(c1Header); 93 c1Header=new Cell(new Paragraph("供應商:"+vendor,keyfont)); 94 tHeader.addCell(c1Header); 95 c1Header=new Cell(new Paragraph("公司:"+company,keyfont)); 96 tHeader.addCell(c1Header); 97 c1Header=new Cell(new Paragraph("供應商工廠:"+vendorName,keyfont)); 98 tHeader.addCell(c1Header); 99 c1Header = new Cell(new Paragraph("CCN: "+ccn+" Master Loc: "+mas_loc,keyfont)); 100 tHeader.addCell(c1Header); 101 c1Header = new Cell(new Paragraph("送貨編號: "+delivery_note+" 送貨日期: "+receive_date,keyfont)); 102 tHeader.addCell(c1Header); 103 c1Header=new Cell(new Paragraph("Dept:"+dept,keyfont)); 104 tHeader.addCell(c1Header); 105 c1Header=new Cell(new Paragraph("ASN#:"+asn,keyfont)); 106 tHeader.addCell(c1Header); 107 document.add(tHeader); 108 109 //record header field 110 Table t=new Table(5); 111 float[] widths={1.5f,1f,1f,1.5f,1f}; 112 t.setWidths(widths); 113 t.setWidth(100); 114 t.getDefaultCell().setBorder(PdfPCell.NO_BORDER); 115 Cell c1 = new Cell(new Paragraph("PO#",keyfont)); 116 t.addCell(c1); 117 c1 = new Cell(new Paragraph("Line",keyfont)); 118 t.addCell(c1); 119 c1 = new Cell(new Paragraph("Part#",keyfont)); 120 t.addCell(c1); 121 c1 = new Cell(new Paragraph("Description",keyfont)); 122 t.addCell(c1); 123 c1 = new Cell(new Paragraph("Origin",keyfont)); 124 t.addCell(c1); 125 126 //calculate the real records within a page ,to calculate the last record number of every page 127 int maxRecordInPage= j+1 ==totalPage ? (remainPage==0?recordPerPage:(ponum.size()%recordPerPage)):recordPerPage; 128 129 for(int i=j*recordPerPage;i<((j*recordPerPage)+maxRecordInPage);i++){ 130 Cell c2=new Cell(new Paragraph(ponum.get(i), textfont)); 131 t.addCell(c2); 132 c2=new Cell(new Paragraph(line.get(i), textfont)); 133 t.addCell(c2); 134 c2=new Cell(new Paragraph(part.get(i), textfont)); 135 t.addCell(c2); 136 c2=new Cell(new Paragraph(description.get(i), textfont)); 137 t.addCell(c2); 138 c2=new Cell(new Paragraph(origin.get(i), textfont)); 139 t.addCell(c2); 140 } 141 document.add(t); 142 143 if(j+1==totalPage){ 144 145 Paragraph foot11 = new Paragraph("檔案只作 Foster 收貨用"+printBlank(150)+"__________________________",keyfont); 146 document.add(foot11); 147 Paragraph foot12 = new Paragraph("Printed from Foster supplier portal"+printBlank(134)+company+printBlank(40)+"版本: 1.0",keyfont); 148 document.add(foot12); 149 HeaderFooter footer11=new HeaderFooter(foot11, true); 150 footer11.setAlignment(HeaderFooter.ALIGN_BOTTOM); 151 HeaderFooter footer12=new HeaderFooter(foot12, true); 152 footer12.setAlignment(HeaderFooter.ALIGN_BOTTOM); 153 } 154 } 155 document.close(); 156 } 157 158 public static String leftPad(String str, int i) { 159 int addSpaceNo = i-str.length(); 160 String space = ""; 161 for (int k=0; k<addSpaceNo; k++){ 162 space= " "+space; 163 }; 164 String result =space + str ; 165 return result; 166 } 167 168 public static void add(List<String> list,int num){ 169 for(int i=0;i<num;i++){ 170 list.add("test"+i); 171 } 172 } 173 174 public static String printBlank(int tmp){ 175 String space=""; 176 for(int m=0;m<tmp;m++){ 177 space=space+" "; 178 } 179 return space; 180 } 181 182 }
為了使副標題嚴格對齊,使用了表格table進行控制,但是卻沒能找到去掉表格邊框的方法..........鬱悶.......
九、總結
總的來說,iText是一套java環境下不錯的製作PDF的元件。因為iText支援jsp/javabean下的開發,這使得B/S應用中的報表問題能得到很好的解決。由於iText畢竟不是專門為製作報表設計,所有報表中的內容、格式都需要通過寫程式碼實現,相對於那些專業的支援視覺化設計的報表軟體來說,程式設計的工作量就有一定程度的增加。