在pom.xml文件中添加以下依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
新建一个HTML文件,例如table.ftl,然后在其中编写HTML模板,包括表格的头部、内容和尾部等部分。具体可以参考下面这个例子:
<table border="1">
<thead>
<tr>
<th>IDth>
<th>Nameth>
<th>Ageth>
tr>
thead>
<tbody>
<#list users as user>
<tr>
<td>${user.id}td>
<td>${user.name}td>
<td>${user.age}td>
tr>
#list>
tbody>
table>
上面的模板中使用了Freemarker的指令语法来实现动态生成表格内容,包括使用<#list>
标签来遍历用户列表,并使用${}
语法来输出用户信息。
在Controller中编写一个方法来获取用户列表,然后渲染上面的模板并返回HTML内容。示例代码如下:
@Controller
public class UserController {
@GetMapping("/users")
public String userList(Model model) {
List<User> users = new ArrayList<>();
users.add(new User(1, "Tom", 18));
users.add(new User(2, "Jerry", 20));
users.add(new User(3, "John", 22));
model.addAttribute("users", users);
return "table";
}
}
上面的方法使用@GetMapping
注解来处理请求,然后创建一个用户列表,并将其添加到模型中。最后返回table
字符串,代表要使用的HTML模板文件。
运行Spring Boot应用程序,然后使用浏览器访问http://localhost:8080/users
,即可看到动态生成的HTML表格。
注意:上面的例子仅供参考,实际应用中需要根据自己的需求进行修改扩展。
理解了freemarker生成html的步骤以后,就可以利用iText把html生成pdf文件了。
<dependency>
<groupId>com.itextpdf.toolgroupId>
<artifactId>xmlworkerartifactId>
<version>5.5.1version>
dependency>
<dependency>
<groupId>com.itextpdfgroupId>
<artifactId>itext-asianartifactId>
<version>5.2.0version>
dependency>
<dependency>
<groupId>org.xhtmlrenderergroupId>
<artifactId>flying-saucer-pdf-itext5artifactId>
<version>9.0.9version>
dependency>
freeMarker转换为html的方法:
public class HtmlGenerator {
public static String generate(String template, Map<String, Object> variables) throws IOException, TemplateException, IOException {
Configuration config = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
// 指定FreeMarker模板文件的位置
config.setClassForTemplateLoading(HtmlGenerator.class, "/filePath");
//读取模板文件地址
config.setDefaultEncoding("UTF-8");
//获取模板文件
Template tp = config.getTemplate(template);
StringWriter stringWriter = new StringWriter();
BufferedWriter writer = new BufferedWriter(stringWriter);
tp.setEncoding("UTF-8");
//把map数据写入
tp.process(variables, writer);
String htmlStr = stringWriter.toString();
writer.flush();
writer.close();
return htmlStr;
}
}
/filePath为项目中的ftl文件相对路径。
html生成pdf的方法:
public class PdfDocumentGenerator {
private static final Logger logger = LoggerFactory.getLogger(PdfDocumentGenerator.class);
/**
* Output a pdf to the specified outputstream
*
* @param htmlStr
* the htmlstr
* @param out
* the specified outputstream
* @throws Exception
*/
public static void generate(String htmlStr, OutputStream out)
throws Exception {
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // compliant
DocumentBuilder builder = df.newDocumentBuilder();
org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(htmlStr
.getBytes()));
ITextRenderer renderer = new ITextRenderer();
renderer.setDocument(doc, null);
renderer.layout();
renderer.createPDF(out);
out.close();
}
public static void generatePlus(String htmlStr, OutputStream out) throws IOException, DocumentException {
final String charsetName = "UTF-8";
Document document = new Document(PageSize.A4, 30, 30, 30, 30);
document.setMargins(30, 30, 30, 30);
PdfWriter writer = PdfWriter.getInstance(document, out);
document.open();
// html内容解析
HtmlPipelineContext htmlContext = new HtmlPipelineContext(
new CssAppliersImpl(new XMLWorkerFontProvider() {
@Override
public Font getFont(String fontname, String encoding,
float size, final int style) {
if (fontname == null) {
fontname = getChineseFont();
}
return super.getFont(fontname, encoding, size,
style);
}
})) {
@Override
public HtmlPipelineContext clone()
throws CloneNotSupportedException {
HtmlPipelineContext context = super.clone();
try {
ImageProvider imageProvider = this.getImageProvider();
context.setImageProvider(imageProvider);
} catch (NoImageProviderException e) {
}
return context;
}
};
// 图片解析
htmlContext.setImageProvider(new AbstractImageProvider() {
String rootPath = PdfDocumentGenerator.class.getResource("/").getPath();
@Override
public String getImageRootPath() {
return rootPath;
}
@Override
public Image retrieve(String src) {
if (StringUtils.isEmpty(src)) {
return null;
}
try {
Image image = Image.getInstance(new File(rootPath, src).toURI().toString());
// 图片显示位置
image.setAbsolutePosition(400, 400);
if (image != null) {
store(src, image);
return image;
}
} catch (Throwable e) {
e.printStackTrace();
}
return super.retrieve(src);
}
});
htmlContext.setAcceptUnknown(true).autoBookmark(true).setTagFactory(Tags.getHtmlTagProcessorFactory());
// css解析
CSSResolver cssResolver = XMLWorkerHelper.getInstance().getDefaultCssResolver(true);
cssResolver.setFileRetrieve(new FileRetrieve() {
@Override
public void processFromStream(InputStream in,
ReadingProcessor processor) throws IOException {
try (
InputStreamReader reader = new InputStreamReader(in, charsetName)) {
int i = -1;
while (-1 != (i = reader.read())) {
processor.process(i);
}
} catch (Throwable e) {
}
}
// 解析href
@Override
public void processFromHref(String href, ReadingProcessor processor) throws IOException {
InputStream is = PdfDocumentGenerator.class.getResourceAsStream("/" + href);
try (InputStreamReader reader = new InputStreamReader(is,charsetName)) {
int i = -1;
while (-1 != (i = reader.read())) {
processor.process(i);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
});
HtmlPipeline htmlPipeline = new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer));
Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, htmlPipeline);
XMLWorker worker = null;
worker = new XMLWorker(pipeline, true);
XMLParser parser = new XMLParser(true, worker, Charset.forName(charsetName));
try (InputStream inputStream = new ByteArrayInputStream(htmlStr.getBytes())) {
parser.parse(inputStream, Charset.forName(charsetName));
}
document.close();
}
/**
* 获取中文字体位置
* @return
*/
public static String getChineseFont() {
String chineseFont = null;
chineseFont = Object.class.getResource("/").getPath() + "font/simsun.ttc";
if(!new File(chineseFont).exists()){
throw new RuntimeException("字体文件不存在!"+chineseFont);
}
return chineseFont;
}
}
运行测试方法:
public class Pdfdest {
public static void main(String[] args) throws Exception {
String outputFile = "d:/test1.pdf";
Map<String, Object> map = new HashMap<>();
map.put("XXX", "测试");
//生成工具,下面有代码
String htmlStr = HtmlGenerator.generate("test.ftl", map);
//生成工具,下面有代码
OutputStream out = new FileOutputStream(outputFile);
PdfDocumentGenerator.generatePlus(htmlStr,out);
}
}
准备一个test.ftl放到resource/filePath下,当然字体最好也放到resource/font下,运行时需要使用。
test.ftl的代码如下:
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">meta>
<style>
@page {
@top-center { content: element(header) }
}
@page {
@bottom-center { content: element(footer) }
}
.apply {
margin: 0 auto;
padding: 0 30px;
}
.title {
margin-top: 40px ;
text-align: center;
font-weight: bold;
//字体需要和后台对应上
font-family: SimSun;
font-weight: bold;
font-size: 20px;
color: #333333;
letter-spacing: 0;
}
.table {
border-collapse: collapse;
width: 100%;
margin-top: 30px;
font-family: SimSun;
font-size: 14px;
color: #111111;
letter-spacing: 0.54px;
}
.label {
background-color: #E6E6E6;
width: 20%;
}
.normaltd {
padding: 10px 0;
}
.maxtd {
height: 250px;
}
.value {
width: 30%;
padding-left: 10px;
}
.apply {
margin: 0 auto;
padding: 0 30px;
}
.title {
margin-top: 40px ;
text-align: center;
font-weight: bold;
//字体需要和后台对应上
font-family: SimSun;
font-weight: bold;
font-size: 20px;
color: #333333;
letter-spacing: 0;
}
.table {
width: 100%;
margin-top: 30px;
font-family: SimSun;
font-size: 14px;
color: #111111;
letter-spacing: 0.54px;
}
.label {
background-color: #E6E6E6;
width: 20%;
}
.normaltd {
padding: 10px 0;
}
.maxtd {
height: 250px;
}
.value {
width: 30%;
padding-left: 10px;
}
tr {
page-break-inside: avoid;
page-break-after: auto;
}
style>
head>
<body style="font-family: SimSun">
<div class="apply">
<p class="title">申请单p>
<table border="1" cellspacing="0" class="table">
<tr>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
<tr>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
<tr>
<td class="label" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
<tr>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">XXXtd>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
<tr>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
<tr >
<td valign="middle" colspan="1" class="label maxtd" align="center">XXXtd>
<td valign="middle" colspan="3" class="maxtd value">${XXX}td>
tr>
<tr>
<td colspan="1" class="label normaltd" align="center">XXXtd>
<td colspan="3" class="normaltd value">${XXX}td>
tr>
<tr>
<td colspan="1" class="label normaltd" align="center">XXXtd>
<td colspan="3" class="normaltd value">${XXX}td>
tr>
<tr>
<td colspan="1" class="label normaltd" align="center">XXXtd>
<td colspan="3" class="normaltd value">${XXX}td>
tr>
<tr>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
<tr>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
<tr>
<td class="label" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
<tr>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">XXXtd>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
<tr>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
<tr >
<td valign="middle" colspan="1" class="label maxtd" align="center">XXXtd>
<td valign="middle" colspan="3" class="maxtd value">${XXX}td>
tr>
<tr>
<td colspan="1" class="label normaltd" align="center">XXXtd>
<td colspan="3" class="normaltd value">${XXX}td>
tr>
<tr>
<td colspan="1" class="label normaltd" align="center">XXXtd>
<td colspan="3" class="normaltd value">${XXX}td>
tr>
<tr>
<td colspan="1" class="label normaltd" align="center">XXXtd>
<td colspan="3" class="normaltd value">${XXX}td>
tr>
<tr>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
<tr>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
<tr>
<td class="label" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
<tr>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">XXXtd>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
<tr>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
<td class="label normaltd" align="center">XXXtd>
<td class="normaltd value">${XXX}td>
tr>
table>
div>
body>
html>
执行就可以看到生成的pdf文件了,文件路径在d:/test1.pdf。