首先是引入POM文件:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.11</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.11</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
<!-- flying saucer,支持对CSS高级特性的解析 -->
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.1.6</version>
</dependency>
编写PDF工具类:
package com.example.test222;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.stereotype.Component;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Map;
@Component
public class PdfUtil {
/**
* 打印pdf方法
*
* @param data 模板中需要填充的数据
* @param templateName 模板名称
* @param response 响应对象 通过流直接返回到前端进行展示
* @throws Exception 这个异常可以自己处理
*/
public void freeMarkerRender(Map<String, Object> data, String templateName, HttpServletResponse response) throws Exception {
// 设置返回字符集、contentType
response.setCharacterEncoding("UTF-8");
response.setContentType("application/pdf");
ServletOutputStream outputStream = response.getOutputStream();
// 获取模板文件
Configuration configuration = new Configuration(Configuration.getVersion());
configuration.setClassForTemplateLoading(this.getClass(), "/static/pdf");
Template template = configuration.getTemplate(templateName);
StringWriter out = new StringWriter();
// 合并模板跟数据
template.process(data, out);
// htmlData 模板字符流
String htmlData = out.toString();
// 设置文档格式,数字边距
Document document = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
// 添加页码
writer.setPageEvent(new PdfReportM1HeaderFooter());
writer.setPageEvent(new PdfPageHelper());
// 打开文档
document.open();
// HTML 转成pdf
XMLWorkerHelper.getInstance().parseXHtml(writer, document, new ByteArrayInputStream(htmlData.getBytes()), StandardCharsets.UTF_8, new MyFontsProvider(12));
// 关闭文档
document.close();
outputStream.flush();
}
static class PdfPageHelper extends PdfPageEventHelper {
@Override
public void onOpenDocument(PdfWriter writer, Document document) {
super.onOpenDocument(writer, document);
writer.getDirectContent().createTemplate(30, 16);
}
@Override
public void onStartPage(PdfWriter writer, Document document) {
super.onStartPage(writer, document);
}
@Override
public void onEndPage(PdfWriter writer, Document document) {
BaseFont bf = null;
try {
bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
} catch (Exception e) {
e.printStackTrace();
}
// extracted(writer, document, bf);
// 添加水印
addWaterMark(writer, bf);
}
// private void extracted(PdfWriter writer, Document document, BaseFont bf) {
// PdfContentByte cb = writer.getDirectContent();
// cb.saveState();
// cb.beginText();
//
// cb.setFontAndSize(bf, 10);
// float x = document.top(-20);
// // 左
// cb.showTextAligned(PdfContentByte.ALIGN_LEFT, "顶部-左", document.left(), x, 0);
// // 中
// cb.showTextAligned(PdfContentByte.ALIGN_CENTER, writer.getPageNumber() + " page", (document.right() + document.left()) / 2, x, 0);
// // 右
// cb.showTextAligned(PdfContentByte.ALIGN_RIGHT, "顶部-右", document.right(), x, 0);
// // Footer
// float y = document.bottom(-20);
// // 左
// cb.showTextAligned(PdfContentByte.ALIGN_LEFT, "底部-左", document.left(), y, 0);
// // 中
// cb.showTextAligned(PdfContentByte.ALIGN_CENTER, writer.getPageNumber() + " page", (document.right() + document.left()) / 2, y, 0);
// // 右
// cb.showTextAligned(PdfContentByte.ALIGN_RIGHT, "底部-右", document.right(), y, 0);
// cb.endText();
// cb.restoreState();
// }
/**
* 添加水印
*/
private void addWaterMark(PdfWriter writer, BaseFont bf) {
PdfContentByte cb = writer.getDirectContent();
cb.saveState();
cb.beginText();
cb.setColorFill(BaseColor.GRAY);
PdfGState gs = new PdfGState();
gs.setFillOpacity(0.3f);
cb.setGState(gs);
cb.setFontAndSize(bf, 100);
// rotation指倾斜度 可以根据自己的需求做调整 不需要倾斜的话可以调整为0
cb.showTextAligned(Element.ALIGN_MIDDLE, "总装一线", 100, 450, 0);
cb.endText();
cb.restoreState();
}
}
static class MyFontsProvider extends XMLWorkerFontProvider {
private int fontSize;
public MyFontsProvider(int fontSize) {
this.fontSize = fontSize;
}
@Override
public Font getFont(final String fontName, final String encoding, final boolean embedded, final float size, final int style, final BaseColor color) {
BaseFont bf = null;
try {
bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
} catch (DocumentException | IOException e) {
e.printStackTrace();
}
Font font = null;
if (fontSize != 0) {
font = new Font(bf, fontSize, style, color);
} else {
font = new Font(bf, size, style, color);
}
font.setColor(color);
return font;
}
public int getFontSize() {
return fontSize;
}
public void setFontSize(int fontSize) {
this.fontSize = fontSize;
}
}
static class PdfReportM1HeaderFooter extends PdfPageEventHelper {
/**
* 页眉
*/
public String header = "页眉";
/**
* 文档字体大小,页脚页眉最好和文本大小一致
*/
public int presentFontSize = 12;
// 模板
public PdfTemplate total;
private BaseFont bf = null;
public PdfReportM1HeaderFooter() {
}
/**
* 文档打开时创建模板
*/
public void onOpenDocument(PdfWriter writer, Document document) {
total = writer.getDirectContent().createTemplate(50, 50);// 共 页 的矩形的长宽高
}
/**
* 关闭每页的时候,写入页眉,写入'第几页共'这几个字。
*
* @see com.itextpdf.text.pdf.PdfPageEventHelper#onEndPage(com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
*/
public void onEndPage(PdfWriter writer, Document document) {
Font fontDetail = null;
try {
bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);
fontDetail = new Font(bf, presentFontSize, Font.NORMAL);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 1.写入页眉
// ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, new Phrase(header, fontDetail), document.left(), document.top() + 20, 0);
// 2.写入前半部分的 第 X页/共
int pageS = writer.getPageNumber();
String foot1 = "第 " + pageS + " 页 /共";
Phrase footer = new Phrase(foot1, fontDetail);
// 3.计算前半部分的foot1的长度,后面好定位最后一部分的'Y页'这俩字的x轴坐标,字体长度也要计算进去 = len
float len = bf.getWidthPoint(foot1, presentFontSize);
// 4.拿到当前的PdfContentByte
PdfContentByte cb = writer.getDirectContent();
// 5.写入页脚
ColumnText.showTextAligned(cb, Element.ALIGN_CENTER, footer, (document.rightMargin() + document.right() + document.leftMargin() - document.left() - len) / 2.0F + 20F, document.bottom() - 20, 0);
cb.addTemplate(total, (document.rightMargin() + document.right() + document.leftMargin() - document.left()) / 2.0F + 20F, document.bottom() - 20);
}
/**
* 关闭文档时,替换模板,完成整个页眉页脚组件
*/
public void onCloseDocument(PdfWriter writer, Document document) {
// 关闭文档的时候,将模板替换成实际的 Y 值,至此,page x of y 制作完毕,完美兼容各种文档size。
total.beginText();
total.setFontAndSize(bf, presentFontSize);// 生成的模版的字体、颜色
String foot2 = " " + (writer.getPageNumber()) + " 页";
total.showText(foot2);// 模版显示的内容
total.endText();
total.closePath();
}
}
}
编写一个html模板:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
<style>
body{font-family:SimSun;}
.title{align-content: center;text-align: center;}
.signature{float:right }
.myDiv{
opacity: 0.5%;
}
</style>
</head>
<body>
<div>
<h1 class="title">pdfTest</h1>
<h4 class="title"> </h4>
<div class="myDiv" style="height: 800px;">
<span>当前时间: ${date_time} </span>
</div>
<div class="signature">日期:${date}</div>
<div class="myDiv" style="height: 800px;">
<span>当前时间: ${date_time} </span>
</div>
<div class="signature">日期:${date}</div>
</div>
</body>
</html>
资源路径:
编写接口前端访问,直接弹出pdf页面:
package com.example.test222;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/test")
public class TestController {
@Autowired
private PdfUtil pdfUtil;
@RequestMapping("/print")
public void print(HttpServletResponse response) throws Exception {
Map<String, Object> map = new HashMap<>();
map.put("date_time", "tiem");
map.put("date", "123");
pdfUtil.freeMarkerRender(map, "test.html", response);
}
}
打印效果如下: