前提:
在项目需求中会涉及到生成的PDF附件,刚开始使用的iText java类库生成pdf,但面对样式变化较多的情况下itext就显得有点复杂,作者在权衡之后选择使用html模板啦生成样式多变的PDF格式。
- 作者选择的工具自己步骤为:
1,使用freemarker的生成HTML模板。
- FreeMarker的是一个模板生成引擎,可以理解为前后端完全分离的JSP,通过页面的表达式和后台设定的值生成动态的模板,这里用它来生成HTML文件。
- Freemarker的模板:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>用户信息</title>
<!-- 新 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet"
href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css" />
</head>
<body style="font-family:'Courier New'">
<#--这里是一段注释,这里是单个属性的展示-->
<h3 class="text-center">这是用户${username}的信息页!</h3>
<#--这里是一段注释,下面是转义字符的应用-->
<a>${"这是一段字符串,里面有转义字符\"双引号\""}</a></br>
<#--这里是一段注释,下面是各种特殊字符原样输出-->
<a>${r"这是一段字符,带有各种>转义字符${foo}fsf"}</a></br>
<#--自定义变量-->
<#assign x=11>
<a>x=${x}</a></br>
<a>x/2=${x/2}</a></br>
<a>x/2=${(x/2)?int}(转为整形)</a></br>
<a>x+“3”=${x+"3"}</a></br>
<#--这里是一段注释,下面是一个表格,数据来源list,包含时间格式的显示-->
<div class="col-md-6 column">
<table class="table table-bordered">
<tr>
<th>has_next:下标:姓名</th>
<th>学生的密码</th>
<th>学生的年龄</th>
<th>学生的地址</th>
<th>学生的生日</th>
</tr>
<#list userList as user>
<tr>
<td>${userList?is_hash?string("true","false")}:${user_index}:${user.username}</td>
<td>${user.password}</td>
<td>${user.age}</td>
<td>${user.address}</td>
<#if user_index==1>
<td>date:${user.birthday?date}</td>
<#elseif user_index==2>
<td>datetime:${user.birthday?datetime}</td>
<#elseif user_index==3>
<td>time:${user.birthday?time}</td>
<#else>
<td>time:${user.birthday?time}</td>
</#if>
</tr>
</#list>
</table>
</div>
</body>
</html>
- FreeMarker的生成HTML文件:调用geneFileStr方法
package com.netsmart.util;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
public class FreeMarkerToHtmlUtil {
private static FreeMarkerToHtmlUtil instance;
private Configuration config;
// String templatePath = "/freeMarker/";
String templatePath = ReadProperties.getValue("template.properties","templatePath");
/**
* instance FreeMarkerUtil
* @return
*/
public static FreeMarkerToHtmlUtil instance() {
if (instance == null) {
instance = new FreeMarkerToHtmlUtil();
}
return instance;
}
/**
* instance Configuration
* @param request
*/
private void configInstance(HttpServletRequest request) {
if (this.config == null) {
this.config = new Configuration();
this.config.setServletContextForTemplateLoading(request.getSession().getServletContext(),templatePath);
}
}
/**
* 利用freemarker模板生成html文件
* @param request
* @param templateFileName 模板文件
* @param propMap 模板中所需数据
* @param htmlPath 存储的html文件名
* @return 生成html文件的绝对路径
*/
public String geneFileStr(HttpServletRequest request,String templateFileName, String htmlPath,Map<String, Object> propMap) {
configInstance(request);
StringWriter out = new StringWriter();
Template tmp;
htmlPath =ReadProperties.getValue("template.properties","htmlLocation")+"/"+htmlPath;
File htmlFile = new File(htmlPath);
try {
htmlPath = htmlFile.getCanonicalPath();
// 定义输出流,注意必须指定编码
Writer writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(htmlFile), "UTF-8"));
// 生成模版
tmp = this.config.getTemplate(templateFileName,"UTF-8");
tmp.setEncoding("UTF-8");
tmp.process(propMap, writer);
} catch (IOException e) {
e.printStackTrace();
} catch (TemplateException e) {
e.printStackTrace();
}
return htmlPath;
}
}
- 然后就可以生成HTML文件,下图是文件效果图:
2,将生成好的HTML文件利用wkhtmltopdf工具生成PDF文件,这里可以利用的java执行DOS命令来生成。
- wkhtmltopdf是一款需要下载安装的用来生成PDF的软件,可以在https://wkhtmltopdf.org/选择合适版本下载安装。
- 安装完成之后,使用dos命令生成pdf:
- 第一个参数为wkhtmltopdf安装目录+“/bin/wkhtmltopdf”,第二个参数为html网址(或本地html路径),第三个为生成pdf全路径,下图为生成效果:
- 可以看出页面的样式也被很好的保留下来。上一步我们已经使用freeMarker引擎生成了html文件,只需要将dos命令中html的参数修改为生成html的路径,就可以生成一份格式良好的pdf了!!!
- 这里我们使用java调用dos命令,下面是调用dos命令的代码:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
public class OperationDOS {
public static int createPdf(String htmlPath,String pdfName){
//wkhtmltopdf的安装路径+"/bin"
String appPath = ReadProperties.getValue("template.properties", "appPath");
//生成的pdf存储路径,可自己设置
String pdfPath = ReadProperties.getValue("template.properties", "pdfPath");
String dosCommand = appPath + "wkhtmltopdf "+htmlPath+ " " + pdfPath + pdfName;
int exitVal = 0;
try {
Process pr = Runtime.getRuntime().exec(dosCommand);
BufferedReader input = new BufferedReader(new InputStreamReader(pr.getInputStream(), Charset.forName("GBK")));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = input.readLine()) != null) {
sb.append(line + "\n");
}
System.out.println(sb.toString());
exitVal = pr.waitFor();
System.out.println("Exited with error code " + exitVal);
} catch (Exception e) {
System.out.println(e.toString());
e.printStackTrace();
}
return exitVal;
}
public static void main(String[] args) {
String dosCommand = "ping www.baidu.com";
int exitVal = 0;
try {
Process pr = Runtime.getRuntime().exec(dosCommand);
BufferedReader input = new BufferedReader(new InputStreamReader(pr.getInputStream(), Charset.forName("GBK")));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = input.readLine()) != null) {
sb.append(line + "\n");
}
System.out.println(sb.toString());
exitVal = pr.waitFor();
System.out.println("Exited with error code " + exitVal);
} catch (Exception e) {
System.out.println(e.toString());
e.printStackTrace();
}
}
}
这样,一份pdf就可以完美实现了,笔者水平有限,如有错误还望大家多多指教!!