利用ItextPdf、core-renderer-R8 来生成PDF「建议收藏」

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

大家好,又见面了,我是全栈君。

近期因为工作上的须要,须要做一个简历产品的下载功能,而下载的形式要去为PDF,内容要求为整个简历的内容,并且格式上要求和简历的格式排版时一致的!前期调研、开发,最后測试上线。差点儿相同花了7天的时间。当然,期间主要完毕了主体功能。如今的话,该功能已经相当完好。

以下,我主要是总结下我在这个开发的过程中遇到的问题和总结的心得。希望能帮组有这方面须要的人。

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

前期调研

前期调研的时候,在网上看了非常多关于转pdf的相关文章和技术框架,具体的我不想在此一一赘述,整体给我的感觉就是,第一:国外的相关技术框架做的就是好,关于这方面的,基本都是国外的技术,最多也就是国内牛人改改源代码。来适应中文等相关的本土化须要。

第二:国内有关生产pdf的需求一般都非常easy。要么就是简单文本,复杂的最多也就是相关报表等,基本么有自己想要实现的那么复杂的内容、排版。

尤其是生成的内容要和也也面上的内容全然一致,样式排版全然一致!

需求和思路

详细需求就是:

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模板代码:
<#assign freemarkerTool= "com.shengao.mojianli.util.FreemarkerTool"?new()> <html> <head> <title>mojianli</title> <style> @font-face { font-family: 'Microsoft YaHei'; font-family: 'Arial'; } html, body, p { margin: 0; padding: 0; } span { line-height: 1px; } body { font-family: 'Microsoft YaHei'; font-size: 11px; color: #666666; } .wrapper { width: 900px; margin: 0 auto; } .block { margin-top: 20px;//+2 } .align-center { text-align: center; margin-left: 252px; width: 200px; } #name { font-size: 25px; margin-top: 0px; color: #333333; margin-bottom: 0; } #phone { font-size: 12px; margin-top: 12px; font-family: "Arial"; } #email { font-size: 11px; margin-top: 4px; font-family: "Arial"; } .timestamp { display: inline-block; width: 110px; } .title { font-size: 13px; } .simple-module { margin-bottom: 10px; font-size: 11px; } .simple-module-item{ font-size: 11px; margin-right: 16px; } .simple-item { font-size: 11px; margin-right: 16px; } .item_thing { font-size: 11px; margin-right: 6px; line-height: 18px; } .label { background: #666; border-radius: 1px; color: white; font-size: 7px; position: relative; top: -1px; line-height: 7px; } .product { margin-left: 150px; margin-bottom: 5px; margin-top: 4px; width:750px; } .capacity-block { margin-left: 22px; } .things { padding-left: 12px; margin-top: 5px; background: url(img/dot.png) left top no-repeat; } .tag { background: #333333; border-radius: 1px; color: white; font-size: 11px; padding: 0 1px 1px 1px; margin-right: 4px; border-radius: 1px; } .enterprise{ margin-bottom: 9px; } .enterprise .simple-item { margin-bottom: 5px; } .capacity-group { margin-top: 5px; width: 520px; } .paragraph { line-height: 16px; font-size: 11px; margin-bottom: 10px; width: 700px; } .h-seperator { border-color: #666; margin-top: 7px; height: 0; border-top: none; border-bottom: 1px solid; margin-bottom: 6px; } .company,.department,.title{ color: #333; } </style> </head> <body> <#escape x as x!""></#escape> <div class="wrapper"> <div class="block align-center" id="contact"> <p id="name">${name}</p> <p id="phone">${phone}</p> <p id="email">${email}</p> </div> <div class="block" id="education"> <span class="title">${exp}</span> <div class="h-seperator"></div> <#list education as education> <p class="simple-module"> <span class="timestamp simple-module-item">${education.start_date} ~ ${education.end_date}</span> <span class="simple-module-item"><#if education.university??>${education.university}</#if></span> <span class="simple-module-item"><#if education.colleges??>${education.colleges}</#if> · <#if education.major??

>${education.major}</#if></span> <span class="simple-module-item"><#if education.degree?

