利用ItextPdf、core-renderer-R8 来生成PDF

来源:互联网 发布:java快速入门 pdf 编辑:程序博客网 时间:2024/06/04 21:56

目录(?)[+]

最近由于工作上的需要,需要做一个简历产品的下载功能,而下载的形式要去为PDF,内容要求为整个简历的内容,而且格式上要求和简历的格式排版时一致的!前期调研、开发,最后测试上线,差不多花了7天的时间,当然,期间主要完成了主体功能,现在的话,该功能已经相当完善。下面,我主要是总结下我在这个开发的过程中遇到的问题和总结的心得,希望能帮组有这方面需要的人。

原创文章,转载请注明出处:http://blog.csdn.net/jessonlv

前期调研

前期调研的时候,在网上看了很多关于转pdf的相关文章和技术框架,详细的我不想在此一一赘述,总体给我的感觉就是,第一:国外的相关技术框架做的就是好,关于这方面的,基本都是国外的技术,最多也就是国内牛人改改源码,来适应中文等相关的本土化需要。第二:国内有关生产pdf的需求一般都很简单,要么就是简单文本,复杂的最多也就是相关报表等,基本么有自己想要实现的那么复杂的内容、排版。尤其是生成的内容要和也也面上的内容完全一致,样式排版完全一致!

需求和思路

具体需求就是:

1、产品是一个简历产品,简历上展示的所有数据都是通过动态获取的。
2、要求内容一致,并保持样式排版一致!
首先大家可以看下我们这个产品的下载功能的应用,网址:www.mojianli.com  右上角的下载功能。 

思路    

大体思路:取出简历所有数据-->通过freemarker生成静态页面-->将html静态页面转换成PDF
这样的思路主要是保证pdf的样式要和页面样式一致。

1、首先通过相关功能接口,取出这个简历的所有数据。

2、通过freemarker排版输出的html静态页面。静态页面的样式决定了生成的pdf的样式。

3、读取html静态页面,转换成pdf。

4、将pdf输出在浏览器,实现下载功能。

开发过程

第一步:取出相关简历的所有数据

这个是和项目相关的,就不再此赘述,换成你自己想要生成的pdf内容即可。

第二步:通过freemarker生成静态页面。

