Flying Saucer 不支持中文,换行,粗体,CheckBox多选框的解决方案

 最近要生成打印版的保单信息,内容比较多,也比较复杂,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  对象中返回给客户端

  1. import java.io.ByteArrayOutputStream;
  2. import java.io.IOException;
  3. import java.io.OutputStreamWriter;
  4. import java.io.PrintWriter;
  5. import java.io.UnsupportedEncodingException;
  6. import javax.servlet.ServletOutputStream;
  7. import javax.servlet.http.HttpServletResponse;
  8. import javax.servlet.http.HttpServletResponseWrapper;
  9. public class PDFResponseWrapper extends HttpServletResponseWrapper {
  10.     private ByteArrayOutputStream buffer = null; 
  11.      private ServletOutputStream out = null; 
  12.      private PrintWriter writer = null; 
  13.      public PDFResponseWrapper(HttpServletResponse resp) throws IOException { 
  14.          super(resp); 
  15.          buffer = new ByteArrayOutputStream();// 真正存储数据的流 
  16.          out = new WapperedOutputStream(buffer); 
  17.          String encoding = resp.getCharacterEncoding();
  18.          writer = new PrintWriter(new OutputStreamWriter(buffer, encoding)); 
  19.      } 
  20.      /**重载父类获取outputstream的方法*/ 
  21.      @Override 
  22.      public ServletOutputStream getOutputStream() throws IOException { 
  23.          return out; 
  24.      } 
  25.      /**重载父类获取writer的方法*/ 
  26.      @Override 
  27.      public PrintWriter getWriter() throws UnsupportedEncodingException { 
  28.          return writer; 
  29.      } 
  30.      /**重载父类获取flushBuffer的方法*/ 
  31.      @Override 
  32.      public void flushBuffer() throws IOException { 
  33.          if (out != null) { 
  34.              out.flush(); 
  35.          } 
  36.          if (writer != null) { 
  37.              writer.flush(); 
  38.          } 
  39.      } 
  40.      @Override 
  41.      public void reset() { 
  42.          buffer.reset(); 
  43.      } 
  44.      /**将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据*/ 
  45.     public byte[] getResponseData() throws IOException { 
  46.          flushBuffer(); 
  47.          return buffer.toByteArray(); 
  48.      } 
  49.      /**内部类,对ServletOutputStream进行包装*/ 
  50.     private class WapperedOutputStream extends ServletOutputStream { 
  51.          private ByteArrayOutputStream bos = null; 
  52.          public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException { 
  53.              bos = stream; 
  54.          } 
  55.          @Override 
  56.          public void write(int b) throws IOException { 
  57.              bos.write(b); 
  58.          } 
  59.          @Override 
  60.          public void write(byte[] b) throws IOException { 
  61.              bos.write(b, 0, b.length); 
  62.          } 
  63.      } 
  64. }
  1. import java.io.IOException;
  2. import java.io.OutputStream;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import org.apache.log4j.Logger;
  7. import com.itextpdf.text.DocumentException;
  8. import com.sinosoft.flex.service.insure.InsuredMaterialDownload;
  9. import com.sinosoft.flex.service.insure.PDFResponseWrapper;
  10. import com.sinosoft.flex.service.insure.PlanApproveBL;
  11. import com.sinosoft.flex.service.insure.PolicyPDFPrintGenerationBL;
  12. public class SubServletForPDF extends SuperServlet{
  13.     /**
  14.      * 
  15.      */
  16.     private static final long serialVersionUID = 1L;
  17.     private static Logger log = Logger.getLogger(PlanApproveBL.class);
  18.     @Override
  19.     public void service(HttpServletRequest request, HttpServletResponse response)
  20.     throws ServletException, IOException {
  21.         String operator1 = "operator1";
  22.         String policyPDF = "policyPDF";
  23.         String operator = request.getParameter("operator");
  24.         ///////////    投保单资料下载1  分红,万能险种资料下载
  25.         if(operator1.equals(operator)){
  26.             /////// Other Logic
  27.         }else if(policyPDF.equals(operator)){
  28.             PolicyPDFPrintGenerationBL pdfBL = new PolicyPDFPrintGenerationBL();
  29.             String printPDF = request.getParameter("print");
  30.             if(printPDF != null && printPDF.equals("Y")){
  31.                ////// Other Logic
  32.             }else{
  33.                 String poNo = request.getParameter("poNo");
  34.                 if(poNo == null || poNo.length()  <1){
  35.                     log.info("获取保单信息失败,请传入保单号");
  36.                     Error err = new Error("查询数据失败");
  37.                      throw new RuntimeErrorException(err);
  38.                 }
  39.                 request.setAttribute("poNo", poNo);
  40.                 String servletPath = pdfBL.getServletPath(poNo); // 获取转发的请求路径,然后把请求转向生成PDFJSP页面
  41.                 response.setCharacterEncoding("UTF-8");
  42.                 PDFResponseWrapper responWrapper = new PDFResponseWrapper(response);
  43.                 request.getRequestDispatcher(servletPath).forward(requestresponWrapper);
  44.                 byte[] data = responWrapper.getResponseData(); // 获取out.write()进来的 html 字节流
  45.                 String str = new String(data,"UTF-8");
  46.                 pdfBL.convertHtmlToPdf(str);  // html格式的 字符串转换成PDF,代码在下面
  47.                 OutputStream out = response.getOutputStream();
  48.                 out.write(data);
  49.                 out.close();
  50.             }
  51.         }
  52.     }
  53. }

