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;
            }
        }
    这里的注意点是:注意下载文件中文标题乱码问题

    至此,以上总体的代码大概是这样,需要的人,可以多看看,如果有什么问题,欢迎随时私信、留言等交流。
  • 相关阅读:
    React元素渲染
    初识JSX
    微信小程序复制文本到剪切板
    微信小程序报错request:fail url not in domain list
    小程序,通过自定义编译条件,模拟推荐人功能
    积分抵扣逻辑
    微信小程序 switch 样式
    tomcat 配置开启 APR 模式
    tomcat8 传输json 报错 Invalid character found in the request target. The valid characters are defined in RFC 3986
    c++数组初始化误区
  • 原文地址:https://www.cnblogs.com/jessonlv/p/4387980.html
Copyright © 2011-2022 走看看