示例程序通过两种方式实现了FreeMarker+flying-saucer-pdf的PDF预览、分页需求
<dependency>
<groupId>org.xhtmlrenderergroupId>
<artifactId>flying-saucer-pdfartifactId>
<version>9.1.22version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<html>
<head>
<style>
span{
border-bottom: 1px solid black;
}
p{
line-height: 1.5;
}
@page {
size: 210mm 297mm; /*设置纸张大小:A4(210mm 297mm)、A3(297mm 420mm) 横向则反过来*/
margin-bottom: 1cm;
padding: 1em;
@top-center {
content: "页眉中间位置";
font-family: SimSun;
font-size: 15px;
color:#000;
};
@bottom-center{
content: "页脚中间位置";
font-family: SimSun;
font-size: 15px;
color:#000;
};
@bottom-right{
content: "第" counter(page) "页 共" counter(pages) "页";
font-family: SimSun;
font-size: 15px;
color:#000;
};
}
#pagenumber:before {
content: counter(page);
}
#pagecount:before {
content: counter(pages);
}
style>
head>
<body style="font-family: SimSun; ">
<p>
<h1 style="text-align: center;">公司授权委托书h1>
p>
<br /><br />
<p>致:<span>${tenEntName!}span>p>
<p> 我单位现委托<span>${userName!}span>作为我单位合法委托代理人,授权其代表我单位进行<span>${indexTitle!}span>账户相关管理工作。该委托代理人的授权范围为:代表我单位在<span>${indexTitle!}span>上注册、签署文件、使用<span>${indexTitle!}span>、资金交易、融资等与<span>${indexTitle!}span>有关的一切事务。在整个<span>${indexTitle!}span>使用过程中,该代理人的一切行为,均代表本单位,与本单位的行为具有同等法律效力。本单位将承担该代理人行为带来的全部法律后果和法律责任。p>
<p> 以上授权委托有效期自盖章之日(含当日)起至<span>${indexTitle!}span>账户注销和/或<span>${indexTitle!}span>全部融资款项结算完毕终止。p>
<p> 代理人无权转换代理权。p>
<p> 特此委托。p>
<p> 代理人姓名:<span>${userName!}span>p>
<p> 身份证号码:<span>${userNum!}span>p>
<br /><br />
<p> 委托人(盖章):p>
<p> 日期:<span>${year!}span>年<span>${month!}span>月<span>${day!}span>日p>
<br /><br /> <br /><br /> <br /><br /> <br /><br /> <br /><br /> <br /><br />
<br /><br /> <br /><br /> <br /><br />
<p>附:委托代理人身份证复印件(<span>正反面、span>加盖公章)p>
<div id = "header">div>
body>
html>
注意事项:
- 在body标签中指定模板文件字体。本文是宋体:font-family: SimSun;
- FreeMarker文件对Html标签要求很严格,尽量使用正确的标签。
- 模板文件位置应用程序资源目录下:/resources/templates/freemarkers
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Locale;
import java.util.Map;
public class FreemarkerUtil {
/**
* 从模板文件中加载
* @param templateFileName
* @param data
* @return
* @throws IOException
* @throws TemplateException
*/
public static String loadTemplateFromFile(String templateFileName, Map<String, String> data) throws IOException, TemplateException {
// 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
// 指定FreeMarker模板文件的位置
cfg.setClassForTemplateLoading(FreemarkerUtil.class,"/templates/freemarkers");
// 设置模板的编码格式
cfg.setEncoding(Locale.CHINA, "UTF-8");
// 获取模板文件
Template template = cfg.getTemplate(templateFileName, "UTF-8");
StringWriter writer = new StringWriter();
// 将数据输出到html中
template.process(data, writer);
writer.flush();
String html = writer.toString();
return html;
}
/**
* 从存储单元中中加载
* @param templateFileContent
* @param data
* @return
* @throws IOException
* @throws TemplateException
*/
public static String loadTemplateFromStorage(String templateFileContent, Map<String, String> data) throws IOException, TemplateException {
String templateName = "自定义模板名称";
StringWriter stringWriter = new StringWriter();
StringTemplateLoader loader = new StringTemplateLoader();
loader.putTemplate(templateName, templateFileContent);
Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
cfg.setTemplateLoader(loader);
cfg.setDefaultEncoding("UTF-8");
Template template = cfg.getTemplate(templateName);
template.process(data, stringWriter);
String html = stringWriter.toString();
return html;
}
}
import com.lowagie.text.pdf.BaseFont;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
public class PDFUtil {
public static ByteArrayOutputStream createPDFFromHtml(String html) throws Exception {
ITextRenderer renderer = new ITextRenderer();
OutputStream out = new ByteArrayOutputStream();
// 设置 css中 的字体样式(暂时仅支持宋体和黑体) 必须,不然中文不显示
renderer.getFontResolver().addFont("/font/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
// 把html代码传入渲染器中
renderer.setDocumentFromString(html);
// // 设置模板中的图片路径 (这里的images在resources目录下) 模板中img标签src路径需要相对路径加图片名 如
// String url = PDFTemplateUtil.class.getClassLoader().getResource("images").toURI().toString();
// renderer.getSharedContext().setBaseURL(url);
renderer.layout();
renderer.createPDF(out, false);
renderer.finishPDF();
out.flush();
ByteArrayOutputStream byteArrayOutputStream = (ByteArrayOutputStream) out;
return byteArrayOutputStream;
}
}
import cn.tyrone.springboot.freemarker.util.FreemarkerUtil;
import cn.tyrone.springboot.freemarker.util.PDFUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/freemarker/pdf")
public class Freemarker2PDFController {
/**
* 从模板文件中加载
* @param response
* @throws Exception
*/
@GetMapping("/template/file")
public void loadFromTemplate(HttpServletResponse response) throws Exception {
String templateFileName = "demo.ftl";
Map<String, String> data = new HashMap<>();
data.put("userName", "孙悟空");
data.put("userNum", "110101199003074071");
data.put("year", "2022");
data.put("month", "01");
data.put("day", "24");
data.put("siteName", "帅链平台");
data.put("ticketTitle", "帅票");
data.put("tenEntName", "郑州市公共交通集团有限公司");
data.put("indexTitle", "帅链科技");
String html = FreemarkerUtil.loadTemplateFromFile(templateFileName, data);
ByteArrayOutputStream baos = PDFUtil.createPDFFromHtml(html);
response.setContentType("application/pdf");
OutputStream out = response.getOutputStream();
baos.writeTo(out);
baos.close();
}
/**
* 从存储单元中加载
* @param response
* @throws Exception
*/
@GetMapping("/template/storage")
public void loadFromStorage(HttpServletResponse response) throws Exception {
Map<String, String> data = new HashMap<>();
data.put("userName", "孙悟空");
data.put("userNum", "110101199003074071");
data.put("year", "2022");
data.put("month", "01");
data.put("day", "24");
data.put("siteName", "帅链平台");
data.put("ticketTitle", "帅票");
data.put("tenEntName", "郑州市公共交通集团有限公司");
data.put("indexTitle", "帅链科技");
String templateContent = "公司授权委托书
致:${tenEntName!}
我单位现委托${userName!}作为我单位合法委托代理人,授权其代表我单位进行${indexTitle!}账户相关管理工作。该委托代理人的授权范围为:代表我单位在${indexTitle!}上注册、签署文件、使用${indexTitle!}、资金交易、融资等与${indexTitle!}有关的一切事务。在整个${indexTitle!}使用过程中,该代理人的一切行为,均代表本单位,与本单位的行为具有同等法律效力。本单位将承担该代理人行为带来的全部法律后果和法律责任。
以上授权委托有效期自盖章之日(含当日)起至${indexTitle!}账户注销和/或${indexTitle!}全部融资款项结算完毕终止。
代理人无权转换代理权。
特此委托。
代理人姓名:${userName!}
身份证号码:${userNum!}
委托人(盖章):
日期:${year!}年${month!}月${day!}日
附:委托代理人身份证复印件(正反面、加盖公章)
";
String html = FreemarkerUtil.loadTemplateFromStorage(templateContent, data);
ByteArrayOutputStream baos = PDFUtil.createPDFFromHtml(html);
response.setContentType("application/pdf");
OutputStream out = response.getOutputStream();
baos.writeTo(out);
baos.close();
}
}
flying-saucer-pdf是通过css样式实现分页的,如下:
<style>
span{
border-bottom: 1px solid black;
}
p{
line-height: 1.5;
}
@page {
size: 210mm 297mm; /*设置纸张大小:A4(210mm 297mm)、A3(297mm 420mm) 横向则反过来*/
margin-bottom: 1cm;
padding: 1em;
@top-center {
content: "页眉中间位置";
font-family: SimSun;
font-size: 15px;
color:#000;
};
@bottom-center{
content: "页脚中间位置";
font-family: SimSun;
font-size: 15px;
color:#000;
};
@bottom-right{
content: "第" counter(page) "页 共" counter(pages) "页";
font-family: SimSun;
font-size: 15px;
color:#000;
};
}
#pagenumber:before {
content: counter(page);
}
#pagecount:before {
content: counter(pages);
}
style>
https://www.iteye.com/blog/wuxiangxin-1550349
https://github.com/myNameIssls/springboot-study/tree/master/springboot-freemarker