首先,利用了freemarker框架,对此框架不熟悉的请自行学习,本问的重点是生成pdf,为了让大家明白此功能的应用场景,所有写了很多,但用到的pdf之外的相关技术,请大家自行学习。
freemarker模板代码:
[java] view plain copy
  1. <#assign freemarkerTool= "com.shengao.mojianli.util.FreemarkerTool"?new()>    
  2. <html>  
  3.   
  4. <head>  
  5.     <title>mojianli</title>  
  6.         <style>  
  7.         @font-face {  
  8.             font-family: 'Microsoft YaHei';  
  9.             font-family: 'Arial';  
  10.         }  
  11.   
  12.         html, body, p {  
  13.             margin: 0;  
  14.             padding: 0;  
  15.         }  
  16.   
  17.         span {  
  18.             line-height: 1px;  
  19.         }  
  20.   
  21.         body {  
  22.             font-family: 'Microsoft YaHei';  
  23.             font-size: 11px;  
  24.             color: #666666;  
  25.         }  
  26.   
  27.         .wrapper {  
  28.             width: 900px;  
  29.             margin: 0 auto;  
  30.         }  
  31.   
  32.         .block {  
  33.             margin-top: 20px;//+2  
  34.         }  
  35.   
  36.         .align-center {  
  37.             text-align: center;  
  38.             margin-left: 252px;  
  39.             width: 200px;  
  40.         }  
  41.   
  42.         #name {  
  43.             font-size: 25px;  
  44.             margin-top: 0px;  
  45.             color: #333333;  
  46.             margin-bottom: 0;  
  47.         }  
  48.   
  49.         #phone {  
  50.             font-size: 12px;  
  51.             margin-top: 12px;  
  52.             font-family: "Arial";  
  53.         }  
  54.   
  55.         #email {  
  56.             font-size: 11px;  
  57.             margin-top: 4px;  
  58.             font-family: "Arial";  
  59.         }  
  60.   
  61.         .timestamp {  
  62.             display: inline-block;  
  63.             width: 110px;  
  64.         }  
  65.   
  66.         .title {  
  67.             font-size: 13px;  
  68.         }  
  69.   
  70.         .simple-module {  
  71.             margin-bottom: 10px;  
  72.             font-size: 11px;  
  73.         }  
  74.           
  75.         .simple-module-item{  
  76.             font-size: 11px;  
  77.             margin-right: 16px;  
  78.         }  
  79.         .simple-item {  
  80.             font-size: 11px;  
  81.             margin-right: 16px;  
  82.         }  
  83.         .item_thing {  
  84.             font-size: 11px;  
  85.             margin-right: 6px;  
  86.             line-height: 18px;  
  87.         }  
  88.         .label {  
  89.             background: #666;  
  90.             border-radius: 1px;  
  91.             color: white;  
  92.             font-size: 7px;  
  93.             position: relative;  
  94.             top: -1px;  
  95.             line-height: 7px;  
  96.         }  
  97.   
  98.         .product {  
  99.             margin-left: 150px;  
  100.             margin-bottom: 5px;  
  101.             margin-top: 4px;  
  102.             width:750px;  
  103.         }  
  104.   
  105.         .capacity-block {  
  106.             margin-left: 22px;  
  107.         }  
  108.   
  109.         .things {  
  110.             padding-left: 12px;  
  111.             margin-top: 5px;  
  112.             background: url(img/dot.png) left top no-repeat;  
  113.         }  
  114.   
  115.         .tag {  
  116.             background: #333333;  
  117.             border-radius: 1px;  
  118.             color: white;  
  119.             font-size: 11px;  
  120.             padding: 0 1px 1px 1px;  
  121.             margin-right: 4px;  
  122.             border-radius: 1px;  
  123.                
  124.         }  
  125.         .enterprise{  
  126.             margin-bottom: 9px;  
  127.         }  
  128.         .enterprise .simple-item {  
  129.           margin-bottom: 5px;  
  130.         }  
  131.         .capacity-group {  
  132.           margin-top: 5px;  
  133.           width: 520px;  
  134.         }  
  135.         .paragraph {  
  136.             line-height: 16px;  
  137.             font-size: 11px;  
  138.             margin-bottom: 10px;  
  139.             width: 700px;  
  140.         }  
  141.   
  142.         .h-seperator {  
  143.             border-color: #666;  
  144.             margin-top: 7px;  
  145.             height: 0;  
  146.             border-top: none;  
  147.             border-bottom: 1px solid;  
  148.             margin-bottom: 6px;  
  149.         }  
  150.         .company,.department,.title{  
  151.             color: #333;  
  152.         }  
  153.     </style>  
  154. </head>  
  155.   
  156. <body>  
  157. <#escape x as x!""></#escape>  
  158. <div class="wrapper">  
  159.     <div class="block align-center" id="contact">  
  160.         <p id="name">${name}</p>  
  161.   
  162.         <p id="phone">${phone}</p>  
  163.   
  164.         <p id="email">${email}</p>  
  165.     </div>  
  166.     <div class="block" id="education">  
  167.         <span class="title">${exp}</span>  
  168.   
  169.         <div class="h-seperator"></div>  
  170.   
  171.     <#list education as education>  
  172.     <p class="simple-module">  
  173.         <span class="timestamp simple-module-item">${education.start_date} ~ ${education.end_date}</span>  
  174.     <span class="simple-module-item"><#if education.university??>${education.university}</#if></span>  
  175.     <span class="simple-module-item"><#if education.colleges??>${education.colleges}</#if> · <#if education.major??>${education.major}</#if></span>  
  176.         <span class="simple-module-item"><#if education.degree??>${education.degree}</#if></span>  
  177.             <span class="simple-module-item"><#if education.explain??>${education.explain}</#if></span>  
  178.                 </p>  
  179.     </#list>  
  180.     </div>  
  181.   
  182.     <!--under-->  
  183.     <!--割一割-->  
  184.     <div class="block" id="experience">  
  185.         <span class="title">${project}</span>  
  186.         <div class="h-seperator"></div>  
  187.   
  188.         <!--项目模板代码开始-->  
  189.         <!--项目经历开始-->  
  190.     <#list experience as experiences>  
  191.         <div class="enterprise">  
  192.             <span class="timestamp simple-item">${experiences.experience.start_date} ~ ${experiences.experience.end_date}</span>  
  193.             <span class="simple-item company">${experiences.experience.company}</span>  
  194.             <span class="simple-item department">${experiences.experience.department}</span>  
  195.             <span class="simple-item title">${experiences.experience.title}</span>  
  196.   
  197.             <!--项目名称开始-->  
  198.             <#list experiences.projects as projects>  
  199.                 <div class="product">  
  200.                     <span class="simple-item">${projects.project.name}</span>  
  201.                     <span class="simple-item">${projects.project.phase}</span>  
  202.                     <span class="simple-item">${projects.project.core_goal}</span>  
  203.   
  204.                     <!--标签、事情开始-->  
  205.                     <#list projects.tags as tags>  
  206.                         <div class="capacity-block">  
  207.                             <div class="capacity-group">  
  208.                                 <!--标签开始-->  
  209.                                 <#list tags.tags as tag>  
  210.                                     <span class="tag">${tag.base_tag_name}</span>  
  211.                                 </#list>  
  212.                                 <!--标签结束-->  
  213.   
  214.                                 <!--事情开始-->  
  215.                                 <#list tags.items as item>  
  216.                                     <div class="things">  
  217.                                         <#list item.labels as label>  
  218.                                             <span class="label">${label.base_label_name}</span>  
  219.                                             <span class="item_thing">${label.content}</span>  
  220.                                         </#list>  
  221.                                     </div>  
  222.                                 </#list>  
  223.                                 <!--事情结束-->  
  224.   
  225.                             </div>  
  226.                         </div>  
  227.                     </#list>  
  228.                     <!--标签、事情结束-->  
  229.   
  230.                 </div>  
  231.             </#list>  
  232.             <!--项目名称结束-->  
  233.   
  234.         </div>  
  235.     </#list>  
  236.         <!--项目名称结束-->  
  237.         <!--项目模板代码结束-->  
  238.     </div>  
  239.   
  240.     <!--割一割-->  
  241.     <div class="block" id="honor">  
  242.         <span class="title">${awards}</span>  
  243.         <div class="h-seperator"></div>  
  244.     <#list awardses as awardses>  
  245.         <p class="simple-module">  
  246.             <span class="simple-item timestamp">${awardses.start_date} ~ ${awardses.end_date}</span>  
  247.             <span class="simple-item"><#if awardses.name??>${awardses.name}</#if></span>  
  248.             <span class="simple-item"><#if awardses.level??>${awardses.level}</#if></span>  
  249.             <span class="simple-item"><#if awardses.rank??>${awardses.rank}</#if></span>  
  250.             <span class="simple-item"><#if awardses.number??>${awardses.number}</#if></span>  
  251.         </p>  
  252.     </#list>  
  253.     </div>  
  254.     <div class="block" id="evaluation">  
  255.         <span class="title">${evaluate}</span>  
  256.         <div class="h-seperator"></div>  
  257.     <#list evaluates as evaluates>  
  258.         <p class="paragraph">${freemarkerTool(evaluates.content)}</p>  
  259.     </#list>  
  260.     </div>  
  261. </div>  
  262. </body>  
  263.   
  264. </html>  