2. Flying Saucer 不支持中文,换行,粗体,CheckBox多选框,我是如何解决的?

    (1)、中文的话,网上有很多说明了,在此我大概说一下,然后给出代码:

        1)、如同网上所说的,直接在代码里面添加中文字体支持,然后CSS中也要设置相关字体(注意大小写一致)
        
[css]  view plain  copy
  1. <code class="language-html"> body{MARGIN:AUTO;width:690px;font-size:12pxfont-family:SimHei; color:#222;} /// 黑体  
  2.  body{MARGIN:AUTO;width:690px;font-size:12pxfont-family:SimSun; color:#222;} ///  宋体  
  3.   
  4. </code>  


  1. import java.io.FileOutputStream;
  2. import java.io.IOException;
  3. import java.io.OutputStream;
  4. import org.apache.log4j.Logger;
  5. import org.xhtmlrenderer.pdf.ITextFontResolver;
  6. import org.xhtmlrenderer.pdf.ITextRenderer;
  7. import com.lowagie.text.DocumentException;
  8. import com.lowagie.text.pdf.BaseFont;
  9. import com.sinosoft.flex.pubfun.PubFun;
  10. import com.sinosoft.utility.SSRS;
  11. public class PolicyPDFPrintGenerationBL {
  12.     private static Logger log = Logger.getLogger(PlanApproveBL.class);
  13.     public boolean convertHtmlToPdf(String html){  
  14.         try {
  15.             OutputStream os = new FileOutputStream("D:\\letter\\iText_22.pdf");       
  16.             ITextRenderer renderer = new ITextRenderer();       
  17. //        String url = new File(inputFile).toURI().toURL().toString();   
  18.             System.out.println(html);
  19.             renderer.setDocumentFromString(html);     
  20.             // 解决中文支持问题       
  21.             ITextFontResolver fontResolver = renderer.getFontResolver();      
  22.             fontResolver.addFont("C:/Windows/Fonts/SIMSUN.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);  /// 宋体
  23.                 //// 下面为黑体
  24.             fontResolver.addFont("C:/Windows/Fonts/simhei.ttf",BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); 
  25.             //解决图片的相对路径问题  
  26.             renderer.getSharedContext().setBaseURL("file:G:\\tmp\\practice/");  
  27.             renderer.layout();      
  28.             renderer.createPDF(os);    
  29.             os.flush();  
  30.             os.close();  
  31.         } catch (DocumentException e) {
  32.             e.printStackTrace();
  33.         } catch (IOException e) {
  34.             e.printStackTrace();
  35.         }       
  36.         return true;  
  37.     }
  38.     public String getServletPath(String policyNo){
  39.         String policyInfoSql = "select a.insureordertype, 'BJ' from FCOrder a where a.insureorderid = ?";
  40.         SSRS tSSRS = PubFun.queryData(policyInfoSql, policyNo);
  41.         if(tSSRS.MaxRow != 1){
  42.             log.error("获取保单信息失败,参数: "+policyNo+"SQL:"+policyInfoSql);
  43.             return null;
  44.         }
  45.         String policyType = tSSRS.GetText(1, 1);
  46.         String subType = tSSRS.GetText(1, 2);
  47.         String servletPath = "/insure/PolicyFPDPrint"+policyType+subType;
  48.         return servletPath;
  49.     }
  50. }


    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 大小就不能再次调节了,具体方法请参见下面这篇文章:

    
    根据我想要的效果是,我的思路是,在TD中再添加DIV,然后固定你想要的长度,这样你就可以控制你的TD大小了,不知道是不是所有jar都支持中文换行,我用的这个jar是支持DIV换行的。

    (3)粗体:

            你发现,无论是 STSong, 还是 SimSun, 当你设置粗体之后,在页面显示是加粗的,但打印出的PDF,却怎么也无法加粗, 这是我阅读源码的原因之一,但可惜花了两天左右的时间,我还是找不出解决的方法;  最后,我忽然发现我可以用黑体代替粗体, 而且效果还不错, 如果客户没那么严格要求,或者说是又不想多花钱用商业软件的话,这个效果已经很不错了。
                ----------------------  如果有谁有其他更好的方法的话,一定记得分享出来...................

      (4) Checkbox,多选框无法显示。

               这也是一个致命的问题啊,我一开始想到的最坏的打算是图片代替,  这样的话会比较麻烦,因为有很多Checkbox的时候,加载,判断都是很麻烦的;
                最后我的思路是(稍微比上面这个好点),用一个特殊字符   √   , 还可以调节大小(font-size),哈哈,这个在中文下可以显示出来,不知道英文会不会显示正常,然后在外面套一个 div ,固定个大小, 不就成多选框了吗。当然,你得封装判断的方法,我的方法是,调用一个类,在类里面从数据库中查询各个选项的值,然后判断,是不是被选中,选中的话,返回一个 className (color:#000),把这个class赋值给div,就显示打钩,否则,返回另一个 className(color:#fff),这样打钩在框框里面就看不见了, 
          
  1. <div class="<%=clasY %>"></div>是  <div class="<%=clasN %>"></div></td>

        它俩所对应的css
  1.   .chk{ width: 12px; height: 12px; line-height: 12px; font-size: 12px; display:inline; border: 1px solid  #444444; margin: 0px  4px  0px  4px; color: #ffffff;}
  2.           .chked{ width: 12px; font-size: 12px; display:inline; border: 1px solid  #444444margin: 0px  4px  0px  4px; font-weight:bold;}



        我在后台写了个方法,是返回class对于的值然后赋值给 div中的class的,这样你就可以根据你的类型,和值判断是否跟后台的值是不是一样的,一样的话返回chked,否则返回chk:
        
    
  1. public String getCheckData(String dataType,String codeKey){
  2.         String clasName =  null;
  3.         String val = chkData.get(dataType);
  4.         val = chkData.get(dataType);
  5.         clasName = (codeKey.equals(val)) ?  "chked" :  "chk";
  6.          if(dataType.equals( "10100103")){
  7.             System.out.println( "codeKey :"+codeKey);
  8.         }
  9.          return clasName;
  10.     }

最后分享一下我做出来的效果吧,




要求不是太高的话,这些基本能满足这些客户要求了。  done。

猜你喜欢

转载自blog.csdn.net/shadow_zed/article/details/80898501
今日推荐