很久没更新 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 的关系,经过反复调试,有点效果了。把代码贴出来,做个标记吧。以免以后又抓狂。
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)),主要是找不到如何输出空白行,所以只好出此下策。如果哪位有解法,请告知一下。
做技术的确不能太着急,慢慢来,总会找到出口的。