flying saucer技术生成pdf文档的实现代码:

Servlet方式:

html代码:

[html] view plaincopyprint?
  1. <%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
  2. <%
  3. String path = request.getContextPath();
  4. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
  5. %>
  6. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  7. <html>
  8. <head>
  9. <base href="<%=basePath%>">
  10. <title>Html2PdfServlet</title>
  11. <meta http-equiv="pragma" content="no-cache">
  12. <meta http-equiv="cache-control" content="no-cache">
  13. <meta http-equiv="expires" content="0">
  14. <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
  15. <meta http-equiv="description" content="This is my page">
  16. <!--
  17. <link rel="stylesheet" type="text/css" href="styles.css">
  18. -->
  19. </head>
  20. <body>
  21. <form action="http://localhost:8081/createpdf/servlet/html2PdfServlet" method="get" >
  22. <table>
  23. <tr>
  24. <td>
  25. <input type="text" id="username" name="username" value="" />
  26. </td>
  27. <td>
  28. <input type="submit" id="submit" name="submit" value="submit" />
  29. </td>
  30. </tr>
  31. </table>
  32. </form>
  33. </body>
  34. </html>
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head><base href="<%=basePath%>"><title>Html2PdfServlet</title><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0">    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"><meta http-equiv="description" content="This is my page"><!--<link rel="stylesheet" type="text/css" href="styles.css">--></head><body><form action="http://localhost:8081/createpdf/servlet/html2PdfServlet" method="get" ><table><tr><td><input type="text" id="username" name="username" value="" /></td><td><input type="submit" id="submit" name="submit" value="submit" /></td></tr></table></form></body>
</html>

java代码:

[java] view plaincopyprint?
  1. package com.test;
  2. import java.io.IOException;
  3. import java.io.OutputStream;
  4. import javax.servlet.ServletContext;
  5. import javax.servlet.ServletException;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import org.xhtmlrenderer.pdf.ITextFontResolver;
  10. import org.xhtmlrenderer.pdf.ITextRenderer;
  11. import com.lowagie.text.pdf.BaseFont;
  12. public class Html2PdfServlet extends HttpServlet {
  13. private static final long serialVersionUID = 1L;
  14. public void doGet(HttpServletRequest request, HttpServletResponse response)
  15. throws ServletException, IOException {
  16. //pageContext.getServletContext().getRealPath("/")
  17. ServletContext sc = request.getSession().getServletContext();
  18. String path = sc.getRealPath(""); //值为D:\apache-tomcat-6.0.26\webapps\createpdf
  19. System.out.println("原path: " + path);
  20. //把路径中的反斜杠转成正斜杠
  21. path = path.replaceAll("\\\\", "/"); //值为D:/apache-tomcat-6.0.26/webapps/createpdf
  22. System.out.println(path);
  23. String path2 = sc.getRealPath("/");
  24. System.out.println("path2: " + path2);
  25. System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));
  26. System.out.println("request.getRequestURI: " + request.getRequestURI());
  27. //获取使用的端口号
  28. System.out.println(request.getLocalPort());
  29. String path3 = request.getContextPath();
  30. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path3+"/";
  31. System.out.println("basepath: " + basePath);
  32. response.setContentType("application/pdf");
  33. //response.setHeader("Content-Disposition", "attachment; filename=WebReport.pdf");
  34. response.setHeader("Content-Disposition""inline; filename=WebReport.pdf");
  35. StringBuffer html = new StringBuffer();
  36. //组装成符合W3C标准的html文件,否则不能正确解析
  37. html.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
  38. html.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">")
  39. .append("<head>")
  40. .append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />")
  41. .append("<style type=\"text/css\" mce_bogus=\"1\">body {font-family: SimSun;}</style>")
  42. .append("<style type=\"text/css\">img {width: 700px;}</style>")
  43. .append("</head>")
  44. .append("<body>");
  45. html.append("<center><h1>统计报表</h1></center>");
  46. html.append("<center>");
  47. html.append("<img src=\"images/chart.jpg\"/>");
  48. html.append("</center>");
  49. html.append("</body></html>");
  50. // parse our markup into an xml Document
  51. try {
  52. ITextRenderer renderer = new ITextRenderer();
  53. /**
  54. * 引入了新的jar包,不用再导入字体了
  55. ITextFontResolver fontResolver = renderer.getFontResolver();
  56. fontResolver.addFont("C:/Windows/fonts/simsun.ttc",
  57. BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
  58. */
  59. renderer.setDocumentFromString(html.toString());
  60. // 解决图片的相对路径问题
  61. //renderer.getSharedContext().setBaseURL("file:/C:/Documents and Settings/dashan.yin/workspace/createpdf/WebRoot/images");
  62. //renderer.getSharedContext().setBaseURL("file:/D:/apache-tomcat-6.0.26/webapps/createpdf/images");
  63. renderer.getSharedContext().setBaseURL("file:/" + path + "/images");
  64. renderer.layout();
  65. OutputStream os = response.getOutputStream();
  66. renderer.createPDF(os);
  67. os.close();
  68. catch (Exception e) {
  69. e.printStackTrace();
  70. }
  71. }
  72. public void doPost(HttpServletRequest request, HttpServletResponse response)
  73. throws ServletException, IOException {
  74. doGet(request, response);
  75. }
  76. }
