项目需要实现自定义pdf模板,上网找了些资料实现了这个功能
参考文档:
https://my.oschina.net/960823/blog/1588166
https://my.oschina.net/u/3557716/blog/3023788/print
maven依赖:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.9</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-itext5</artifactId>
<version>9.0.3</version>
</dependency>
PdfUtil.java
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import com.alibaba.fastjson.JSONObject;
import com.itextpdf.text.pdf.BaseFont;
/***
* pdf工具类
*/
public class PdfUtil {
/**
* 将HTML转成PD格式的文件。html文件的格式比较严格
* @param body body内容
* @param os 输出流
* @throws Exception
*/
public static void convertHtmlToPdf(String body, OutputStream os) throws Exception {
body = filterHtml(body);
ITextRenderer renderer = new ITextRenderer();
String content = "<!DOCTYPE html [ <!ENTITY nbsp \" \">] >" + "<html>" + " <head>"
+ " <style type=\"text/css\">" + " body {" + " font-family: SimSun;"
+ " background: none;" + " margin-left: auto;"
+ " margin-right: auto;" + " }" + " p {"
+ " margin: 5px 0;" + " }" + " </style>" + " </head>" + " <body>" + body
+ " </body>" + "</html>" + "";
renderer.setDocumentFromString(content);
// step 3 解决中文支持
ITextFontResolver fontResolver = renderer.getFontResolver();
if ("linux".equals(getCurrentOperatingSystem())) {
fontResolver.addFont("/usr/share/fonts/chiness/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
} else {
fontResolver.addFont("c:/Windows/Fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
}
renderer.layout();
renderer.createPDF(os);
os.flush();
os.close();
System.out.println("create pdf done!!");
}
/***
* 获取系统环境
* @return
*/
public static String getCurrentOperatingSystem() {
String os = System.getProperty("os.name").toLowerCase();
System.out.println("---------当前操作系统是-----------" + os);
return os;
}
/***
* 过滤html中的不规则文本
* @param content
*/
private static String filterHtml(String content) {
return content.replace("<br>", "<br/>");
}
/***
* 将html转成pdf 并输出到outputstream中
* @param content body正文
* @param valueMap 占位符解析的值map
* @param os 输出流
* @throws Exception
*/
public static void convertHtmlToPdfOutStream(String content, Map<String, Object> valueMap, OutputStream os)
throws Exception {
content = PlaceholderResolver.resolveByMap(content, valueMap);
convertHtmlToPdfOutStream(content, os);
}
public static void convertHtmlToPdfOutStream(String content, OutputStream os) throws Exception {
PdfUtil.convertHtmlToPdf(content, os);
}
/***
* 下载文件-测试方法
* @param os response的OutputStream
* @throws Exception
*/
public static void test(OutputStream os) throws Exception {
String content = "<p style=\"text-align: center;\"><span style=\"font-size: 24px;\"><strong>测试模板</strong></span></p>\r\n" +
" <p><span style=\"font-size: 14px;\">尊敬的<span style=\"font-size: 14px; color: rgb(255, 0, 0);\">${userName}</span>:</span></p>\r\n" +
" <p style=\"text-indent: 2em;\"><span style=\"font-size: 14px;\">恭喜你中了100元大奖</span></p>\r\n" +
" <p style=\"text-align: right; text-indent: 0em;\"><span style=\"font-size: 14px;\">${date}</span></p>\r\n" +
" <p>​<br></p>";
JSONObject map = new JSONObject();
map.put("userName", "testAdmin");
map.put("date", new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss").format(new Date()));
content = PlaceholderResolver.resolveByMap(content, map);
convertHtmlToPdfOutStream(content, os);
}
/***
* 生成本地pdf文件
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
String pdfFile = "d:/log/testoone2.pdf";
String content = "<body style=\"cursor: text;\">\r\n"
+ " <p style=\"text-align: center;\"><span style=\"font-size: 24px;\">测试模板</span></p>\r\n"
+ " <p><span style=\"font-size: 14px;\">尊敬的<span style=\"font-size: 14px; color: rgb(255, 0, 0);\">${userName}</span>:</span></p>\r\n"
+ " <p><span style=\"font-size: 14px;\"> ​恭喜你中了100元大奖<br></span></p>\r\n"
+ " <p><span style=\"font-size: 14px;\"><br></span></p>\r\n"
+ " <p style=\"text-align: right;\"><span style=\"font-size: 14px;\">​2019年8月16日 ​</span></p>\r\n"
+ " </body>";
JSONObject map = new JSONObject();
map.put("userName", "testAdmin");
content = PlaceholderResolver.resolveByMap(content, map);
OutputStream os = new FileOutputStream(pdfFile);
convertHtmlToPdfOutStream(content, os);
}
}
PlaceholderResolver.java
import java.util.Map;
import java.util.Properties;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* 占位符解析器
* @author meilin.huang
* @version 1.0
*/
public class PlaceholderResolver {
/**
* 默认前缀占位符
*/
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
/**
* 默认后缀占位符
*/
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
/**
* 占位符前缀
*/
private static String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
/**
* 占位符后缀
*/
private static String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;
/**
* 解析带有指定占位符的模板字符串,默认占位符为前缀:${ 后缀:}<br/><br/>
* 如:template = category:${}:product:${}<br/>
* values = {"1", "2"}<br/>
* 返回 category:1:product:2<br/>
* @param content 要解析的带有占位符的模板字符串
* @param values 按照模板占位符索引位置设置对应的值
* @return
*/
public static String resolve(String content, String... values) {
int start = content.indexOf(placeholderPrefix);
if (start == -1) {
return content;
}
//值索引
int valueIndex = 0;
StringBuilder result = new StringBuilder(content);
while (start != -1) {
int end = result.indexOf(placeholderSuffix);
String replaceContent = values[valueIndex++];
result.replace(start, end + placeholderSuffix.length(), replaceContent);
start = result.indexOf(placeholderPrefix, start + replaceContent.length());
}
return result.toString();
}
/**
* 解析带有指定占位符的模板字符串,默认占位符为前缀:${ 后缀:}<br/><br/>
* 如:template = category:${}:product:${}<br/>
* values = {"1", "2"}<br/>
* 返回 category:1:product:2<br/>
*
* @param content 要解析的带有占位符的模板字符串
* @param values 按照模板占位符索引位置设置对应的值
* @return
*/
public static String resolve(String content, Object[] values) {
return resolve(content, Stream.of(values).map(String::valueOf).toArray(String[]::new));
}
/**
* 根据替换规则来替换指定模板中的占位符值
* @param content 要解析的字符串
* @param rule 解析规则回调
* @return
*/
public static String resolveByRule(String content, Function<String, String> rule) {
int start = content.indexOf(placeholderPrefix);
if (start == -1) {
return content;
}
StringBuilder result = new StringBuilder(content);
while (start != -1) {
int end = result.indexOf(placeholderSuffix, start);
//获取占位符属性值,如${id}, 即获取id
String placeholder = result.substring(start + placeholderPrefix.length(), end);
//替换整个占位符内容,即将${id}值替换为替换规则回调中的内容
String replaceContent = placeholder.trim().isEmpty() ? placeholder : rule.apply(placeholder);
result.replace(start, end + placeholderSuffix.length(), replaceContent);
start = result.indexOf(placeholderPrefix, start + replaceContent.length());
}
return result.toString();
}
/**
* 替换模板中占位符内容,占位符的内容即为map key对应的值,key为占位符中的内容。<br/><br/>
* 如:content = product:${id}:detail:${did}<br/>
* valueMap = id -> 1; pid -> 2<br/>
* 经过解析返回 product:1:detail:2<br/>
*
* @param content 模板内容。
* @param valueMap 值map
* @return 替换完成后的字符串。
*/
public static String resolveByMap(String content, final Map<String, Object> valueMap) {
return resolveByRule(content, placeholderValue -> String.valueOf(valueMap.get(placeholderValue)));
}
/**
* 根据properties文件替换占位符内容
* @param content
* @param properties
* @return
*/
public static String resolveByProperties(String content, final Properties properties) {
return resolveByRule(content, placeholderValue -> properties.getProperty(placeholderValue));
}
}
TestController.java 测试下载controller
import java.io.OutputStream;
import java.net.URLEncoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/testHtmlToPdf")
public class TestController {
@GetMapping("/getPdf")
public void getPdf(HttpServletRequest request, HttpServletResponse response) throws Exception {
String fileName = URLEncoder.encode("模板.pdf", "UTF-8");
// 设置response的Header
response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
OutputStream os = response.getOutputStream();
PdfUtil.test(os);
return;
}
}