1 依赖
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.26-incubating</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.4.2</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.4.1</version>
</dependency>
2 写freemarker工具类
import freemarker.cache.FileTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
public class FreeMarkerUtil {
public static String getContent(String templatesPath,String templateName,Object data){
StringWriter writer = new StringWriter();
try {
Template template = getConfiguration(templatesPath).getTemplate(templateName);
template.process(data,writer);
} catch (IOException e) {
e.printStackTrace();
} catch (TemplateException e) {
e.printStackTrace();
} finally {
writer.flush();
return writer.toString();
}
}
private static Configuration getConfiguration(String templateFilePath){
Configuration config = new Configuration(Configuration.VERSION_2_3_25);
config.setDefaultEncoding("UTF-8");
config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
config.setLogTemplateExceptions(false);
FileTemplateLoader fileTemplateLoader=null;
try {
fileTemplateLoader = new FileTemplateLoader(new File(templateFilePath));
} catch (IOException e) {
e.printStackTrace();
}
config.setTemplateLoader(fileTemplateLoader);
return config;
}
}
3 写生成pdf的工具类
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class PdfUtil {
public static void exportPdfFile(String outFilePath,String content,String fontPath){
File file = new File(outFilePath);
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
Document document = new Document(PageSize.A4);
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream(file);
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
MyPdfEvent myPdfEvent = new MyPdfEvent();
myPdfEvent.setFontSize(15);
writer.setPageEvent(myPdfEvent);
document.open();
XMLWorkerHelper.getInstance().parseXHtml(writer,document,
new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)),
XMLWorkerHelper.class.getResourceAsStream("/default.css"),
StandardCharsets.UTF_8,
new XMLWorkerFontProvider("E:/siyuan/fonts"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
document.close();
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4 继承PdfPageEventHelper绑定事件
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import java.io.IOException;
public class MyPdfEvent extends PdfPageEventHelper {
private String fontFileName;
private BaseFont baseFont;
private Font font;
private int fontSize;
private PdfTemplate templateFoot;
private PdfTemplate templateHeader;
private Object data;
public MyPdfEvent() {
}
@Override
public void onEndPage(PdfWriter writer, Document document) {
initFront();
writeHeader(writer,document,this.font);
try {
writeFooter(writer,document,data,this.font);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void initFront(){
try {
if (this.baseFont == null) {
String fontPath = MyPdfEvent.class.getClassLoader().getResource("fonts/ping_fang_light.ttf").getPath();
this.baseFont = BaseFont.createFont(fontPath,BaseFont.IDENTITY_H,BaseFont.EMBEDDED);
}
if (this.font == null) {
this.font = new Font(this.baseFont, fontSize, Font.NORMAL);
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void writeHeader(PdfWriter writer, Document document, Font font) {
PdfContentByte cb = writer.getDirectContent();
float x = (document.left() + document.right()) / 2;
float y = document.top() + 20;
ColumnText.showTextAligned(cb, Element.ALIGN_CENTER, new Phrase("我是页眉", font), x, y, 0);
cb.setLineWidth(0.5f);
cb.moveTo(document.left(), y-2);
cb.lineTo(document.right(), y-2);
cb.stroke();
}
public void writeFooter(PdfWriter writer, Document document, Object data, Font font) throws DocumentException, IOException {
int pageS = writer.getPageNumber();
int currentPage = pageS ;
if (currentPage <= 0) {
return;
}
Phrase footer1 = new Phrase( "2023/04/15", font);
Phrase footer2 = new Phrase(currentPage + "/", font);
PdfContentByte cb = writer.getDirectContent();
ColumnText.showTextAligned(
cb,
Element.ALIGN_LEFT,
footer1,
(document.left() + 10),
document.bottom() - 20,
0);
ColumnText.showTextAligned(
cb,
Element.ALIGN_RIGHT,
footer2,
(document.right() - 30),
document.bottom() - 20, 0);
cb.addTemplate(this.templateFoot, document.right() - 30, document.bottom() - 20);
}
@Override
public void onOpenDocument(PdfWriter writer, Document document) {
this.templateFoot = writer.getDirectContent().createTemplate(50,50);
}
@Override
public void onCloseDocument(PdfWriter writer, Document document) {
templateFoot.beginText();
templateFoot.setFontAndSize(this.baseFont,this.fontSize);
int total = writer.getPageNumber() - 1;
templateFoot.showText(total+ "");
templateFoot.endText();
templateFoot.closePath();
}
public String getFontFileName() {
return fontFileName;
}
public void setFontFileName(String fontFileName) {
this.fontFileName = fontFileName;
}
public BaseFont getBaseFont() {
return baseFont;
}
public void setBaseFont(BaseFont baseFont) {
this.baseFont = baseFont;
}
public Font getFont() {
return font;
}
public void setFont(Font font) {
this.font = font;
}
public int getFontSize() {
return fontSize;
}
public void setFontSize(int fontSize) {
this.fontSize = fontSize;
}
public PdfTemplate getTemplateFoot() {
return templateFoot;
}
public void setTemplateFoot(PdfTemplate templateFoot) {
this.templateFoot = templateFoot;
}
public PdfTemplate getTemplateHeader() {
return templateHeader;
}
public void setTemplateHeader(PdfTemplate templateHeader) {
this.templateHeader = templateHeader;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
5 测试
5.1 准备数据
static {
person.setName("思源");
person.setAge(1);
person.setAvatar("C:\\Users\\Administrator\\Desktop\\siyuan.png");
person.setSex("1");
LocalDate localBirthDate = LocalDate.of(2022, 6, 18);
Date birthDate = Date.from(localBirthDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
person.setBirthDate(birthDate);
Pet petDog = new Pet("老大","狗");
Pet petCat = new Pet("老二","猫");
Pet petDog2 = new Pet("老三","狗");
Pet petCat2 = new Pet("老四","猫");
List<Pet> pets = Arrays.asList(petDog, petCat,petDog2,petCat2);
person.setPetList(pets);
}
5.2 测试
@GetMapping("/genePdf")
public String generatePdf(){
String temPath = TestController.class.getClassLoader().getResource("templates").getPath();
String content = FreeMarkerUtil.getContent(temPath,"思源.ftl", person);
String outFile = siYuanConfig.getOutPath() + "/" + "siyuan.pdf";
PdfUtil.exportPdfFile(outFile,content,siYuanConfig.getFontPath());
return outFile;
}
5.3 测试结果
- 生成成功

- pdf内容如下:

6 ftl模版如下
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta http-equiv="Content-Style-Type" content="text/css"/>
<title></title>
<style type="text/css">
body {
font-family: pingfang sc light;
font-size: 12px;
line-height: 2;
}
.center{
text-align: center;
width: 100%;
}
</style>
</head>
<body>
<div>
<div>思源</div>
<div>
<strong>姓名:</strong>${name!'无数据'}
</div>
<div>
<strong>年龄:</strong>${age!'无数据'}
</div>
<div><strong>性别:</strong>
<span>
<#if sex??>
<#if sex=="0" >男</#if><#if sex=="1" >女</#if><#if sex=="2" >未知</#if>
<#else>
无数据
</#if>
</span>
</div>
<div>
<strong>出生日期:</strong>${(birthDate?string('yyyy-MM-dd'))!'无数据'}
</div>
<div>
<strong>头像:</strong><img src="${avatar}" width="170" height="170"/>
</div>
</div>
<div>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;margin-bottom: 20px;border-style: dashed;" border="1" rules="all" >
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;border-bottom: 1px #999999 solid;margin-bottom: 20px;text-align: center;" border="1" rules="all">
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;margin-bottom: 20px;border-style: dashed;" border="1" rules="all" >
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;border-bottom: 1px #999999 solid;margin-bottom: 20px;text-align: center;" border="1" rules="all">
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;margin-bottom: 20px;border-style: dashed;" border="1" rules="all" >
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;border-bottom: 1px #999999 solid;margin-bottom: 20px;text-align: center;" border="1" rules="all">
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;margin-bottom: 20px;border-style: dashed;" border="1" rules="all" >
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;border-bottom: 1px #999999 solid;margin-bottom: 20px;text-align: center;" border="1" rules="all">
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;margin-bottom: 20px;border-style: dashed;" border="1" rules="all" >
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;border-bottom: 1px #999999 solid;margin-bottom: 20px;text-align: center;" border="1" rules="all">
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;margin-bottom: 20px;border-style: dashed;" border="1" rules="all" >
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;border-bottom: 1px #999999 solid;margin-bottom: 20px;text-align: center;" border="1" rules="all">
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;margin-bottom: 20px;border-style: dashed;" border="1" rules="all" >
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;border-bottom: 1px #999999 solid;margin-bottom: 20px;text-align: center;" border="1" rules="all">
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;margin-bottom: 20px;border-style: dashed;" border="1" rules="all" >
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<table cellspacing="0" cellpadding="10" style="width: 100%;border-collapse: collapse;border-bottom: 1px #999999 solid;margin-bottom: 20px;text-align: center;" border="1" rules="all">
<tbody>
<tr>
<td style="background: #F2F2F2;font-size: 12px;">宠物名</td>
<td style="background: #F2F2F2;font-size: 12px;">种类</td>
</tr>
<#if petList??>
<#list petList as pet>
<tr>
<td style="font-size: 12px;">${pet.petName!'无数据'}</td>
<td style="font-size: 12px;">${pet.category!'无数据'}</td>
</tr>
</#list>
</#if>
</tbody>
</table>
</div>
</body>
</html>