模板相关的数据填充,调用java方法的做法等,网上很多,我也是现学现用的。
利用此模板生成的静态页面的样式,就是你想要的pdf的样式。

然后是读取此模板,生成html页面的代码:
[java] view plain copy
  1. @RequestMapping(value = "/createPdf.s", method = {RequestMethod.POST,RequestMethod.GET})  
  2.     public void getAllResumeInfoById(HttpServletRequest request, HttpServletResponse response,  
  3.                                     @RequestParam(value="id", required = true) Long id) {  
  4.           
  5.         String perName = "";   
  6.         String positionName = "";  
  7.         long resumeId = id;  
  8.         //获取所有的数据  
  9.         //个人基本信息  
  10.         ResumeInfoBean resumeInfo = new ResumeInfoBean();  
  11.         //教育经历  
  12.         List<EducationBean> eduList =  new ArrayList<EducationBean>();  
  13.         //获奖经历  
  14.         List<AwardsBean> awardsList = new ArrayList<AwardsBean>();  
  15.         //个人评价  
  16.         List<EvaluateBean> evaList = new ArrayList<EvaluateBean>();  
  17.         //项目经历  
  18.         List<PdfExperience> pdfExperience = new ArrayList<PdfExperience>();  
  19.         try {  
  20.             Map<String, Object> map = resumeInfoService.getAllResumeInfoById(id);  
  21.               
  22.             resumeInfo = (ResumeInfoBean)map.get("resumeInfo");  
  23.             eduList = (List<EducationBean>)map.get("education");  
  24.             awardsList = (List<AwardsBean>)map.get("awards");  
  25.             evaList = (List<EvaluateBean>)map.get("evaluates");  
  26.             pdfExperience = (List<PdfExperience>)map.get("experiences");  
  27.               
  28.             System.out.println("finish...pdfExperience.size=="+pdfExperience.size());  
  29.         } catch (Exception e) {  
  30.             log.warn(e);  
  31.             JsonUtil.errorToClient(response, 400, e.getMessage());  
  32.             return;  
  33.         }  
  34.         //工程路径  
  35.         String path = request.getSession().getServletContext().getRealPath("/");  
  36.         try {  
  37.   
  38.             Configuration cfg = new Configuration();  
  39.             cfg.setDirectoryForTemplateLoading(new File(getPath(request,response)));  
  40.             cfg.setObjectWrapper(new DefaultObjectWrapper());  
  41.             cfg.setDefaultEncoding("UTF-8");   //这个一定要设置,不然在生成的页面中会乱码  
  42.   
  43.             //设置对象包装器  
  44.             cfg.setObjectWrapper(new DefaultObjectWrapper());  
  45.   
  46.             //设计异常处理器  
  47.             cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);  
  48.   
  49.             //准备魔简历数据  
  50.             Map<String, Object> ResumeMap = new HashMap<String, Object>();  
  51.   
  52.             //头部信息bean对象  
  53.             String name = resumeInfo.getName();  
  54.             perName = name;  
  55.             positionName = resumeInfo.getBase_position_name();  
  56.             String phone = resumeInfo.getMobile();  
  57.             String email = resumeInfo.getEmail();  
  58.             ResumeMap.put("name",name);  
  59.             ResumeMap.put("phone",phone);  
  60.             ResumeMap.put("email",email);  
  61.   
  62.             //定义四个模块标题  
  63.             ResumeMap.put("exp","教育经历");  
  64.             ResumeMap.put("project","项目经历");  
  65.             ResumeMap.put("awards","获奖经历");  
  66.             ResumeMap.put("evaluate","个人评价");  
  67.   
  68.             //封装教育经历对象数据  
  69.             ResumeMap.put("education", eduList);  
  70.             String streducationlist = JsonUtil.list2json(eduList);  
  71.                   
  72.             //封装项目经历数据  
  73.             ResumeMap.put("experience", pdfExperience);  
  74.             String strEx= JsonUtil.list2json(pdfExperience);  
  75.             System.out.print(strEx);  
  76.             //封装获奖经历数据  
  77.             ResumeMap.put("awardses",awardsList);  
  78.             String strawardsList = JsonUtil.list2json(awardsList);  
  79.   
  80.             //封装个人评价数据  
  81.             ResumeMap.put("evaluates",evaList);  
  82.             String strevaList = JsonUtil.list2json(evaList);  
  83.   
  84.             //获取指定模板文件  
  85.             Template template = cfg.getTemplate("mojianli.ftl");  
  86.   
  87.             //控制台打印  
  88.             template.process(ResumeMap, new PrintWriter(System.out));  
  89.   
  90.             //定义输入文件,默认生成在工程根目录  
  91.             String s = getPath(request,response);  
  92.             path = s+id+"_mojianli.html";  
  93.             Writer out = new OutputStreamWriter(new FileOutputStream(path),"UTF-8");  
  94.   
  95.             //最后开始生成  
  96.             template.process(ResumeMap, out);  
  97.             System.out.println("create the html successful!!!"+"path="+request.getSession().getServletContext().getRealPath("/"));  
  98.         }catch (Exception e){  
  99.             e.printStackTrace();  
  100.             jsonObjOutPut.clear();  
  101.             jsonObjOutPut = JsonUtil.createJsonObject(MSG.STATUS_RESPONSE_FAIL_201, MSG.MSG_RESPONSE_FAIL_201);  
  102.             stringOutPutData = JsonUtil.object2json(jsonObjOutPut);  
  103.             JsonUtil.jsonStringToClient(response,stringOutPutData);  
  104.         }  
  105.         boolean boo = false;  
  106.         try {  
  107.             boo =  html2Pdf(request,response,perName,resumeId,positionName);  
  108.         }catch (Exception e){  
  109.             e.printStackTrace();  
  110.             jsonObjOutPut.clear();  
  111.             jsonObjOutPut = JsonUtil.createJsonObject(MSG.STATUS_RESPONSE_FAIL_201, MSG.MSG_RESPONSE_FAIL_201);  
  112.             stringOutPutData = JsonUtil.object2json(jsonObjOutPut);  
  113.             JsonUtil.jsonStringToClient(response,stringOutPutData);  
  114.         }  
  115.  }  

