利用 iText 实现 PDF 报表上载 (转)

利用 iText 实现 PDF 报表下载 (转)很久没更新 blog 了,工作和一些事情占用了大部分精力,实在是身不由己。

利用 iText 实现 PDF 报表下载 (转)
很久没更新 blog 了,工作和一些事情占用了大部分精力,实在是身不由己。今天终于有空整理一下最近用到的东西。

有个朋友的项目需要用到 PDF 报表下载,之前我只做过 Excel 的,相信再做一次 PDF 的下载一定很有趣吧。在网上找了一大圈,似乎 iText 比较符合我的要求,而且这个工具很早很早以前就有了,生命力很旺盛。进入 iText 的主页(http://www.lowagie.com/iText/),发现作者很勤劳,最近2个月都有新版本发布。哪知道现在高兴得太早了,一堆问题接踵而至。

下载倒是很简单,一个iText-2.1.4.jar搞定,然后去找入门文档,进了文档页面,一股浓郁的商业气氛迎面而来,这里只提供了部分文档,除非去买"iText in Action",随后被踢到 iText by Example 页面。好吧,既然这是老牌工具了,肯定有不少中文资料吧,找了一圈,没发现什么和生成并下载相关的 out of box 文档,很多都是经验性的总结和进阶文章。无奈又啃 iText by Example,在这里找到些有用的资源,iText in a Web Application正是我要找的,不过这个例子很简单。通过 Google 之后,又发现要下载一个 CJK 的包(iTextAsian.jar)才能正确显示中文,好吧我去找。很幸运的是在 iText by Example 里找到了这个 jar 的 link,兴致勃勃的跑去下载,结果这是个无效链接,最后在 sourceForge 上才找到,不容易啊。解决了这些问题,想必能够安稳的使用了吧,由于这个项目比较急,没什么耐心一个个的翻阅 iText by Example,想找点捷径,据说 iText 可以从 html 直接生成 PDF,窃喜!找了 apache common 的 httpclient,动态模拟 http 请求来抓 html,根据控制台的 print,的确把 html 抓到了,然后开始转换到 PDF,先解决了中文显示问题,可是后面的问题解决不了了,html 的 table 和 div 这些,转换到 PDF 都走样了... ...

很不爽,看来还是只有老老实实的啃 iText by Example实在点。这次稍微耐心点,一点点的看,首先搞清楚了它的 Font 设置,然后是 Table 和 Cell 的关系,经过反复调试,有点效果了。把代码贴出来,做个标记吧。以免以后又抓狂。

1 package org.rosenjiang.servlet;
  2
  3 import java.awt.Color;
  4 import java.io.IOException;
  5 import java.util.HashMap;
  6 import java.util.List;
  7 import java.util.Map;
  8
  9 import javax.servlet.ServletException;
10 import javax.servlet.http.HttpServlet;
11 import javax.servlet.http.HttpServletRequest;
12 import javax.servlet.http.HttpServletResponse;
13
14 import org.springframework.web.context.WebApplicationContext;
15 import org.springframework.web.context.support.WebApplicationContextUtils;
16
17 import org.rosenjiang.service.UserService;
18 import com.lowagie.text.Document;
19 import com.lowagie.text.DocumentException;
20 import com.lowagie.text.Font;
21 import com.lowagie.text.Paragraph;
22 import com.lowagie.text.pdf.BaseFont;
23 import com.lowagie.text.pdf.PdfPCell;
24 import com.lowagie.text.pdf.PdfPTable;
25 import com.lowagie.text.pdf.PdfWriter;
26
27 /*
28  * ReportServlet
29  * @author rosen jiang
30  * @since 2008-12
31   */
32 public class ReportServlet extends HttpServlet {
33       
34     /**
35      * Return a PDF document for download.
36      *
37      */
38     public void doGet (HttpServletRequest request, HttpServletResponse response)
39     throws IOException, ServletException {
40         String account_id = request.getParameter("account_id");
41         String search_date_from = request.getParameter("search_date_from");
42         String to = request.getParameter("to");
43         WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
44         UserService userService = (UserService)ctx.getBean("userService");
45         List<Map<String, Object>> list = userService.getAccountActivity(account_id, search_date_from, to);
46         // create PDF document
47         Document document = new Document();
48         try {
49             //set response info
50             response.setContentType("application/x-msdownload;charset=UTF-8");
51             response.setHeader("Content-Disposition","attachment;filename=report.pdf");
52             //open output stream
53             PdfWriter.getInstance(document, response.getOutputStream());
54             // open PDF document
55             document.open();
56             // set chinese font
57             BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); 
58             Font f2 = new Font(bfChinese, 2, Font.NORMAL);
59             Font f6 = new Font(bfChinese, 6, Font.NORMAL);
60             Font f8 = new Font(bfChinese, 8, Font.NORMAL);
61             Font f10 = new Font(bfChinese, 10, Font.NORMAL);
62             Font f12 = new Font(bfChinese, 12, Font.BOLD);
63             //set title
64             document.add(new Paragraph("金融报表", f12));
65             //<br>
66             document.add(new Paragraph(" ",f6));
67             //set sub title
68             document.add(new Paragraph("账户信息", f10));
69             //<br>
70             document.add(new Paragraph(" ", f2));
71             //process business data
72             if(list.size()>0 && list.get(0).get("bankbook_no")!=null){
73                 float openBalance = 0;
74                 //create table with 7 columns
75                 PdfPTable table = new PdfPTable(7);
76                 //100% width
77                 table.setWidthPercentage(100);
78                 table.setHorizontalAlignment(PdfPTable.ALIGN_LEFT);
79                 //create cells
80                 PdfPCell cell = new PdfPCell();
81                 //set color
82                 cell.setBackgroundColor(new Color(213, 141, 69));
83                 cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);
84                 //
85                 cell.setPhrase(new Paragraph("交易日", f8));
86                 table.addCell(cell);
87                 cell.setPhrase(new Paragraph("类型", f8));
88                 table.addCell(cell);
89                 cell.setPhrase(new Paragraph("备注", f8));
90                 table.addCell(cell);
91                 cell.setPhrase(new Paragraph("ID", f8));
92                 table.addCell(cell);
93                 cell.setPhrase(new Paragraph("票号", f8));
94                 table.addCell(cell);
95                 cell.setPhrase(new Paragraph("合计", f8));
96                 table.addCell(cell);
97                 cell.setPhrase(new Paragraph("余额", f8));
98                 table.addCell(cell);
99                 //create another cell
100                 PdfPCell newcell = new PdfPCell();
101                 newcell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);
102                
103                 Map<String, Object> map = new HashMap<String, Object>();
104                 for(int i = 0; i < list.size(); i++){
105                     map = list.get(i);
106                     String cashInout = map.get("cash_inout").toString();
107                     newcell.setPhrase(new Paragraph(map.get("trade_date").toString(), f8));
108                     table.addCell(newcell);
109                     newcell.setPhrase(new Paragraph(map.get("bankbook_type").toString(), f8));
110                     table.addCell(newcell);
111                     newcell.setPhrase(new Paragraph(map.get("memo").toString(), f8));
112                     table.addCell(newcell);
113                     newcell.setPhrase(new Paragraph(map.get("account_id").toString(), f8));
114                     table.addCell(newcell);
115                     newcell.setPhrase(new Paragraph(map.get("ticket_no").toString(), f8));
116                     table.addCell(newcell);
117                     newcell.setPhrase(new Paragraph(map.get("amount").toString(), f8));
118                     table.addCell(newcell);
119                     newcell.setPhrase(new Paragraph(openBalance+"", f8));
120                     table.addCell(newcell);
121                     if(cashInout.equals("I")){
122                         openBalance = openBalance + Float.valueOf(map.get("amount").toString());
123                     }else if(cashInout.equals("O")){
124                         openBalance = openBalance - Float.valueOf(map.get("amount").toString());
125                     }
126                 }
127                 //print total column
128                 newcell.setPhrase(new Paragraph("合计"+openBalance, f8));
129                 table.addCell("");
130                 table.addCell("");
131                 table.addCell("");
132                 table.addCell("");
133                 table.addCell("");
134                 table.addCell("");
135                 table.addCell(newcell);
136                 document.add(table);
137             }else{
138                 PdfPTable table = new PdfPTable(1);
139                 table.setWidthPercentage(100);
140                 table.setHorizontalAlignment(PdfPTable.ALIGN_LEFT);
141                 PdfPCell cell = new PdfPCell(new Paragraph("暂无数据"));
142                 cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);
143                 table.addCell(cell);
144                 document.add(table);
145             }
146         }
147         catch(DocumentException de) {
148             de.printStackTrace();
149             System.err.println("document: " + de.getMessage());
150         }finally{
151             // close the document and the outputstream is also closed internally
152             document.close();
153         }       
154     }
155 }




代码结构清晰,本来也没什么东西,就是通过 Spring 调用 service 方法,获取数据后按照 iText 结构输出即可。不过代码里面有个很愚蠢的动作:document.add(new Paragraph(" ",f6)),主要是找不到如何输出空白行,所以只好出此下策。如果哪位有解法,请告知一下。

做技术的确不能太着急,慢慢来,总会找到出口的。