1:依赖导入
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>7.1.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>7.1.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>font-asian</artifactId>
<version>7.1.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
<version>2.6.1</version>
</dependency>
2:配置文件添加
spring:
freemarker:
template-loader-path: classpath:/templates
suffix: .ftl
cache: false
charset: UTF-8
content-type: text/html
check-template-location: true
allow-request-override: true
allow-session-override: true
expose-request-attributes: true
expose-session-attributes: true
expose-spring-macro-helpers: true
request-context-attribute: request
prefer-file-system-access: false
3:工具类代码
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.font.FontProvider;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Map;
/**
* PDF工具类
*/
@Slf4j
public class FreemarkerToPdfUtil {
/**
* 创建pdf文件
*/
public static void writeFile(Map<String, Object> paramMap, Template template, HttpServletResponse response) throws IOException {
// 该路径用于本地测试使用
ServletOutputStream servletOutputStream;
response.setContentType("application/pdf");
// 设置浏览器以下载的方式处理该文件
response.setHeader("content-disposition", "attachment;filename=document.pdf");
servletOutputStream = response.getOutputStream();
String path = PdfUtil.class.getResource("/").getPath();
//字体路径 C:\Windows\Fonts
PdfFont ttcFont = PdfFontFactory.createTtcFont(path + "/fond/simsun.ttc", 1,
PdfEncodings.IDENTITY_H, false, true);
log.info("开始创建pdf");
long start = System.currentTimeMillis();
try {
Writer out = new StringWriter();
// 合并数据模型与模板
template.process(paramMap, out);
out.flush();
out.close();
String html = out.toString();
ConverterProperties props = new ConverterProperties();
FontProvider fontProvider = new FontProvider();
fontProvider.addFont(ttcFont.getFontProgram());
props.setFontProvider(fontProvider);
props.setCharset("utf-8");
PdfDocument pdf = new PdfDocument(new PdfWriter(servletOutputStream));
pdf.setDefaultPageSize(PageSize.A4);
//添加水印监听器
WaterMarkHandler waterMarkHandler = new WaterMarkHandler("你好",ttcFont);
pdf.addEventHandler(PdfDocumentEvent.INSERT_PAGE, waterMarkHandler);
Document document = HtmlConverter.convertToDocument(html, pdf, props);
document.getRenderer().close();
document.close();
log.info("写入pdf完成,耗时: {}", System.currentTimeMillis() - start);
} catch (Exception e) {
log.error("writeFile", e);
log.error("写入pdf失败: {}", e.getMessage());
}
}
/**
* 监听事件 添加水印
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
protected static class WaterMarkHandler implements IEventHandler {
private String waterMarkContent;
private PdfFont ttcFont;
@Override
public void handleEvent(com.itextpdf.kernel.events.Event event) {
PdfDocumentEvent documentEvent = (PdfDocumentEvent) event;
PdfDocument document = documentEvent.getDocument();
PdfPage page = documentEvent.getPage();
Rectangle pageSize = page.getPageSize();
JLabel label = new JLabel();
label.setText(this.waterMarkContent);
FontMetrics metrics = label.getFontMetrics(label.getFont());
int textHeight = metrics.getHeight();
int textWidth = metrics.stringWidth(label.getText());
Color myColor = new DeviceRgb(255, 100, 20);
//Canvas对象过期了,可以使用Document对象
Document document1 = new Document(document)
.setFont(ttcFont) //设置字体
.setFontColor(myColor)
.setFontSize(16); //字体大小
//循环添加水印-posX表示横向起始位置,从左向右。posY表示纵向起始位置,从下到上。
for (float posX = 75f; posX < pageSize.getWidth(); posX = posX + textWidth * 3) {
for (float posY = 50f; posY < pageSize.getHeight(); posY = posY + textHeight * 4) {
/*document1.showTextAligned(new Paragraph(this.waterMarkContent), posX, posY, document.getPageNumber(page),
TextAlignment.CENTER, VerticalAlignment.MIDDLE, 145);*/
document1.showTextAligned(this.waterMarkContent, posX, posY, TextAlignment.CENTER, 145f);
}
}
}
}
}
4:TestController
@RetController
@Slf4j
public class TestController {
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurergurer;
@GetMapping(value = "/exportTest")
public void exportTest(HttpServletResponse response) throws Exception {
log.info("=====================");
// 准备数据
Map<String, Object> dataMap = new HashMap();
dataMap.put("applyNo", "测试");
dataMap.put("mchtName", "测试");
dataMap.put("applyTime", "测试");
dataMap.put("customerName", "测试");
dataMap.put("images", TestController.class.getResource("/").getPath()+"/images/a.jpg");
dataMap.put("receiverAddress", "测试");
List<ExportProductmentProductDto> productList = new ArrayList<>();
ExportProductmentProductDto dto = new ExportProductmentProductDto();
dto.setUnit("台");
dto.setProductName("商品1");
dto.setDeliverNums("10");
dto.setPurchasePrice("200.00");
dto.setDeviceModel("M-550");
dto.setPurchaseTotalPrice("2000.00");
productList.add(dto);
productList.add(dto);
productList.add(dto);
dataMap.put("productList", productList);
Template template = freeMarkerConfigurergurer.getConfiguration().getTemplate("productment.ftl");
FreemarkerToPdfUtil.writeFile(dataMap, template, response);
}
5:模板代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Document</title>
<style>
h2 {
text-align: center;
}
table {
width: 100%;
border-collapse: collapse;
}
td {
padding: 10px;
text-align: center;
border: 1px solid black;
}
.leftTxt td {
border: none;
text-align: left;
}
</style>
</head>
<body>
<div>
<h2>
<img src="${(images)!''}" style="position: absolute;
left: 14px;
top: 29px;
width: 60px;
height: 60px;"/>
模板测试<br>
</h2>
</div>
<table>
<tr class="leftTxt">
<td colspan="2"><b>供应商:</b>${(mchtName)!''} </td>
<td colspan="2"><b>业务日期:</b>${(applyTime)!''} </td>
<td colspan="2"><b>订单编号:</b>${(applyNo)!''} </td>
</tr>
<tr class="leftTxt">
<td colspan="2"><b>采购员:</b>${(customerName)!''} </td>
<td colspan="4"><b>收货地址:</b>${(receiverAddress)!''} </td>
</tr>
<tr>
<td><b>产品类型</b></td>
<td><b>产品名称</b></td>
<td><b>数量</b></td>
<td><b>计量单位</b></td>
<td><b>含税单价(元)</b></td>
<td><b>含税金额(元)</b></td>
</tr>
<#if productList?? && (productList?size >0)>
<#list productList as product>
<tr>
<td>${(product.deviceModel)!''}</td>
<td>${(product.productName)!''}</td>
<td>${(product.deliverNums)!''}</td>
<td>${(product.unit)!''}</td>
<td>${(product.purchasePrice)!''}</td>
<td>${(product.purchaseTotalPrice)!''}</td>
</tr>
</#list>
</#if>
</table>
</body>
</html>
6:效果图