?>${education.degree}</#if></span> <span class="simple-module-item"><#if education.explain??>${education.explain}</#if></span> </p> </#list> </div> <!--under--> <!--割一割--> <div class="block" id="experience"> <span class="title">${project}</span> <div class="h-seperator"></div> <!--项目模板代码開始--> <!--项目经历開始--> <#list experience as experiences> <div class="enterprise"> <span class="timestamp simple-item">${experiences.experience.start_date} ~ ${experiences.experience.end_date}</span> <span class="simple-item company">${experiences.experience.company}</span> <span class="simple-item department">${experiences.experience.department}</span> <span class="simple-item title">${experiences.experience.title}</span> <!--项目名称開始--> <#list experiences.projects as projects> <div class="product"> <span class="simple-item">${projects.project.name}</span> <span class="simple-item">${projects.project.phase}</span> <span class="simple-item">${projects.project.core_goal}</span> <!--标签、事情開始--> <#list projects.tags as tags> <div class="capacity-block"> <div class="capacity-group"> <!--标签開始--> <#list tags.tags as tag> <span class="tag">${tag.base_tag_name}</span> </#list> <!--标签结束--> <!--事情開始--> <#list tags.items as item> <div class="things"> <#list item.labels as label> <span class="label">${label.base_label_name}</span> <span class="item_thing">${label.content}</span> </#list> </div> </#list> <!--事情结束--> </div> </div> </#list> <!--标签、事情结束--> </div> </#list> <!--项目名称结束--> </div> </#list> <!--项目名称结束--> <!--项目模板代码结束--> </div> <!--割一割--> <div class="block" id="honor"> <span class="title">${awards}</span> <div class="h-seperator"></div> <#list awardses as awardses> <p class="simple-module"> <span class="simple-item timestamp">${awardses.start_date} ~ ${awardses.end_date}</span> <span class="simple-item"><#if awardses.name??>${awardses.name}</#if></span> <span class="simple-item"><#if awardses.level??>${awardses.level}</#if></span> <span class="simple-item"><#if awardses.rank??>${awardses.rank}</#if></span> <span class="simple-item"><#if awardses.number??

>${awardses.number}</#if></span> </p> </#list> </div> <div class="block" id="evaluation"> <span class="title">${evaluate}</span> <div class="h-seperator"></div> <#list evaluates as evaluates> <p class="paragraph">${freemarkerTool(evaluates.content)}</p> </#list> </div></div></body></html>

模板相关的数据填充,调用java方法的做法等。网上非常多,我也是现学现用的。

利用此模板生成的静态页面的样式,就是你想要的pdf的样式。


