您的位置:首页 > 编程语言 > PHP开发

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

2017-09-19 22:28 429 查看
来源:http://blog.csdn.net/jessonlv/article/details/36657093。

目录(?)[+]

最近由于工作上的需要,需要做一个简历产品的下载功能,而下载的形式要去为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

<#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页面的代码:

[java] view
plain copy

@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的代码

[java] view
plain copy

//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以后,就是推送到浏览器的问题:

[java] view
plain copy

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;

}

}

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

至此,以上总体的代码大概是这样,需要的人,可以多看看,如果有什么问题,欢迎随时私信、留言等交流。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: