zoukankan      html  css  js  c++  java
  • 利用ItextPdf、core-renderer-R8 来生成PDF

    最近由于工作上的需要,需要做一个简历产品的下载功能,而下载的形式要去为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模板代码:
    <#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;
            }
        	//工程路径
            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));
    
                //定义输入文件,默认生成在工程根目录
                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;
        //工程路径
        /*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;
            }
        }
    这里的注意点是:注意下载文件中文标题乱码问题

    至此,以上总体的代码大概是这样,需要的人,可以多看看,如果有什么问题,欢迎随时私信、留言等交流。
  • 相关阅读:
    Python学习【第五篇】:面向对象及相关
    Python之路【第四篇】:模块
    Python之路【第三篇】:python基础(二)
    Python之路【第二篇】:Python基础(一)
    Python之路【第一篇】:Python简介和入门
    Open-source Tutorial
    Algorithms
    Mathematics Base
    Mathematics Base
    Open-source Tutorial
  • 原文地址:https://www.cnblogs.com/jessonlv/p/4387980.html
Copyright © 2011-2022 走看看