然后是读取此模板,生成html页面的代码:
@RequestMapping(value = "/createPdf.s", method = {RequestMethod.POST,RequestMethod.GET}) public void getAllResumeInfoById(HttpServletRequest request, HttpServletResponse response, @RequestParam(value="id", required = true) Long id) { String perName = ""; String positionName = ""; long resumeId = id; //获取全部的数据 //个人基本信息 ResumeInfoBean resumeInfo = new ResumeInfoBean(); //教育经历 List<EducationBean> eduList = new ArrayList<EducationBean>(); //获奖经历 List<AwardsBean> awardsList = new ArrayList<AwardsBean>(); //个人评价 List<EvaluateBean> evaList = new ArrayList<EvaluateBean>(); //项目经历 List<PdfExperience> pdfExperience = new ArrayList<PdfExperience>(); try { Map<String, Object> map = resumeInfoService.getAllResumeInfoById(id); resumeInfo = (ResumeInfoBean)map.get("resumeInfo"); eduList = (List<EducationBean>)map.get("education"); awardsList = (List<AwardsBean>)map.get("awards"); evaList = (List<EvaluateBean>)map.get("evaluates"); pdfExperience = (List<PdfExperience>)map.get("experiences"); System.out.println("finish...pdfExperience.size=="+pdfExperience.size()); } catch (Exception e) { log.warn(e); JsonUtil.errorToClient(response, 400, e.getMessage()); return; } //project路径 String path = request.getSession().getServletContext().getRealPath("/"); try { Configuration cfg = new Configuration(); cfg.setDirectoryForTemplateLoading(new File(getPath(request,response))); cfg.setObjectWrapper(new DefaultObjectWrapper()); cfg.setDefaultEncoding("UTF-8"); //这个一定要设置。不然在生成的页面中会乱码 //设置对象包装器 cfg.setObjectWrapper(new DefaultObjectWrapper()); //设计异常处理器 cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER); //准备魔简历数据 Map<String, Object> ResumeMap = new HashMap<String, Object>(); //头部信息bean对象 String name = resumeInfo.getName(); perName = name; positionName = resumeInfo.getBase_position_name(); String phone = resumeInfo.getMobile(); String email = resumeInfo.getEmail(); ResumeMap.put("name",name); ResumeMap.put("phone",phone); ResumeMap.put("email",email); //定义四个模块标题 ResumeMap.put("exp","教育经历"); ResumeMap.put("project","项目经历"); ResumeMap.put("awards","获奖经历"); ResumeMap.put("evaluate","个人评价"); //封装教育经历对象数据 ResumeMap.put("education", eduList); String streducationlist = JsonUtil.list2json(eduList); //封装项目经历数据 ResumeMap.put("experience", pdfExperience); String strEx= JsonUtil.list2json(pdfExperience); System.out.print(strEx); //封装获奖经历数据 ResumeMap.put("awardses",awardsList); String strawardsList = JsonUtil.list2json(awardsList); //封装个人评价数据 ResumeMap.put("evaluates",evaList); String strevaList = JsonUtil.list2json(evaList); //获取指定模板文件 Template template = cfg.getTemplate("mojianli.ftl"); //控制台打印 template.process(ResumeMap, new PrintWriter(System.out)); //定义输入文件。默认生成在project根文件夹 String s = getPath(request,response); path = s+id+"_mojianli.html"; Writer out = new OutputStreamWriter(new FileOutputStream(path),"UTF-8"); //最后開始生成 template.process(ResumeMap, out); System.out.println("create the html successful!!!"+"path="+request.getSession().getServletContext().getRealPath("/")); }catch (Exception e){ e.printStackTrace(); jsonObjOutPut.clear(); jsonObjOutPut = JsonUtil.createJsonObject(MSG.STATUS_RESPONSE_FAIL_201, MSG.MSG_RESPONSE_FAIL_201); stringOutPutData = JsonUtil.object2json(jsonObjOutPut); JsonUtil.jsonStringToClient(response,stringOutPutData); } boolean boo = false; try { boo = html2Pdf(request,response,perName,resumeId,positionName); }catch (Exception e){ e.printStackTrace(); jsonObjOutPut.clear(); jsonObjOutPut = JsonUtil.createJsonObject(MSG.STATUS_RESPONSE_FAIL_201, MSG.MSG_RESPONSE_FAIL_201); stringOutPutData = JsonUtil.object2json(jsonObjOutPut); JsonUtil.jsonStringToClient(response,stringOutPutData); } }

前半段关于数据的封装等的代码能够无论。填上自己的数据即可了。


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

//html转成pdf private boolean html2Pdf(HttpServletRequest request,HttpServletResponse response,String name,long id,String postionName) throws IOException, DocumentException, ParserConfigurationException { boolean bl = false; //project路径 /*String separator = File.separator; String root = request.getSession().getServletContext().getRealPath(""); String path = root+separator+"WEB-INF"+separator+"resources"+name+"_"+id+"_mojianli.html";*/ String path = getPath(request,response)+id+"_mojianli.html"; //获取已经生成的html页面的路径 //String path = "F:\\tomcat_myeclipse\\webapps\\mojianli\\WEB-INF\\resources\\mojianli.html"; //读取html FileInputStream fis =new FileInputStream(path); StringWriter writers = new StringWriter(); InputStreamReader isr = null; String string = null; //此处将io流转换成String try { isr = new InputStreamReader(fis,"utf-8");//包装基础输入流且指定编码方式 //将输入流写入输出流 char[] buffer = new char[2048]; int n = 0; while (-1 != (n = isr.read(buffer))) { writers.write(buffer, 0, n); } }catch (Exception e){ e.printStackTrace(); } finally { if (isr != null) try { isr.close(); } catch (IOException e) { e.printStackTrace(); jsonObjOutPut.clear(); jsonObjOutPut = JsonUtil.createJsonObject(MSG.STATUS_RESPONSE_FAIL_201, MSG.MSG_RESPONSE_FAIL_201); stringOutPutData = JsonUtil.object2json(jsonObjOutPut); JsonUtil.jsonStringToClient(response,stringOutPutData); } } if (writers!=null){ string = writers.toString(); } System.out.print(string); //利用renderer来准备数据 ITextRenderer renderer = new ITextRenderer(); ITextFontResolver fontResolver = renderer.getFontResolver(); //设置创建PDF的时候要用的字体,此字体必需要和简历模板的字体保持一致!!

fontResolver.addFont(getPath(request, response)+"msyh.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); fontResolver.addFont(getPath(request, response)+"arial.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); //get font family name BaseFont font = null; BaseFont font2 = null; try { font = BaseFont.createFont(getPath(request, response)+"msyh.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); font2 = BaseFont.createFont(getPath(request, response)+"arial.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); } catch (DocumentException e) { e.printStackTrace(); jsonObjOutPut.clear(); jsonObjOutPut = JsonUtil.createJsonObject(MSG.STATUS_RESPONSE_FAIL_201, MSG.MSG_RESPONSE_FAIL_201); stringOutPutData = JsonUtil.object2json(jsonObjOutPut); JsonUtil.jsonStringToClient(response,stringOutPutData); } //fontFamilyName‘s value is the key for font-family String fontFamilyName = TrueTypeUtil.getFamilyName(font2); System.out.println("fontFamilyName222="+fontFamilyName); //设置pdf内容。!

renderer.setDocumentFromString(string); //设置图片的绝对路径 renderer.getSharedContext().setBaseURL("file:"+getPath(request,response)+"\\img"); System.out.println(getPath(request,response)+"img"); renderer.layout(); //create the pdf //String pdfPath = path+"WEB-INF\\resources\\"+name+"_mojianli.pdf"; String pdfPath = getPath(request, response)+id+"_mojianli.pdf"; FileOutputStream outputStream = new FileOutputStream(pdfPath);//文件输出根文件夹下 renderer.createPDF(outputStream); //Finishing up //renderer.finishPDF(); System.out.println("created the pdf !!"); //下载 try{ //downloadPdf(response,request,name,outputStream); downLoadPdf(request,response,name,id,postionName); }catch (Exception e ){ e.printStackTrace(); } bl = true; return bl; }

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


生成pdf以后,就是推送到浏览器的问题:
public void downLoadPdf(HttpServletRequest request, HttpServletResponse response,String name,long id,String postionName) { try { String separator = File.separator; String root = request.getSession().getServletContext().getRealPath(""); String filePath = root+separator+"WEB-INF"+separator+"resources"; String headerName = new String(name.getBytes("utf-8"),"iso8859_1");//解决下载文件里文标题乱码问题 String postion = new String(postionName.getBytes("utf-8"),"iso8859_1"); response.setCharacterEncoding("UTF-8"); response.setContentType("application/pdf"); response.setHeader("Content-Disposition", "attachment; filename="+headerName+"-"+postion+".pdf"); OutputStream outputStream = response.getOutputStream(); InputStream inputStream = new FileInputStream(filePath + separator+id+"_mojianli.pdf"); byte[] buffer = new byte[1024]; int i = -1; while ((i = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, i); } outputStream.flush(); //outputStream.close(); inputStream.close(); } catch (Exception e) { e.printStackTrace(); log.warn(e); JsonUtil.errorToClient(response, 400, e.getMessage()); return; } }

这里的注意点是:注意下载文件里文标题乱码问题

至此,以上整体的代码大概是这样。须要的人,能够多看看,假设有什么问题,欢迎随时私信、留言等交流。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/115897.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • linux .gz文件 解压缩命令的简单使用

    linux .gz文件 解压缩命令的简单使用压缩压缩文件语法gzip源文件1如压缩b.txt使用命令gzipb.txt即可注意压缩为.gz文件源文件会消失如果想保留源文件使用命令gzip-c源文件&gt;压缩文件1如压缩b.txt且保留b.txt使用命令gzip-cb.txt&gt;b.txt.gz压缩目录语法gzip-r目录1…

  • Nginx工作原理和优化总结。「建议收藏」

    NGINX以高性能的负载均衡器,缓存,和web服务器闻名,驱动了全球超过40%最繁忙的网站。在大多数场景下,默认的NGINX和Linux设置可以很好的工作,但要达到最佳性能,有些时候必须做些调整。首先我们先了解其工作原理。1.Nginx的模块与工作原理Nginx由内核和模块组成,其中,内核的设计非常微小和简洁,完成的工作也非常简单,仅仅通过查找配置文件将客…

  • mybatis-plus自动生成代码的调用用法(mybatisplus批量新增)

    一、介绍本教程将介绍如何使用mybatis-plus工具自动给我们生成Controller、Service、Entity、Mapper、Mapper.xml层代码要求:①生成的Controller类,需要继承BaseController②生成的Entity类,需要继承BaseEntity③生成的Service,默认名称下是以I开头的接口,在生成Se…

  • mybatiscodehelperpro在线激活码【永久激活】

    (mybatiscodehelperpro在线激活码)JetBrains旗下有多款编译器工具(如:IntelliJ、WebStorm、PyCharm等)在各编程领域几乎都占据了垄断地位。建立在开源IntelliJ平台之上,过去15年以来,JetBrains一直在不断发展和完善这个平台。这个平台可以针对您的开发工作流进行微调并且能够提供…

  • Typora下载和简单使用教程

    Typora下载和简单使用教程Typora下载Typora官网下载地址根据自己的电脑选择不同的版本,我用的是Windows64位的系统的就选择DownloadBeta(×64)下载完Typora以后我们就可以使用markdown了。Typora的学习1.标题数量 标题级数 快捷键 一个#加空格加内容 一级标题 Ctrl+1 两个#加空格加内容 二级标题 Ctrl+2 三个#加空格加内容 三级标题 Ctrl+3 以此类..

  • rabbitmq异步处理_怎么解决js异步方法执行顺序

    rabbitmq异步处理_怎么解决js异步方法执行顺序RabbitMQ即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。使用RabbitMQ实现异步更新文章浏览量,提升阅读文章时的响应速度。从直接更新数据库耗时450ms到异步更新数据库耗时50ms,明显提升接口性能,非常的nice~………

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号