package com.test;import java.io.IOException;
import java.io.OutputStream;import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;import com.lowagie.text.pdf.BaseFont;public class Html2PdfServlet extends HttpServlet {private static final long serialVersionUID = 1L;public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//pageContext.getServletContext().getRealPath("/")ServletContext sc = request.getSession().getServletContext();String path = sc.getRealPath(""); //值为D:\apache-tomcat-6.0.26\webapps\createpdfSystem.out.println("原path: " + path);//把路径中的反斜杠转成正斜杠path = path.replaceAll("\\\\", "/"); //值为D:/apache-tomcat-6.0.26/webapps/createpdfSystem.out.println(path);String path2 = sc.getRealPath("/");System.out.println("path2: " + path2);System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));System.out.println("request.getRequestURI: " + request.getRequestURI());//获取使用的端口号System.out.println(request.getLocalPort());String path3 = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path3+"/";System.out.println("basepath: " + basePath);response.setContentType("application/pdf");//response.setHeader("Content-Disposition", "attachment; filename=WebReport.pdf");response.setHeader("Content-Disposition", "inline; filename=WebReport.pdf");StringBuffer html = new StringBuffer();//组装成符合W3C标准的html文件,否则不能正确解析html.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");html.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">").append("<head>").append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />").append("<style type=\"text/css\" mce_bogus=\"1\">body {font-family: SimSun;}</style>").append("<style type=\"text/css\">img {width: 700px;}</style>").append("</head>").append("<body>");html.append("<center><h1>统计报表</h1></center>");html.append("<center>");html.append("<img src=\"images/chart.jpg\"/>");html.append("</center>");html.append("</body></html>");// parse our markup into an xml Documenttry {ITextRenderer renderer = new ITextRenderer();/*** 引入了新的jar包,不用再导入字体了ITextFontResolver fontResolver = renderer.getFontResolver();fontResolver.addFont("C:/Windows/fonts/simsun.ttc",BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);*/renderer.setDocumentFromString(html.toString());// 解决图片的相对路径问题//renderer.getSharedContext().setBaseURL("file:/C:/Documents and Settings/dashan.yin/workspace/createpdf/WebRoot/images");//renderer.getSharedContext().setBaseURL("file:/D:/apache-tomcat-6.0.26/webapps/createpdf/images");renderer.getSharedContext().setBaseURL("file:/" + path + "/images");renderer.layout();OutputStream os = response.getOutputStream();renderer.createPDF(os);os.close();} catch (Exception e) {e.printStackTrace();}}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}

Struts1形式:

html代码:

[html] view plaincopyprint?
  1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
  2. <%@ page import="Config.application.*" %>
  3. <%@ include file="/pages/common/global.jsp" %>
  4. <%@ taglib uri="/WEB-INF/tlds/birt.tld" prefix="birt"%>
  5. <script type="text/javascript">
  6. function interactivity() {
  7. var url = contextName + "/viewAction.do?method=viewLinkResult";
  8. var startDate_s = $("#startDate_s").val();
  9. var endDate_s = $("#endDate_s").val();
  10. var serviceInstance = $("#serviceInstance").val();
  11. var serviceInstance = encodeURIComponent(serviceInstance);
  12. var status = $("#status").val();
  13. if(startDate_s){
  14. url += "&startDate_s=" + startDate_s;
  15. }
  16. if(endDate_s){
  17. url += "&endDate_s=" + endDate_s;
  18. }
  19. if(serviceInstance){
  20. url += "&serviceInstance=" + serviceInstance;
  21. }
  22. if(status){
  23. url += "&status=" + status;
  24. }
  25. url += "&random=" + Math.random();
  26. //alert(url);
  27. retrieveURL(url,'viewResult');
  28. //tipsWindown("详细信息","url:get?" + url,"800","700","true","","true","text");
  29. }
  30. function viewreturn() {
  31. var url = contextName + "/viewAction.do?method=viewReturnResult";
  32. //时间参数设空
  33. document.getElementById("startDate_s").value = "";
  34. document.getElementById("endDate_s").value = "";
  35. url += "&random=" + Math.random();
  36. retrieveURL(url,'viewResult');
  37. //tipsWindown("详细信息","url:get?" + url,"800","700","true","","true","text");
  38. }
  39. </script>
  40. <html:form action="/viewAction.do?method=viewExportPDF">
  41. <table class="form_t" style="width:100%">
  42. <tr>
  43. <td>
  44. <input type="submit" id="submit" name="submit" value="导出" class="button4C"/>
  45. </td>
  46. </tr>
  47. </table>
  48. </html:form>
  49. <html:form action="/viewAction.do?method=viewResult"  >
  50. <table class="form_t" style="width:100%">
  51. <tr>
  52. <th class="tablelogo" colspan="3">
  53. 统计时间
  54. </th>
  55. </tr>
  56. <tr>
  57. <td style="width: 175px; text-align: right;">
  58. 开始时间
  59. </td>
  60. <td>
  61. <input type="text" name="startDate_s" id="startDate_s"
  62. class="Wdate"
  63. onclick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss',maxDate:'#F{$dp.$D(\'endDate_s\')||\'%y-%M-%d %H:{%m-1}:%s\'}'})" />
  64. </td>
  65. <td>
  66. <input type="hidden" name="serviceInstance" id="serviceInstance" value=""/>
  67. <input type="hidden" name="status" id="status" value=""/>
  68. </td>
  69. </tr>
  70. <tr>
  71. <td style="width: 175px; text-align: right;">
  72. 结束时间
  73. </td>
  74. <td>
  75. <input type="text" name="endDate_s" id="endDate_s" class="Wdate"
  76. onclick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss',minDate:'#F{$dp.$D(\'startDate_s\')}',maxDate:'%y-%M-%d %H:%m:%s'})" />
  77. </td>
  78. <td>
  79. </td>
  80. </tr>
  81. <tr>
  82. <td style="padding-left: 175px;" colspan="2">
  83. <input type="button" value="查看" class="button4C"
  84. onclick="javascript:viewForm(this.form,'viewResult');" />
  85. <input type="button" value="返回" class="button4C"
  86. onclick="javascript:viewreturn();" />
  87. </td>
  88. <td>
  89. </td>
  90. </tr>
  91. </table>
  92. </html:form>
  93. <table width="100%">
  94. <tr>
  95. <td id="viewResult" width="100%"></td>
  96. </tr>
  97. </table>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page import="Config.application.*" %>
<%@ include file="/pages/common/global.jsp" %>
<%@ taglib uri="/WEB-INF/tlds/birt.tld" prefix="birt"%>
<script type="text/javascript">function interactivity() {var url = contextName + "/viewAction.do?method=viewLinkResult";var startDate_s = $("#startDate_s").val();var endDate_s = $("#endDate_s").val();var serviceInstance = $("#serviceInstance").val();var serviceInstance = encodeURIComponent(serviceInstance);var status = $("#status").val();if(startDate_s){url += "&startDate_s=" + startDate_s;}if(endDate_s){url += "&endDate_s=" + endDate_s;}if(serviceInstance){url += "&serviceInstance=" + serviceInstance;}if(status){url += "&status=" + status;}url += "&random=" + Math.random();//alert(url);retrieveURL(url,'viewResult');//tipsWindown("详细信息","url:get?" + url,"800","700","true","","true","text");}function viewreturn() {var url = contextName + "/viewAction.do?method=viewReturnResult";//时间参数设空document.getElementById("startDate_s").value = "";document.getElementById("endDate_s").value = "";url += "&random=" + Math.random();retrieveURL(url,'viewResult');//tipsWindown("详细信息","url:get?" + url,"800","700","true","","true","text");}</script><html:form action="/viewAction.do?method=viewExportPDF"><table class="form_t" style="width:100%"><tr><td><input type="submit" id="submit" name="submit" value="导出" class="button4C"/></td></tr></table>
</html:form>
<html:form action="/viewAction.do?method=viewResult"  ><table class="form_t" style="width:100%"><tr><th class="tablelogo" colspan="3">统计时间</th></tr><tr><td style="width: 175px; text-align: right;">开始时间</td><td> <input type="text" name="startDate_s" id="startDate_s"class="Wdate"οnclick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss',maxDate:'#F{$dp.$D(\'endDate_s\')||\'%y-%M-%d %H:{%m-1}:%s\'}'})" /></td><td><input type="hidden" name="serviceInstance" id="serviceInstance" value=""/><input type="hidden" name="status" id="status" value=""/></td></tr><tr><td style="width: 175px; text-align: right;">结束时间</td><td> <input type="text" name="endDate_s" id="endDate_s" class="Wdate"οnclick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss',minDate:'#F{$dp.$D(\'startDate_s\')}',maxDate:'%y-%M-%d %H:%m:%s'})" /></td><td></td></tr><tr><td style="padding-left: 175px;" colspan="2"><input type="button" value="查看" class="button4C"οnclick="javascript:viewForm(this.form,'viewResult');" /><input type="button" value="返回" class="button4C"οnclick="javascript:viewreturn();" /></td><td> </td></tr></table>
</html:form>
<table width="100%"><tr><td id="viewResult" width="100%"></td></tr>
</table>

java代码:

[java] view plaincopyprint?
  1. public void viewExportPDF(ActionMapping mapping, ActionForm form,
  2. HttpServletRequest request, HttpServletResponse response)
  3. throws Exception {
  4. ServletContext sc = request.getSession().getServletContext();
  5. String rootpath = sc.getRealPath(""); //值为D:\apache-tomcat-6.0.26\webapps\webmonitor
  6. //把路径中的反斜杠转成正斜杠
  7. rootpath = rootpath.replaceAll("\\\\", "/"); //值为D:/apache-tomcat-6.0.26/webapps/webmonitor
  8. //临时存储目录
  9. String pdfPathName = rootpath + "/WebReport.pdf";
  10. ArrayList<String> list = new ArrayList<String>();
  11. for(int i=0;i<outstreamlist.size();i++) {
  12. list.add(outstreamlist.get(i));
  13. }
  14. //调用方法
  15. boolean flag = createPdf(list, pdfPathName, request, response);
  16. if (flag == true) {
  17. //要实现另存为下载,必须满足两个条件:导入commons-upload.jar包,表单提交
  18. try {
  19. OutputStream out = response.getOutputStream();
  20. byte by[] = new byte[1024];
  21. File fileLoad = new File(pdfPathName);
  22. response.reset();
  23. response.setContentType("application/pdf");
  24. response.setHeader("Content-Disposition",
  25. "attachment; filename=WebReport.pdf");
  26. long fileLength = fileLoad.length();
  27. String length1 = String.valueOf(fileLength);
  28. response.setHeader("Content_Length", length1);
  29. FileInputStream in = new FileInputStream(fileLoad);
  30. int n;
  31. while ((n = in.read(by)) != -1) {
  32. out.write(by, 0, n);
  33. }
  34. in.close();
  35. out.flush();
  36. catch(Exception e) {
  37. e.printStackTrace();
  38. }
  39. else {
  40. renderText(response, ERR_MESSAGE);
  41. }
  42. return ;
  43. }
  44. //生成pdf
  45. public boolean createPdf(ArrayList<String> list, String pdfPathName,
  46. HttpServletRequest request, HttpServletResponse response)
  47. throws Exception {
  48. /**
  49. * 用rootpath指定目录也可以生成pdf文件,只不过不能在myeclipse的左边导航窗口中看不到而已
  50. * 左边导航窗口对应C盘目录下的workspace目录下程序
  51. * 用rootpath指定的目录是D盘Tomcat目录
  52. */
  53. ServletContext sc = request.getSession().getServletContext();
  54. String rootpath = sc.getRealPath(""); //值为D:\apache-tomcat-6.0.26\webapps\webmonitor
  55. //把路径中的反斜杠转成正斜杠
  56. rootpath = rootpath.replaceAll("\\\\", "/"); //值为D:/apache-tomcat-6.0.26/webapps/webmonitor
  57. boolean flag = false;
  58. String outputFile = pdfPathName;
  59. //指定目录导出文件
  60. OutputStream os = new FileOutputStream(outputFile);
  61. ITextRenderer renderer = new ITextRenderer();
  62. StringBuffer html = new StringBuffer();
  63. //组装成符合W3C标准的html文件,否则不能正确解析
  64. html.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
  65. html.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">")
  66. .append("<head>")
  67. .append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />")
  68. .append("<style type=\"text/css\" mce_bogus=\"1\">body {font-family: SimSun;}</style>")
  69. .append("<style type=\"text/css\">img {width: 500px;}</style>")
  70. .append("<style type=\"text/css\">table {font-size:13px;}</style>")
  71. .append("</head>")
  72. .append("<body>");
  73. html.append("<center>");
  74. html.append("<h1>统计报表</h1>");
  75. for(int i=0;i<list.size();i++) {
  76. html.append("<div>" + list.get(i) + "</div>");
  77. }
  78. html.append("</center>");
  79. html.append("</body></html>");
  80. try {
  81. renderer.setDocumentFromString(html.toString());
  82. // 解决图片的相对路径问题,图片路径必须以file开头
  83. renderer.getSharedContext().setBaseURL("file:/" + rootpath);
  84. renderer.layout();
  85. renderer.createPDF(os);
  86. os.close();
  87. flag = true;
  88. catch (Exception e) {
  89. flag = false;
  90. e.printStackTrace();
  91. }
  92. return flag;
  93. }
public void viewExportPDF(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response)throws Exception {ServletContext sc = request.getSession().getServletContext();String rootpath = sc.getRealPath(""); //值为D:\apache-tomcat-6.0.26\webapps\webmonitor//把路径中的反斜杠转成正斜杠rootpath = rootpath.replaceAll("\\\\", "/"); //值为D:/apache-tomcat-6.0.26/webapps/webmonitor//临时存储目录String pdfPathName = rootpath + "/WebReport.pdf";ArrayList<String> list = new ArrayList<String>();for(int i=0;i<outstreamlist.size();i++) {list.add(outstreamlist.get(i));}//调用方法boolean flag = createPdf(list, pdfPathName, request, response);if (flag == true) {//要实现另存为下载,必须满足两个条件:导入commons-upload.jar包,表单提交try {OutputStream out = response.getOutputStream();byte by[] = new byte[1024];File fileLoad = new File(pdfPathName);response.reset();response.setContentType("application/pdf");response.setHeader("Content-Disposition","attachment; filename=WebReport.pdf");long fileLength = fileLoad.length();String length1 = String.valueOf(fileLength);response.setHeader("Content_Length", length1);FileInputStream in = new FileInputStream(fileLoad);int n;while ((n = in.read(by)) != -1) {out.write(by, 0, n);}in.close();out.flush();} catch(Exception e) {e.printStackTrace();}} else {renderText(response, ERR_MESSAGE);}return ;}//生成pdfpublic boolean createPdf(ArrayList<String> list, String pdfPathName,HttpServletRequest request, HttpServletResponse response)throws Exception {/*** 用rootpath指定目录也可以生成pdf文件,只不过不能在myeclipse的左边导航窗口中看不到而已* 左边导航窗口对应C盘目录下的workspace目录下程序* 用rootpath指定的目录是D盘Tomcat目录*/ServletContext sc = request.getSession().getServletContext();String rootpath = sc.getRealPath(""); //值为D:\apache-tomcat-6.0.26\webapps\webmonitor//把路径中的反斜杠转成正斜杠rootpath = rootpath.replaceAll("\\\\", "/"); //值为D:/apache-tomcat-6.0.26/webapps/webmonitorboolean flag = false;String outputFile = pdfPathName;//指定目录导出文件OutputStream os = new FileOutputStream(outputFile);ITextRenderer renderer = new ITextRenderer();StringBuffer html = new StringBuffer();//组装成符合W3C标准的html文件,否则不能正确解析html.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");html.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">").append("<head>").append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />").append("<style type=\"text/css\" mce_bogus=\"1\">body {font-family: SimSun;}</style>").append("<style type=\"text/css\">img {width: 500px;}</style>").append("<style type=\"text/css\">table {font-size:13px;}</style>").append("</head>").append("<body>");html.append("<center>");html.append("<h1>统计报表</h1>");for(int i=0;i<list.size();i++) {html.append("<div>" + list.get(i) + "</div>");}html.append("</center>");html.append("</body></html>");try {renderer.setDocumentFromString(html.toString());// 解决图片的相对路径问题,图片路径必须以file开头renderer.getSharedContext().setBaseURL("file:/" + rootpath);renderer.layout();renderer.createPDF(os);os.close();flag = true;} catch (Exception e) {flag = false;e.printStackTrace();}return flag;}

在Java世界,要想生成PDF,方案不少。最近一直在和这个东西打交道,所以简单做一个小结吧。

在此之前,先来勾画一下我心中比较理想的一个解决方案。在企业应用中,碰到的比较多的PDF的需求,可能是针对某个比较典型的具备文档特性的内容,导出成为PDF进行存档。由于我们现在往往使用一些开源框架,诸如ssh来构建我们的应用,所以我们相对熟悉的方案是针对具体的业务逻辑设计实体,使用开源框架来实现我们的业务逻辑。而PDF的导出,最好不要破坏现有的程序框架,甚至能复用我们业务逻辑层的代码。因为如果把PDF作为一种特殊的表现形式的话,实际上它有点类似模板。最佳的情况,是我们能够通过编写某种模板,把PDF的大概样子确定下来,然后把数据和模板做一次整合,得到最后的结果

带着这个目标,开始在网上搜索解决方案。也找到了一些方案,下面简单小结一下:

Jasper Report

看到的市面上采用的最多的方案,是Jasper Report。相关的文档也很多,不过很杂,需要完全掌握,我认为还是有些坡度和时间的。这个时间和坡度我认为主要来自于对iReport这个IDE的反复尝试,对里面的每个属性的摸索。

Jasper Report的设计思路,本身是不违反我上面所说的初衷的。因为我们的努力方向是先生成模板,然后得到数据,最后将两者整合得到结果。但是Jasper Report的问题在于,其生成模板的方式过于复杂,即使有IDE的帮助,我们还是需要对其中的众多规则有所了解才行,否则就会给调试带来极大的麻烦。

所以,我认为Jasper Report是一个半调子方案,这种强依赖于IDE进行可视化编辑的方式令我很不爽。同时,由此带来的诸多的限制,相信也让很多使用者颇为头疼。在经历了一番痛苦的挣扎后,决定放弃使用这种方案。

iText

其实Jasper Report是基于iText的。于是有的人会说,那么直接使用iText不是一种倒退么?的确,直接使用iText似乎就需要直接使用原生的API进行编程了。不过幸好iText其实提供了一些方便的API,通过使用这些API,我们可以直接将HTML代码转化成iText可识别的Document对象,从而导出PDF文档。

java代码:

[java] view plaincopyprint?
  1. import java.io.FileOutputStream;
  2. import java.io.FileReader;
  3. import java.util.ArrayList;
  4. import com.lowagie.text.Document;
  5. import com.lowagie.text.Element;
  6. import com.lowagie.text.html.simpleparser.HTMLWorker;
  7. import com.lowagie.text.html.simpleparser.StyleSheet;
  8. import com.lowagie.text.pdf.PdfWriter;
  9. public class MainClass {
  10. public static void main(String[] args) throws Exception {
  11. Document document = new Document();
  12. StyleSheet st = new StyleSheet();
  13. st.loadTagStyle("body""leading""16,0");
  14. PdfWriter.getInstance(document, new FileOutputStream("html2.pdf"));
  15. document.open();
  16. ArrayList p = HTMLWorker.parseToList(new FileReader("example.html"), st);
  17. for (int k = 0; k < p.size(); ++k)
  18. document.add((Element) p.get(k));
  19. document.close();
  20. }
  21. }
import java.io.FileOutputStream;
import java.io.FileReader;
import java.util.ArrayList;   import com.lowagie.text.Document;
import com.lowagie.text.Element;
import com.lowagie.text.html.simpleparser.HTMLWorker;
import com.lowagie.text.html.simpleparser.StyleSheet;
import com.lowagie.text.pdf.PdfWriter;   public class MainClass {   public static void main(String[] args) throws Exception {   Document document = new Document();   StyleSheet st = new StyleSheet();   st.loadTagStyle("body", "leading", "16,0");   PdfWriter.getInstance(document, new FileOutputStream("html2.pdf"));   document.open();   ArrayList p = HTMLWorker.parseToList(new FileReader("example.html"), st);   for (int k = 0; k < p.size(); ++k)   document.add((Element) p.get(k));   document.close();   }
}

这是从网上找到的一个例子。从代码中,我们可以看到,iText本身提供了一个简单的HTML的解析器,它可以把HTML转化成我们需要的PDF的document。

有了这个东西,基本上我的目标就能达成一大半了。接下来我的任务就是根据实际情况去编写HTML代码,然后扔进这个方法,就OK了。而真正的HTML代码,我们则可以在这里使用真正的模板技术,Freemarker或者Velocity去生成我们所需要的内容。当然,这已经是我们熟门熟路的东西了。

正当我觉得这个方案基本能符合我的要求的时候,我也同样找到了它的很多弱项:

1. 无法识别很多HTML的tag和attribute(应该是iText的HTMLParser不够强大)
2. 无法识别CSS

如果说第一点我还可以勉强接受的话,那么第二点我就完全不能接受了。无法识别简单的CSS,就意味着HTML失去了最基本的活力,也无法根据实际要求调整样式。

所以这种方案也必然无法成为我的方案。

flying sauser

在这种情况下,我几乎已经燃起了自己编写一个支持CSS解析的HTML Parser的想法。幸好,在一个非常偶然的情况下,我在google中搜到了这样一个开源项目,它能够满足我的一切需求。这就是flying sauser,项目主页是:https://xhtmlrenderer.dev.java.net/

项目的首页非常吸引人:An XML/XHTML/CSS 2.1 Renderer。这不正是我要的东西么?

仔细再看里面的文档:

引用
Flying Saucer is an XML/CSS renderer, which means it takes XML files as input, applies formatting and styling using CSS, and generates a rendered representation of that XML as output. The output may go to the screen (in a GUI), to an image, or to a PDF file. Because we believe most people will be interested in re-using their knowledge of web layout, our main target for content is XHTML 1.0 (strict), an XML document format that standardizes HTML.

完美了。这东西能解析HTML和CSS,而且能输出成image,PDF等格式。哇!我们来看看sample代码(代码丑陋,不过已经能说明问题了):
java代码:

[java] view plaincopyprint?
  1. /*
  2. * ITextRendererTest.java *
  3. * Copyright 2009 Shanghai TuDou.
  4. * All rights reserved.
  5. */
  6. package itext;
  7. import java.io.File;
  8. import java.io.FileOutputStream;
  9. import java.io.OutputStream;
  10. import org.xhtmlrenderer.pdf.ITextFontResolver;
  11. import org.xhtmlrenderer.pdf.ITextRenderer;
  12. import com.lowagie.text.pdf.BaseFont;
  13. /**
  14. * TODO class description *
  15. *
  16. * @author pcwang
  17. *
  18. * @version 1.0, 上午11:03:26  create $Id$
  19. */
  20. public class ITextRendererTest {
  21. public static void main(String[] args) throws Exception {
  22. String inputFile = "conf/template/test.html";
  23. String url = new File(inputFile).toURI().toURL().toString();
  24. String outputFile = "firstdoc.pdf";
  25. OutputStream os = new FileOutputStream(outputFile);
  26. ITextRenderer renderer = new ITextRenderer();
  27. renderer.setDocument(url);
  28. // 解决中文支持问题
  29. ITextFontResolver fontResolver = renderer.getFontResolver();
  30. fontResolver.addFont("C:/Windows/Fonts/arialuni.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
  31. // 解决图片的相对路径问题
  32. renderer.getSharedContext().setBaseURL("file:/D:/Work/Demo2do/Yoda/branch/Yoda%20-%20All/conf/template/");
  33. renderer.layout();
  34. renderer.createPDF(os);
  35. os.close();
  36. }
  37. }
/*
* ITextRendererTest.java *
* Copyright 2009 Shanghai TuDou.
* All rights reserved.
*/  package itext;   import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;   import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;   import com.lowagie.text.pdf.BaseFont;   /**   * TODO class description *   *  * @author pcwang  *  * @version 1.0, 上午11:03:26  create $Id$  */
public class ITextRendererTest {   public static void main(String[] args) throws Exception {   String inputFile = "conf/template/test.html";   String url = new File(inputFile).toURI().toURL().toString();   String outputFile = "firstdoc.pdf";   OutputStream os = new FileOutputStream(outputFile);   ITextRenderer renderer = new ITextRenderer();   renderer.setDocument(url);   // 解决中文支持问题   ITextFontResolver fontResolver = renderer.getFontResolver();   fontResolver.addFont("C:/Windows/Fonts/arialuni.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);   // 解决图片的相对路径问题   renderer.getSharedContext().setBaseURL("file:/D:/Work/Demo2do/Yoda/branch/Yoda%20-%20All/conf/template/");   renderer.layout();   renderer.createPDF(os);   os.close();   }
}

运行,成功!实在太简单了!API帮你完成了一切!

有了这个东西,我们就可以将PDF的生成流程变成这样:

1) 编写Freemarker或者Velocity模板,打造HTML,勾画PDF的样式(请任意使用CSS)

2) 在你的业务逻辑层引入Freemarker的引擎或者Velocity的引擎,并将业务逻辑层中可以获取的数据和模板,使用引擎生成最终的内容

3) 将我上面的sample代码做简单封装后,调用,生成PDF

这样,我想作为一个web程序员来说,上面的3点,都不会成为你的绊脚石。你可以轻松驾驭PDF了。

在Flying Saucer的官方文档中,有一些Q&A,可以解决读者们大部分的问题。包括PDF的字体、PDF的格式、Image如何处理等等。大家可以尝试着去阅读。

还有一篇文章,好像是作者写的,非常不错:http://today.java.net/pub/a/today/2007/06/26/generating-pdfs-with-flying-saucer-and-itext.html

Freemarker+Flying sauser +Itext 整合生成PDF

Freemarker、Flying sauser 、Itext ,这三个框架的作用就不详细介绍了,google一下就知道了。

Itext提供了很多底层的API,让我们可以用java代码画一个pdf出来,但是很不灵活,布局渲染代码都hard code 进java类里面了。

当需求发生改变时,哪怕只需要更改一个属性名,我们都要重新修改那段代码,很不符合开放关闭的原则。想到用模版来做渲染,但自己实现起来比较繁琐,然后google了下,找到了用freemarker做模版,Flying sauser 照着模版做渲染,让Itext做输出生成PDF的方案。

freemarker和itext都比较熟悉了,Flying sauser 第一次听说,看完官方的user guide(http://flyingsaucerproject.github.com/flyingsaucer/r8/guide/users-guide-R8.html)后,自己着手做了个demo实践:

测试数据模型:

java代码:

[java] view plaincopyprint?
  1. package com.jeemiss.pdfsimple.entity;
  2. public class User {
  3. private String name;
  4. private int age;
  5. private int sex;
  6. /**
  7. * Constructor with all fields
  8. *
  9. * @param name
  10. * @param age
  11. * @param sex
  12. */
  13. public User(String name, int age, int sex) {
  14. super();
  15. this.name = name;
  16. this.age = age;
  17. this.sex = sex;
  18. }
  19. ///   getter and setter   ///
  20. public String getName() {
  21. return name;
  22. }
  23. public void setName(String name) {
  24. this.name = name;
  25. }
  26. public int getAge() {
  27. return age;
  28. }
  29. public void setAge(int age) {
  30. this.age = age;
  31. }
  32. public int getSex() {
  33. return sex;
  34. }
  35. public void setSex(int sex) {
  36. this.sex = sex;
  37. }
  38. }
package com.jeemiss.pdfsimple.entity;   public class User {   private String name;   private int age;   private int sex;   /**  * Constructor with all fields  *   * @param name  * @param age  * @param sex  */  public User(String name, int age, int sex) {   super();   this.name = name;   this.age = age;   this.sex = sex;   }   ///   getter and setter   ///   public String getName() {   return name;   }   public void setName(String name) {   this.name = name;   }   public int getAge() {   return age;   }   public void setAge(int age) {   this.age = age;   }   public int getSex() {   return sex;   }   public void setSex(int sex) {   this.sex = sex;   }   }

java代码:

[java] view plaincopyprint?
  1. package com.jeemiss.pdfsimple.freemarker;
  2. import freemarker.template.Configuration;
  3. public class FreemarkerConfiguration {
  4. private static Configuration config = null;
  5. /**
  6. * Static initialization.
  7. *
  8. * Initialize the configuration of Freemarker.
  9. */
  10. static{
  11. config = new Configuration();
  12. config.setClassForTemplateLoading(FreemarkerConfiguration.class"template");
  13. }
  14. public static Configuration getConfiguation(){
  15. return config;
  16. }
  17. }
package com.jeemiss.pdfsimple.freemarker;   import freemarker.template.Configuration;   public class FreemarkerConfiguration {   private static Configuration config = null;   /**  * Static initialization.  *   * Initialize the configuration of Freemarker.  */  static{   config = new Configuration();   config.setClassForTemplateLoading(FreemarkerConfiguration.class, "template");   }   public static Configuration getConfiguation(){   return config;   }   }

html生成器:

java代码:

[java] view plaincopyprint?
  1. package com.jeemiss.pdfsimple.generator;
  2. import java.io.BufferedWriter;
  3. import java.io.StringWriter;
  4. import java.util.Map;
  5. import com.jeemiss.pdfsimple.freemarker.FreemarkerConfiguration;
  6. import freemarker.template.Configuration;
  7. import freemarker.template.Template;
  8. public class HtmlGenerator {
  9. /**
  10. * Generate html string.
  11. *
  12. * @param template   the name of freemarker teamlate.
  13. * @param variables  the data of teamlate.
  14. * @return htmlStr
  15. * @throws Exception
  16. */
  17. public static String generate(String template, Map<String,Object> variables) throws Exception{
  18. Configuration config = FreemarkerConfiguration.getConfiguation();
  19. Template tp = config.getTemplate(template);
  20. StringWriter stringWriter = new StringWriter();
  21. BufferedWriter writer = new BufferedWriter(stringWriter);
  22. tp.setEncoding("UTF-8");
  23. tp.process(variables, writer);
  24. String htmlStr = stringWriter.toString();
  25. writer.flush();
  26. writer.close();
  27. return htmlStr;
  28. }
  29. }
package com.jeemiss.pdfsimple.generator;   import java.io.BufferedWriter;
import java.io.StringWriter;
import java.util.Map;
import com.jeemiss.pdfsimple.freemarker.FreemarkerConfiguration;
import freemarker.template.Configuration;
import freemarker.template.Template;   public class HtmlGenerator {   /**  * Generate html string.  *   * @param template   the name of freemarker teamlate.  * @param variables  the data of teamlate.  * @return htmlStr  * @throws Exception  */  public static String generate(String template, Map<String,Object> variables) throws Exception{   Configuration config = FreemarkerConfiguration.getConfiguation();   Template tp = config.getTemplate(template);   StringWriter stringWriter = new StringWriter();     BufferedWriter writer = new BufferedWriter(stringWriter);     tp.setEncoding("UTF-8");     tp.process(variables, writer);     String htmlStr = stringWriter.toString();   writer.flush();     writer.close();   return htmlStr;   }   }

pdf生成器:

java代码:

[java] view plaincopyprint?
  1. package com.jeemiss.pdfsimple.generator;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.OutputStream;
  4. import javax.xml.parsers.DocumentBuilder;
  5. import javax.xml.parsers.DocumentBuilderFactory;
  6. import org.w3c.dom.Document;
  7. import org.xhtmlrenderer.pdf.ITextRenderer;
  8. public class PdfGenerator {
  9. /**
  10. * Output a pdf to the specified outputstream
  11. *
  12. * @param htmlStr the htmlstr
  13. * @param out the specified outputstream
  14. * @throws Exception
  15. */
  16. public static void generate(String htmlStr, OutputStream out)
  17. throws Exception {
  18. DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
  19. Document doc = builder.parse(new ByteArrayInputStream(htmlStr.getBytes()));
  20. ITextRenderer renderer = new ITextRenderer();
  21. renderer.setDocument(doc, null);
  22. renderer.layout();
  23. renderer.createPDF(out);
  24. out.close();
  25. }
  26. }
package com.jeemiss.pdfsimple.generator;   import java.io.ByteArrayInputStream;
import java.io.OutputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.xhtmlrenderer.pdf.ITextRenderer;   public class PdfGenerator {   /**  * Output a pdf to the specified outputstream  *   * @param htmlStr the htmlstr  * @param out the specified outputstream  * @throws Exception  */  public static void generate(String htmlStr, OutputStream out)   throws Exception {   DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();   Document doc = builder.parse(new ByteArrayInputStream(htmlStr.getBytes()));   ITextRenderer renderer = new ITextRenderer();   renderer.setDocument(doc, null);   renderer.layout();   renderer.createPDF(out);   out.close();   }   }

用来做测试的ftl模版,用到部分css3.0的属性来控制pdf强制分页和输出分页信息

html代码:

[html] view plaincopyprint?
  1. <html>
  2. <head>
  3. <title>${title}</title>
  4. <style>
  5. table {
  6. width:100%;border:green dotted ;border-width:2 0 0 2
  7. }
  8. td {
  9. border:green dotted;border-width:0 2 2 0
  10. }
  11. @page {
  12. size: 8.5in 11in;
  13. @bottom-center {
  14. content: "page " counter(page) " of  " counter(pages);
  15. }
  16. }
  17. </style>
  18. </head>
  19. <body>
  20. <h1>Just a blank page.</h1>
  21. <div style="page-break-before:always;">
  22. <div align="center">
  23. <h1>${title}</h1>
  24. </div>
  25. <table>
  26. <tr>
  27. <td><b>Name</b></td>
  28. <td><b>Age</b></td>
  29. <td><b>Sex</b></td>
  30. </tr>
  31. <#list userList as user>
  32. <tr>
  33. <td>${user.name}</td>
  34. <td>${user.age}</td>
  35. <td>
  36. <#if user.sex = 1>
  37. male
  38. <#else>
  39. female
  40. </#if>
  41. </td>
  42. </tr>
  43. </#list>
  44. </table>
  45. </div>
  46. </body>
  47. </html>
<html>
<head>  <title>${title}</title>  <style>  table {   width:100%;border:green dotted ;border-width:2 0 0 2   }   td {   border:green dotted;border-width:0 2 2 0   }   @page {     size: 8.5in 11in;    @bottom-center {   content: "page " counter(page) " of  " counter(pages);   }   }   </style>
</head>
<body>  <h1>Just a blank page.</h1>  <div style="page-break-before:always;">  <div align="center">  <h1>${title}</h1>  </div>  <table>  <tr>  <td><b>Name</b></td>  <td><b>Age</b></td>  <td><b>Sex</b></td>  </tr>  <#list userList as user>  <tr>  <td>${user.name}</td>  <td>${user.age}</td>  <td>  <#if user.sex = 1>  male   <#else>  female   </#if>  </td>  </tr>  </#list>  </table>  </div>
</body>
</html>

最后写个测试用例看看:

java代码:

[java] view plaincopyprint?
  1. package com.jeemiss.pdfsimple.test;
  2. import java.io.FileOutputStream;
  3. import java.io.OutputStream;
  4. import java.util.ArrayList;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.Map;
  8. import org.junit.Test;
  9. import com.jeemiss.pdfsimple.entity.User;
  10. import com.jeemiss.pdfsimple.generator.HtmlGenerator;
  11. import com.jeemiss.pdfsimple.generator.PdfGenerator;
  12. public class TestCase
  13. {
  14. @Test
  15. public void generatePDF() {
  16. try{
  17. String outputFile = "C:\\sample.pdf";
  18. Map<String,Object> variables = new HashMap<String,Object>();
  19. List<User> userList = new ArrayList<User>();
  20. User tom = new User("Tom",19,1);
  21. User amy = new User("Amy",28,0);
  22. User leo = new User("Leo",23,1);
  23. userList.add(tom);
  24. userList.add(amy);
  25. userList.add(leo);
  26. variables.put("title""User List");
  27. variables.put("userList", userList);
  28. String htmlStr = HtmlGenerator.generate("sample.ftl", variables);
  29. OutputStream out = new FileOutputStream(outputFile);
  30. PdfGenerator.generate(htmlStr, out);
  31. }catch(Exception ex){
  32. ex.printStackTrace();
  33. }
  34. }
  35. }
package com.jeemiss.pdfsimple.test;   import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import com.jeemiss.pdfsimple.entity.User;
import com.jeemiss.pdfsimple.generator.HtmlGenerator;
import com.jeemiss.pdfsimple.generator.PdfGenerator;   public class TestCase
{   @Test  public void generatePDF() {   try{   String outputFile = "C:\\sample.pdf";   Map<String,Object> variables = new HashMap<String,Object>();   List<User> userList = new ArrayList<User>();   User tom = new User("Tom",19,1);   User amy = new User("Amy",28,0);   User leo = new User("Leo",23,1);   userList.add(tom);   userList.add(amy);   userList.add(leo);   variables.put("title", "User List");   variables.put("userList", userList);   String htmlStr = HtmlGenerator.generate("sample.ftl", variables);   OutputStream out = new FileOutputStream(outputFile);   PdfGenerator.generate(htmlStr, out);   }catch(Exception ex){   ex.printStackTrace();   }   }
}

到C盘下打开sample.pdf ,看看输出的结果:

flying saucer 使用中的一些问题 (java导出pdf)

flying saucer(源代码托管在githubhttps://github.com/flyingsaucerproject/flyingsaucer)是java导出pdf的一种解决方案,最早是从downpour老大的文章里看到它:http://www.iteye.com/topic/509417 ,感觉比之前的iText好用许多,它可以解析css,即我将页面先设置好,然后传递给它,它既可以给我生成一个pdf出来,跟页面一样,当时感觉很酷,于是就研究了一下,现在项目中也用到了,效果还不错。
  
     优点很明显,之前也提到了,可以解析css,这样很方便,大大的减少了工作量。pdf加水印也变得很简单——只需为body设置一个background-image即可。
     说说使用中需要注意的一些问题吧:
[list=1]

  • 中文换行问题
       老外做的东西,没有考虑到中文问题。默认提供的包里,中文不会换行,有人修改了源代码,解决了这个问题,重新编译好的包在附件里,可以下载。需要注意的是,在官网提供的jar包里,有两个包,一个是core-renderer.jar,另一个是core-renderer-minimal.jar。引用时,只需引用前者就行。有人曾经说用这个重新编译后的包替换了原来的包之后,不起作用,原因就在此。

    中文换行包下载地址:http://community.csdn.net/detail/shanliangliuxing

    另外,想要中文换行,如果是table,那么table 的style必须加上这句话

    html代码:

    [html] view plaincopyprint?
    1. style="table-layout:fixed; word-break:break-strict;"
    style="table-layout:fixed; word-break:break-strict;"  

     

    css路径问题
       在一个java project里,使用相对css路径是可以的,效果也都不错。但在java web project里,使用css相对路径是不可以的(最起码这里困扰了我很久,差点就放弃flying saucer了)。例如,我有一个模板叫addOne.jsp,里面引用到了某个css,就应该这样写(windows)

    js代码:

    [javascript] view plaincopyprint?
    1. <link href="file:///D|/project/WebContent/commons/css/module-pdf.css" rel="stylesheet" type="text/css" />
    <link href="file:///D|/project/WebContent/commons/css/module-pdf.css" rel="stylesheet" type="text/css" /> 

    只有这样写了之后,它才能找到这个css,很诡异。每次换了机器之后都要改路径,很麻烦。

  • 中文字体问题
       downpour老大在它那篇文章里提到了怎样处理中文字体的,他可能高估了许多人的水平。其实说起来,很简单,就两点:一是在java代码里引用字体,二是在页面上引用字体。
      引用字体:
    java代码:

    [java] view plaincopyprint?
    1. // 解决中文支持问题
    2. ITextFontResolver fontResolver = renderer.getFontResolver();
    3. fontResolver.addFont("C:/Windows/Fonts/arialuni.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
    // 解决中文支持问题     ITextFontResolver fontResolver = renderer.getFontResolver();     fontResolver.addFont("C:/Windows/Fonts/arialuni.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);  

    这里引用了arialuni.ttf字体,它位于C盘windows/fonts文件夹下,将它引用后,还需要在页面上使用这个字体

    html代码:

    [html] view plaincopyprint?
    1. <body style="font-family:'Arial Unicode MS'">
    <body style="font-family:'Arial Unicode MS'">
    

    这里的Arial Unicode MS 即刚才 的arialuni.ttf字体的名字,换了其它字体后要注意修改这里的名称。这样才可以在pdf中显示中文。
      许多人有这样一个问题——按照以上两个步骤做了之后,页面中还是没有中文,这时,请检查你引用的css文件,其中一定设置了其它字体,只需将它去掉即可

    缺点:
        我在使用中发现,flying saucer不支持富文本,如果用到了KindEditor此类富文本编辑器,
    还要将其中的内容转化成pdf,那对flying saucer来说就是个灾难。会报一堆错误,目前我还没有找到解决方案。还好这次项目中不是必须使用富文本编辑器,对于有此类需求的同学来说,请慎重选择flying saucer。另外,flying saucer严格遵守html规则,一个小小的错误,都会导致它报错。诸如
    html代码:

    [html] view plaincopyprint?
    1. <td colspan="2""2">
    <td colspan="2""2">
    

    此类的html代码在jsp中是不会有问题的,可是flying saucer却会报错,曾经这个问题导致我花了一小时时间来寻找问题所在。不过很难说这到底是缺点还是优点

    最后贴一个较完整的例子:
        我使用spring mvc,在controller里
    java代码:

    [java] view plaincopyprint?
    1. @RequestMapping("/pdf/{projectId}")
    2. public ModelAndView generatePdf(HttpServletRequest request,
    3. HttpServletResponse response, @PathVariable
    4. String projectId) {
    5. Project project = this.projectService.getProjectById(projectId);
    6. ModelAndView mav = new ModelAndView();
    7. if (project == null) {
    8. mav.setViewName("forward:/error/page-not-found");
    9. return mav;
    10. }
    11. //中文需转义
    12. String pdfName = "pdfName";
    13. response.setHeader("Content-disposition""attachment;filename="+pdfName;
    14. response.setContentType("application/pdf");
    15. OutputStream os = response.getOutputStream();
    16. ITextRenderer renderer = new ITextRenderer();
    17. //指定模板地址
    18. renderer.setDocument("http://localhost/project/preview/"+projectId);
    19. ITextFontResolver fontResolver = renderer.getFontResolver();
    20. if (StringUtils.isOSWindow())
    21. fontResolver.addFont("C:/Windows/Fonts/ARIALUNI.TTF",
    22. BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
    23. else
    24. fontResolver.addFont("/usr/share/fonts/TTF/ARIALUNI.TTF",
    25. BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
    26. renderer.layout();
    27. renderer.createPDF(os);
    28. os.close();
    29. return null;
    30. }
    31. @RequestMapping("/preview/{projectId}")
    32. public ModelAndView pdf(@PathVariable
    33. String projectId) {
    34. Project project = this.projectService.getProjectById(projectId);
    35. ModelAndView mav = new ModelAndView();
    36. if (project == null) {
    37. mav.setViewName("forward:/error/page-not-found");
    38. return mav;
    39. }
    40. mav.setViewName("pdf");
    41. mav.addObject("project",project);
    42. return mav;
    43. }
    @RequestMapping("/pdf/{projectId}")   public ModelAndView generatePdf(HttpServletRequest request,   HttpServletResponse response, @PathVariable  String projectId) {   Project project = this.projectService.getProjectById(projectId);   ModelAndView mav = new ModelAndView();   if (project == null) {   mav.setViewName("forward:/error/page-not-found");   return mav;   }   //中文需转义   String pdfName = "pdfName";   response.setHeader("Content-disposition", "attachment;filename="+pdfName;   response.setContentType("application/pdf");   OutputStream os = response.getOutputStream();   ITextRenderer renderer = new ITextRenderer();
    //指定模板地址
    renderer.setDocument("http://localhost/project/preview/"+projectId);   ITextFontResolver fontResolver = renderer.getFontResolver();   if (StringUtils.isOSWindow())   fontResolver.addFont("C:/Windows/Fonts/ARIALUNI.TTF",   BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);   else  fontResolver.addFont("/usr/share/fonts/TTF/ARIALUNI.TTF",   BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);   renderer.layout();   renderer.createPDF(os);   os.close();   return null;   }   @RequestMapping("/preview/{projectId}")   public ModelAndView pdf(@PathVariable  String projectId) {   Project project = this.projectService.getProjectById(projectId);   ModelAndView mav = new ModelAndView();   if (project == null) {   mav.setViewName("forward:/error/page-not-found");   return mav;   }   mav.setViewName("pdf");   mav.addObject("project",project);   return mav;   }
    

    jsp页面如下

    html代码:

    [html] view plaincopyprint?
    1. <html xmlns="http://www.w3.org/1999/xhtml">
    2. <head>
    3. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    4. <title>title</title>
    5. <link href="file:///D|/project/WebContent/commons/css/print-pdf.css" rel="stylesheet" type="text/css"  />
    6. </head>
    7. <body style="font-family:'Arial Unicode MS'">
    8. <table border="1" cellspacing="0" cellpadding="0" class="table" style="table-layout:fixed; word-break:break-strict;">
    9. <tr>
    10. <td rowspan="9" width="4%" class="tc">项目单位基本信息</td>
    11. <td colspan="2" style="width:160px">(1)项目单位名称 </td>
    12. <td colspan="2"><%=StringUtils.getValueString(user.getDeptName()) %></td>
    13. </tr>
    14. </table>
    15. </body>
     <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>title</title>
    <link href="file:///D|/project/WebContent/commons/css/print-pdf.css" rel="stylesheet" type="text/css"  />  </head>
    <body style="font-family:'Arial Unicode MS'">  <table border="1" cellspacing="0" cellpadding="0" class="table" style="table-layout:fixed; word-break:break-strict;">  <tr>  <td rowspan="9" width="4%" class="tc">项目单位基本信息</td>  <td colspan="2" style="width:160px">(1)项目单位名称 </td>  <td colspan="2"><%=StringUtils.getValueString(user.getDeptName()) %></td>  </tr>  </table>
    </body>
    

    使用flyingsaucer将网页转换为pdf之中文问题彻底解决

    前几天遇到个导出pdf的需求,在网络上查找了一下java导出pdf的方案.多数人推荐使用iText,研究了一下,感觉直接写pdf的方法太笨,可维护性差,一旦pdf格式要变化改起来很费劲.还有一个方案,可以先预先定义一个pdf作为模板文件,然后用业务数据进行填空.是个不错的方案,只可惜不适合我的需求.需求中有些行是动态加行的,这个方案无法实现.后来发现有可以将网页直接转成pdf的开源包flyingsaucer(中文名:灰碟),逐将注意力转移到这上面,发现是个不错的选择.只要写网页就可以了,而且pdf格式变化维护起来也方便,代码也会比较干净.只是它对中文支持的不好,但这不是无法解决的.下面就来说说这个flyingsaucer.

    Flyingsaucer使用iText2.0.8作为其pdf输出的基础工具,另外增加了解析html/xml并形成pdf式排版的功能.最重要的它还支持css样式表.组合这些能力后,它就可以将网页变成pdf了.但是,它也有他的问题.大家知道iText的版本现在已经升级为5.0以上了,而flyingsaucer却依然沿用2.0.8的版本.为什么呢? 因为这个灰碟貌似自2007年就已经停止维护了,最终版本flyingsaucer-R8.也就是说这是个几年前的工具包,至于新的替代此功能的包又在哪里?我没有找到,倒是有个功能相似但是收费的,不知道是不是它的成长版.这是题外话我们不研究.只看这个flyingsaucer-R8这个版本能否满足我们的基本要求吧.
    在使用过程中发现flyingsaucer-R8对导出pdf的网页有一些要求.
    1. 所有的标签必须都闭合.
    2. 网页开头引入的DTD必须与网页体中使用的标签一致.
    3. 部分不太常用的html标签貌似不认.比如<u>.
    因为flyingsaucer解析html文档是遵照xml标准来的,所以这个网页写起来不能像我们平时的网页那么随意.xml该有的规则都要遵守.这个要求并不高我们可以做到,而且试了下导出的pdf文档没啥问题,因此我们还是可以使用它来满足我们的需求.(至于,这个工具包怎么用,这里有不多说了,google一下一大片.这里只写目前google不到的东西.)
    既然要用它不可回避的就会遇到其对中文支持不好的问题.
    问题1:来源自渲染器输出时没有使用支持中文的字体.虽然我们看到iText有亚洲语言包iTextAsian.jar,但是仅仅引入此包并不能使我们的中文字符输出.以至于网上有个哥们写到:
    打开ITextOutputDevice这个类找到:

    java代码:

    [java] view plaincopyprint?
    1. cb.setFontAndSize(_font.getFontDescription().getFont(), _font.getSize2D() / _dotsPerPoint);
    cb.setFontAndSize(_font.getFontDescription().getFont(), _font.getSize2D() / _dotsPerPoint);     

    改成:

    java代码:

    [java] view plaincopyprint?
    1. 01.try {
    2. 02.            cb.setFontAndSize(BaseFont.createFont("STSong-Light""UniGB-UCS2-H", BaseFont.NOT_EMBEDDED), _font.getSize2D()/_dotsPerPoint);
    3. 03.        } catch (Exception e) {
    4. 04.        }
    01.try {
    02.            cb.setFontAndSize(BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED), _font.getSize2D()/_dotsPerPoint);
    03.        } catch (Exception e) {
    04.        }     

    Ok,改了以后我们终于可以看到pdf里有中文了.但是别高兴的太早哦,问题并没有完全解决.如果一段标签中有且只有中文字符的时候,导出pdf后内容便会消失.比如<div>中文</div>,这样的代码将什么也输出不了,而<div>中文a</div>则会将标签内容全部输出.通过测试我们发现,纯中文是无法输出的,但是加上一点点英文、数字或符号就可以输出了.有同学可能要说我们把纯中文后面加上空格不就行了?我只能说很不幸,加空格是不管用的.如果你的页面上纯中文的地方可以随便让你加字母/数字/符号,那可以不必往下看了.但是我觉得大多数的人恐怕不会这么干的,即使我们想客户也不让啊.那就要解决这个问题.

    开源的东西有个好处,可以看源代码.从源码中我发现是字体的问题,于是乎,google一下,找到以下方案:
    在导出的代码里加入这两句引入字体

    java代码:

    [java] view plaincopyprint?
    1. 01.ITextFontResolver fontResolver = renderer.getFontResolver();
    2. 02.fontResolver.addFont("C:/WINDOWS/Fonts/ARIALUNI.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
    01.ITextFontResolver fontResolver = renderer.getFontResolver();
    02.fontResolver.addFont("C:/WINDOWS/Fonts/ARIALUNI.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);     

    于是我照做了.杯具的是情况没有任何好转.即使有好转这个绝对路径的字体引入方式也很让人不舒服吧.所以这个不是我们想要的解决方案.

    Google不到只能继续从代码里找办法了.通过跟深入的研究代码,发现问题根源所在.输出PDF时在计算字符宽度的时候,对中文字符计算出的宽度居然是0.这也就能解释为什么纯中文字符串输出后会看不到了,因为字符总体的宽度被计算为0也就失去了被输出的机会,而加上一点非中文字符的则至少会有一点宽度,即获得了输出的机会,即使实际宽度比计算的宽度要宽也是可以输出的.
    于是修改BaseFont中的getWidth(String text)方法

    java代码:

    [java] view plaincopyprint?
    1. 01.if (char1 < 128 || (char1 >= 160 && char1 <= 255))
    2. 02.    total += widths[char1];
    3. 03.else
    4. 04.    total += widths[PdfEncodings.winansi.get(char1)];
    01.if (char1 < 128 || (char1 >= 160 && char1 <= 255))
    02.    total += widths[char1];
    03.else
    04.    total += widths[PdfEncodings.winansi.get(char1)];     

    这几行改为:

    java代码:

    [java] view plaincopyprint?
    1. ·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······15001.if (char1 < 128 || (char1 >= 160 && char1 <= 255))
    2. 02.    total += widths[char1];
    3. 03.else if ((char1 >= 19968) && (char1 <= 40869)) // 如果是中文字符加宽度500
    4. 04.    total += 500;
    5. 05.else
    6. 06.    total += widths[PdfEncodings.winansi.get(char1)];
    ·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······15001.if (char1 < 128 || (char1 >= 160 && char1 <= 255))
    02.    total += widths[char1];
    03.else if ((char1 >= 19968) && (char1 <= 40869)) // 如果是中文字符加宽度500
    04.    total += 500;
    05.else
    06.    total += widths[PdfEncodings.winansi.get(char1)];     

    再次测试,通过.至此,使用flyingsaucer网页导出成pdf中文问题总算解决了.可是总觉得这个解决的方法有点不太正宗,因为修改了父类嘛.但又没有找到其他正宗的解决方案,只能先这样解决一下了.发出此文,只当抛砖引玉,如果有哪位高人有更好的解决方案请不吝赐教啊.

    附件提供修改了的flyingsaucer-R8的两个jar包: core-renderer.jar和iText2.0.8.jar另有一个iText亚洲语言包.

    下载修改源码后的jar包地址:http://download.csdn.net/detail/shanliangliuxing/3640286

    利用 iText 实现 PDF 报表下载 (没有用flying saucer)

    很久没更新 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 的关系,经过反复调试,有点效果了。把代码贴出来,做个标记吧。以免以后又抓狂。

    java代码:

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

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

    一定要学会看官方文档,因为有些技术网上的资料太少,简单的还能满足需求,复杂一点的就得需要自己慢慢去研究官方文档了

    官方文档一般都是英文的,所以,英文水平还要过的去才行,说不作要求,最起码要能读懂。

    flying saucer的官方文档:

    http://code.google.com/p/flying-saucer/

    官方文档中关于生成pdf的demo:

    http://today.java.net/pub/a/today/2007/06/26/generating-pdfs-with-flying-saucer-and-itext.html

    相关例子源文件下载:

    https://svn.atlassian.com/svn/public/atlassian/vendor/xhtmlrenderer-8.0/tags/8.3-atlassian/demos/samples/src/

flying saucer技术生成pdf文档相关推荐

  1. iText和flying saucer结合生成pdf的技术

    原博文地址 http://blog.csdn.net/shanliangliuxing/article/details/6833471 下面是我自己利用flying saucer技术生成pdf文档的实 ...

  2. 【报表技术】IReport图形化报表开发工具生成PDF文档

    [报表技术]IReport图形化报表开发工具生成PDF文档 新建一个文件. 由于IReport的默认设置是不支持中文的,IReport没有内置中文的包. 了解报表模板结构. ①title:报表标题. ...

  3. 用C#实现生成PDF文档和将WORD转换为PDF (转自海东的技术资料)

    转自:http://www.cnblogs.com/ghd258/articles/258060.html 前言:由于一个客户的项目中需要将WORD文档转换成PDF格式,故写了本篇实站教程  需求分析 ...

  4. java-使用 flying-saucer 通过 xhtml 生成 pdf 文档支持 css 和 图片

    java-使用 flying-saucer 通过 xhtml 生成 pdf 文档支持 css 和 图片 重要说明: 1.使用 xhtml 生成 pdf ,对于 xhtml 的语法要求非常严格: 2.中 ...

  5. 【教程】如何使用Java生成PDF文档?

    在如今数字化时代,越来越多的人使用PDF文档进行信息传递和共享.而使用Java生成PDF文档也成为了一个非常重要的技能,因为Java作为一种通用的编程语言,可以在不同的操作系统和平台上运行.下面,我们 ...

  6. c语言调用pdf文档,使用PDFLib生成PDF文档方法介绍(C语言版)

    本文简单介绍了PDFLib生成PDF文档(C语言版)的基本使用方法. 1.基本环境 ① 打开.关闭.文档信息设定: 新建PDFLib对象,PDF_new() 设定错误处理的方式,PDF_set_par ...

  7. php操作pdf文档输出,PHP生成PDF文档实用技巧

    PHP生成PDF文档实用技巧 实际工作中,我们要使用PHP动态的创建PDF文档,目前有许多开源的PHP创建PDF的类库,今天我给大家来介绍一款优秀的PDF库,它就是TCPDF,TCPDF是一个用于快速 ...

  8. php header 生成pdf,PHP如何生成PDF文档

    PHP如何生成PDF文档? 使用PHPTCPDF 实际工作中,我们要使用PHP动态的创建PDF文档,目前有许多开源的PHP创建PDF的类库,今天我给大家来介绍一款优秀的PDF库,它就是TCPDF,TC ...

  9. 手把手教你使用 Java 在线生成 pdf 文档

    一.介绍 在实际的业务开发的时候,研发人员往往会碰到很多这样的一些场景,需要提供相关的电子凭证信息给用户,例如网银/支付宝/微信购物支付的电子发票.订单的库存打印单.各种电子签署合同等等,以方便用户查 ...

  10. 用C#实现生成PDF文档和将WORD转换为PDF

    知识点:线程调用,异步线程,异步线程回调函数,C#的类,WSH宿主脚本开发 前言:由于一个客户的项目中需要将WORD文档转换成PDF格式,故写了本篇实站教程 需求分析:客户的项目以B/S结构为主,提供 ...

最新文章

  1. 简述事件接口与事件适配器的联系与区别_设计模式——适配器模式
  2. java随机产生字母排序_Java生成含字母和数字的6位随机字符串
  3. R语言实战应用精讲50篇(一)-万字长文干货R语言ggplot2包图形绘制
  4. HTML5新特性之WebRTC
  5. java配置文件中的plugin,启用ContextReplacementPlugin以忽略webpack中的配置和测试设置文件...
  6. InitializingBean接口使用方法
  7. Linux有关Shell中if用法笔记
  8. Java 遍历系统根目录
  9. 【技巧】搜狗输入法特殊技巧
  10. Hbase常用操作记录
  11. Java关键字与保留字说明及使用
  12. Discuz X1.5 X2.5 X3 UC_KEY Getshell Write PHPCODE into config/config_ucenter.php Via /api/uc.php Vul
  13. 初探 performance - 监控网页与程序性能
  14. C++ #include头文件随想
  15. 2010年VMware中国获奖总结(1/2)
  16. Java设计person类,有姓名,年龄,性别。要求:该类至多只能创建一男,一女两个对象。
  17. 希捷、西部数据硬盘保修查询
  18. python 列表去重
  19. 京东淘宝,拼多多三大电商平台竞品分析
  20. 今日头条2018 坐标

热门文章

  1. 使用 CP2102通过串口下载程序到STM32F103中 (MCUISP)
  2. powerdesigner 导出mysql 库,自动生成ER图
  3. c语言bubblesort函数,C++实现冒泡排序(BubbleSort)
  4. linux音频alsa-uda134x驱动分析之一(over-view)
  5. OpenCV 网络视频传输 C++ 和 python实现
  6. 电子设计(2)三极管稳压电路仿真分析
  7. 射频电路设计——射频器件基础
  8. 十大免费SSL证书:网站免费添加HTTPS加密
  9. 大学数学学习参考书点评
  10. vue添加响应response拦截器,响应登陆超时处理