zoukankan      html  css  js  c++  java
  • 利用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 {
                 900px;
                margin: 0 auto;
            }
    
            .block {
                margin-top: 20px;//+2
            }
    
            .align-center {
                text-align: center;
                margin-left: 252px;
                 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;
                 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;
                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;
    		   520px;
    		}
            .paragraph {
                line-height: 16px;
                font-size: 11px;
                margin-bottom: 10px;
                 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;
            }
        }
    这里的注意点是:注意下载文件里文标题乱码问题

    至此,以上整体的代码大概是这样。须要的人,能够多看看,假设有什么问题,欢迎随时私信、留言等交流。
  • 相关阅读:
    Java 泛型 泛型的约束与局限性
    Java 泛型 泛型方法
    Java 泛型 泛型数组
    Java 泛型 协变性、逆变性
    Java 泛型 协变式覆盖和泛型重载
    Java 泛型 泛型代码和虚拟机
    Insertion Sort List
    Remove Duplicates from Sorted List II
    String to Integer (atoi)
    SpringMvc源码入门
  • 原文地址:https://www.cnblogs.com/yxwkf/p/5218385.html
Copyright © 2011-2022 走看看