前半段关于数据的封装等的代码可以不管,填上自己的数据就行了。

生成页面有就是读取相关页面,并生成pdf的代码

[java] view plain copy
  1. //html转成pdf  
  2.     private boolean html2Pdf(HttpServletRequest request,HttpServletResponse response,String name,long id,String postionName) throws  IOException, DocumentException, ParserConfigurationException {  
  3.   
  4.     boolean bl = false;  
  5.     //工程路径  
  6.     /*String separator = File.separator; 
  7.     String root = request.getSession().getServletContext().getRealPath(""); 
  8.     String path = root+separator+"WEB-INF"+separator+"resources"+name+"_"+id+"_mojianli.html";*/  
  9.     String path = getPath(request,response)+id+"_mojianli.html";  
  10.     //获取已经生成的html页面的路径  
  11.     //String path = "F:\\tomcat_myeclipse\\webapps\\mojianli\\WEB-INF\\resources\\mojianli.html";  
  12.   
  13.     //读取html  
  14.     FileInputStream fis =new FileInputStream(path);  
  15.     StringWriter writers = new StringWriter();  
  16.     InputStreamReader isr = null;  
  17.     String string = null;  
  18.     //此处将io流转换成String  
  19.     try {  
  20.         isr = new InputStreamReader(fis,"utf-8");//包装基础输入流且指定编码方式  
  21.         //将输入流写入输出流  
  22.         char[] buffer = new char[2048];  
  23.         int n = 0;  
  24.         while (-1 != (n = isr.read(buffer))) {  
  25.             writers.write(buffer, 0, n);  
  26.         }  
  27.     }catch (Exception e){  
  28.         e.printStackTrace();  
  29.     } finally {  
  30.         if (isr != null)  
  31.             try {  
  32.                 isr.close();  
  33.             } catch (IOException e) {  
  34.                 e.printStackTrace();  
  35.                 jsonObjOutPut.clear();  
  36.                 jsonObjOutPut = JsonUtil.createJsonObject(MSG.STATUS_RESPONSE_FAIL_201, MSG.MSG_RESPONSE_FAIL_201);  
  37.                 stringOutPutData = JsonUtil.object2json(jsonObjOutPut);  
  38.                 JsonUtil.jsonStringToClient(response,stringOutPutData);  
  39.             }  
  40.     }  
  41.     if (writers!=null){  
  42.         string = writers.toString();  
  43.     }  
  44.     System.out.print(string);  
  45.     //利用renderer来准备数据  
  46.     ITextRenderer renderer = new ITextRenderer();  
  47.     ITextFontResolver fontResolver = renderer.getFontResolver();  
  48.   
  49.     //设置创建PDF的时候要用的字体,此字体必须要和简历模板的字体保持一致!!  
  50.     fontResolver.addFont(getPath(request, response)+"msyh.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);  
  51.     fontResolver.addFont(getPath(request, response)+"arial.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);  
  52.   
  53.     //get font family name  
  54.     BaseFont font = null;  
  55.     BaseFont font2 = null;  
  56.     try {  
  57.         font = BaseFont.createFont(getPath(request, response)+"msyh.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);  
  58.         font2 = BaseFont.createFont(getPath(request, response)+"arial.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);  
  59.     } catch (DocumentException e) {  
  60.         e.printStackTrace();  
  61.         jsonObjOutPut.clear();  
  62.         jsonObjOutPut = JsonUtil.createJsonObject(MSG.STATUS_RESPONSE_FAIL_201, MSG.MSG_RESPONSE_FAIL_201);  
  63.         stringOutPutData = JsonUtil.object2json(jsonObjOutPut);  
  64.         JsonUtil.jsonStringToClient(response,stringOutPutData);  
  65.     }  
  66.   
  67.     //fontFamilyName‘s  value is the key for font-family  
  68.     String fontFamilyName = TrueTypeUtil.getFamilyName(font2);  
  69.     System.out.println("fontFamilyName222="+fontFamilyName);  
  70.   
  71.     //设置pdf内容!!  
  72.     renderer.setDocumentFromString(string);  
  73.     //设置图片的绝对路径  
  74.     renderer.getSharedContext().setBaseURL("file:"+getPath(request,response)+"\\img");  
  75.     System.out.println(getPath(request,response)+"img");  
  76.     renderer.layout();  
  77.   
  78.     //create the pdf  
  79.       
  80.     //String pdfPath = path+"WEB-INF\\resources\\"+name+"_mojianli.pdf";  
  81.     String pdfPath = getPath(request, response)+id+"_mojianli.pdf";  
  82.     FileOutputStream outputStream = new FileOutputStream(pdfPath);//文件输出根目录下  
  83.     renderer.createPDF(outputStream);  
  84.     
  85.     //Finishing up  
  86.     //renderer.finishPDF();  
  87.     System.out.println("created the pdf !!");  
  88.     //下载  
  89.     try{  
  90.         //downloadPdf(response,request,name,outputStream);    
  91.         downLoadPdf(request,response,name,id,postionName);  
  92.     }catch (Exception e ){  
  93.         e.printStackTrace();  
  94.     }  
  95.       
  96.     bl = true;  
  97.     return  bl;  
  98.     }  

这里需要注意的两点是:1、设置中文字体,以及中文字体文件的引用2、引用图片的问题。 仔细看代码注释,上面都有!

生成pdf以后,就是推送到浏览器的问题:
[java] view plain copy
  1. public void downLoadPdf(HttpServletRequest request, HttpServletResponse response,String name,long id,String postionName) {  
  2.   
  3.         try {  
  4.               
  5.             String separator = File.separator;  
  6.               
  7.             String root = request.getSession().getServletContext().getRealPath("");  
  8.               
  9.             String filePath = root+separator+"WEB-INF"+separator+"resources";  
  10.             String headerName = new String(name.getBytes("utf-8"),"iso8859_1");//解决下载文件中文标题乱码问题  
  11.             String postion = new String(postionName.getBytes("utf-8"),"iso8859_1");  
  12.             response.setCharacterEncoding("UTF-8");  
  13.             response.setContentType("application/pdf");  
  14.             response.setHeader("Content-Disposition""attachment; filename="+headerName+"-"+postion+".pdf");  
  15.               
  16.             OutputStream outputStream = response.getOutputStream();  
  17.             InputStream inputStream = new FileInputStream(filePath + separator+id+"_mojianli.pdf");  
  18.   
  19.             byte[] buffer = new byte[1024];  
  20.             int i = -1;  
  21.             while ((i = inputStream.read(buffer)) != -1)   
  22.             {  
  23.                 outputStream.write(buffer, 0, i);  
  24.             }  
  25.             outputStream.flush();  
  26.             //outputStream.close();  
  27.             inputStream.close();  
  28.               
  29.         } catch (Exception e) {  
  30.             e.printStackTrace();  
  31.             log.warn(e);  
  32.             JsonUtil.errorToClient(response, 400, e.getMessage());  
  33.             return;  
  34.         }  
  35.     }  
这里的注意点是:注意下载文件中文标题乱码问题

至此,以上总体的代码大概是这样,需要的人,可以多看看,如果有什么问题,欢迎随时私信、留言等交流。

原创粉丝点击