补充 Flying Saucer 不支持中文,换行,粗体,CheckBox多选框如何解决
来源:互联网 发布:win7网络小图标不见了 编辑:程序博客网 时间:2024/06/06 08:46
最近要生成打印版的保单信息,内容比较多,也比较复杂,iText直接生成的话,想必花很多时间,而且可能也很难维护,偶然看到了HTML 在 Fly Saucer的帮助下能转换成PDF,解析CSS还不错,顿时随便拿个网页转了一下,比想象中好,于是决定用在项目中了:
然而真正用起来的话,会遇到很多问题,在此我一一总觉出来,然后给予解我的决办法,也希望大家有什么更好的办法的话,互相分享;
1. Flying Saucer 只能解析HTML, 但我们的文件都是JSP , 那怎么解决呢?
我的想法是,截取请求,当客户发送一次请求后,我们后台务必会解析JSP,然后生成HTML返回给浏览器,这时我们就可以获取response所携带的字节流。但是,由于我们写到response中的流是无法再次获取的(在服务端);就像我们用OutputStream 把内容写入文件一样,我们只持有当前OutputStream对象的话,是无法再次获取已经写入的内容一样,这就是输出流的不可逆性;所以,我们得借助 HttpServletResponseWrapper 接口,我们编写一个自己的类实现这个接口,然后我们的类就可以充当 HttpServletResponse 来使用(相当于我们的类冒充HttpServletResponse一样),然后我们在Servlet中截取请求,再把请求跳转到要打印成PDF的jsp页面,当请求回来时,我们就可以获取请求带回来的数据,然后转换成String,生成PDF,同时写入真正的 HttpServletResponse 对象中返回给客户端。
import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.io.UnsupportedEncodingException;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpServletResponseWrapper;public class PDFResponseWrapper extends HttpServletResponseWrapper { private ByteArrayOutputStream buffer = null; private ServletOutputStream out = null; private PrintWriter writer = null; public PDFResponseWrapper(HttpServletResponse resp) throws IOException { super(resp); buffer = new ByteArrayOutputStream();// 真正存储数据的流 out = new WapperedOutputStream(buffer); String encoding = resp.getCharacterEncoding(); writer = new PrintWriter(new OutputStreamWriter(buffer, encoding)); } /**重载父类获取outputstream的方法*/ @Override public ServletOutputStream getOutputStream() throws IOException { return out; } /**重载父类获取writer的方法*/ @Override public PrintWriter getWriter() throws UnsupportedEncodingException { return writer; } /**重载父类获取flushBuffer的方法*/ @Override public void flushBuffer() throws IOException { if (out != null) { out.flush(); } if (writer != null) { writer.flush(); } } @Override public void reset() { buffer.reset(); } /**将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据*/ public byte[] getResponseData() throws IOException { flushBuffer(); return buffer.toByteArray(); } /**内部类,对ServletOutputStream进行包装*/ private class WapperedOutputStream extends ServletOutputStream { private ByteArrayOutputStream bos = null; public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException { bos = stream; } @Override public void write(int b) throws IOException { bos.write(b); } @Override public void write(byte[] b) throws IOException { bos.write(b, 0, b.length); } } }
import java.io.IOException;import java.io.OutputStream;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.log4j.Logger;import com.itextpdf.text.DocumentException;import com.sinosoft.flex.service.insure.InsuredMaterialDownload;import com.sinosoft.flex.service.insure.PDFResponseWrapper;import com.sinosoft.flex.service.insure.PlanApproveBL;import com.sinosoft.flex.service.insure.PolicyPDFPrintGenerationBL;public class SubServletForPDF extends SuperServlet{ /** * */ private static final long serialVersionUID = 1L; private static Logger log = Logger.getLogger(PlanApproveBL.class); @Override public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String operator1 = "operator1"; String policyPDF = "policyPDF"; String operator = request.getParameter("operator"); /////////// 投保单资料下载1 分红,万能险种资料下载 if(operator1.equals(operator)){ /////// Other Logic }else if(policyPDF.equals(operator)){ PolicyPDFPrintGenerationBL pdfBL = new PolicyPDFPrintGenerationBL(); String printPDF = request.getParameter("print"); if(printPDF != null && printPDF.equals("Y")){ ////// Other Logic }else{ String poNo = request.getParameter("poNo"); if(poNo == null || poNo.length() <1){ log.info("获取保单信息失败,请传入保单号"); Error err = new Error("查询数据失败"); throw new RuntimeErrorException(err); } request.setAttribute("poNo", poNo); String servletPath = pdfBL.getServletPath(poNo); // 获取转发的请求路径,然后把请求转向生成PDF的JSP页面 response.setCharacterEncoding("UTF-8"); PDFResponseWrapper responWrapper = new PDFResponseWrapper(response); request.getRequestDispatcher(servletPath).forward(request, responWrapper); byte[] data = responWrapper.getResponseData(); // 获取out.write()进来的 html 字节流 String str = new String(data,"UTF-8"); pdfBL.convertHtmlToPdf(str); // html格式的 字符串转换成PDF,代码在下面 OutputStream out = response.getOutputStream(); out.write(data); out.close(); } } }}
2. Flying Saucer 不支持中文,换行,粗体,CheckBox多选框,我是如何解决的?
(1)、中文的话,网上有很多说明了,在此我大概说一下,然后给出代码:
1)、如同网上所说的,直接在代码里面添加中文字体支持,然后CSS中也要设置相关字体(注意大小写一致)
body{MARGIN:AUTO;width:690px;font-size:12px; font-family:SimHei; color:#222;} /// 黑体 body{MARGIN:AUTO;width:690px;font-size:12px; font-family:SimSun; color:#222;} /// 宋体
import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;import org.apache.log4j.Logger;import org.xhtmlrenderer.pdf.ITextFontResolver;import org.xhtmlrenderer.pdf.ITextRenderer;import com.lowagie.text.DocumentException;import com.lowagie.text.pdf.BaseFont;import com.sinosoft.flex.pubfun.PubFun;import com.sinosoft.utility.SSRS;public class PolicyPDFPrintGenerationBL { private static Logger log = Logger.getLogger(PlanApproveBL.class); public boolean convertHtmlToPdf(String html){ try { OutputStream os = new FileOutputStream("D:\\letter\\iText_22.pdf"); ITextRenderer renderer = new ITextRenderer(); // String url = new File(inputFile).toURI().toURL().toString(); System.out.println(html); renderer.setDocumentFromString(html); // 解决中文支持问题 ITextFontResolver fontResolver = renderer.getFontResolver(); fontResolver.addFont("C:/Windows/Fonts/SIMSUN.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); /// 宋体 //// 下面为黑体 fontResolver.addFont("C:/Windows/Fonts/simhei.ttf",BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); //解决图片的相对路径问题 renderer.getSharedContext().setBaseURL("file:G:\\tmp\\practice/"); renderer.layout(); renderer.createPDF(os); os.flush(); os.close(); } catch (DocumentException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return true; } public String getServletPath(String policyNo){ String policyInfoSql = "select a.insureordertype, 'BJ' from FCOrder a where a.insureorderid = ?"; SSRS tSSRS = PubFun.queryData(policyInfoSql, policyNo); if(tSSRS.MaxRow != 1){ log.error("获取保单信息失败,参数: "+policyNo+"SQL:"+policyInfoSql); return null; } String policyType = tSSRS.GetText(1, 1); String subType = tSSRS.GetText(1, 2); String servletPath = "/insure/PolicyFPDPrint"+policyType+subType; return servletPath; }}
2). 修改源代码:添加宋体支持,Flying Saucer ,大家想想估计也能发现,以上的方法在Linux是行不通的,至少Linux没有C盘。所以我们只能通过iText本身的支持添加字体了(说实话,我没在Linux下试过,在windows上试了试可以正常显示的,感觉应该能在Linux下也能正常工作):
然后就可以直接使用了,只需要页面中的css字体修改为 font-family:STSong; (大小写敏感)
我修改好了的jar包:http://download.csdn.net/detail/tingyingg/9706074
(2).表格中TD换行的话,我试了网上说的方法是可行的的,但达不到我想要的效果,因为设置了样式之后,你表格中的 td 大小就不能再次调节了,具体方法请参见下面这篇文章:
http://kaka2008.iteye.com/blog/1005894
根据我想要的效果是,我的思路是,在TD中再添加DIV,然后固定你想要的长度,这样你就可以控制你的TD大小了,不知道是不是所有jar都支持中文换行,我用的这个jar是支持DIV换行的。
(3). 粗体:
你发现,无论是 STSong, 还是 SimSun, 当你设置粗体之后,在页面显示是加粗的,但打印出的PDF,却怎么也无法加粗, 这是我阅读源码的原因之一,但可惜花了两天左右的时间,我还是找不出解决的方法; 最后,我忽然发现我可以用黑体代替粗体, 而且效果还不错, 如果客户没那么严格要求,或者说是又不想多花钱用商业软件的话,这个效果已经很不错了。
---------------------- 如果有谁有其他更好的方法的话,一定记得分享出来...................
(4)。 Checkbox,多选框无法显示。
这也是一个致命的问题啊,我一开始想到的最坏的打算是图片代替, 这样的话会比较麻烦,因为有很多Checkbox的时候,加载,判断都是很麻烦的;
最后我的思路是(稍微比上面这个好点),用一个特殊字符 √ , 还可以调节大小(font-size),哈哈,这个在中文下可以显示出来,不知道英文会不会显示正常,然后在外面套一个 div ,固定个大小, 不就成多选框了吗。当然,你得封装判断的方法,我的方法是,调用一个类,在类里面从数据库中查询各个选项的值,然后判断,是不是被选中,选中的话,返回一个 className (color:#000),把这个class赋值给div,就显示打钩,否则,返回另一个 className(color:#fff),这样打钩在框框里面就看不见了,
<div class="<%=clasY %>">√</div>是 <div class="<%=clasN %>">√</div>否</td>
它俩所对应的css
.chk{width:12px;height:12px;line-height:12px;font-size:12px;display:inline;border:1px solid #444444;margin:0px 4px 0px 4px;color:#ffffff;} .chked{width:12px;font-size:12px;display:inline;border:1px solid #444444; margin:0px 4px 0px 4px;font-weight:bold;}
我在后台写了个方法,是返回class对于的值然后赋值给 div中的class的,这样你就可以根据你的类型,和值判断是否跟后台的值是不是一样的,一样的话返回chked,否则返回chk:
public String getCheckData(String dataType,String codeKey){ String clasName = null; String val = chkData.get(dataType); val = chkData.get(dataType); clasName = (codeKey.equals(val)) ? "chked" : "chk"; if(dataType.equals("10100103")){ System.out.println("codeKey :"+codeKey); } return clasName; }
最后分享一下我做出来的效果吧,
要求不是太高的话,这些基本能满足这些客户要求了。 done。
0 0
- 补充 Flying Saucer 不支持中文,换行,粗体,CheckBox多选框如何解决
- flying saucer 中文不显示、以及中文换行的问题解决
- itext+flying saucer 生成PDF不换行
- 解决iText+flying saucer+freemark导出pdf不支持base64的解决办法
- 解决iText+flying saucer+freemark导出pdf不支持base64的解决办法
- iText+Flying Saucer生成pdf文档,中文不显示和不自动换行问题
- flying saucer
- flying saucer生成pdf
- CODEFORCES Flying Saucer Segments
- itext+Flying Saucer生成pdf
- 使用Flying Saucer生成pdf
- Flying Saucer生成pdf报表
- freemarker+flying-saucer生成pdf
- flying-saucer + iText + Freemarker实现pdf的导出, 支持中文、css以及图片
- Flying Sauser不支持中文的分析及处理方法
- freemark与flying saucer生成PDF
- flying saucer技术生成pdf文档
- iTextRenderer(Flying Saucer) HTML转PDF
- 计算机组成原理期末复习_清华大学_2016秋
- Atitit 补充说明 sql知识图谱与线路图attilax总结补充说明
- laravel维护模式
- STM32F407启动流程浅析
- g_1
- 补充 Flying Saucer 不支持中文,换行,粗体,CheckBox多选框如何解决
- 日常总结的知识点(2)
- (5)预定义实体与CDATA节
- Activity启动模式与任务栈(Task)全面深入记录
- LeetCode 20:Valid Parentheses
- iOS开发中证书相关的操作
- C# 中的一些 基本语句,循环的格式.2
- 从源码分析 HashMap
- Android弹出框BottomSheet