一、概述:
1. springboot如何生成pdf,接口可以预览可以下载
2. vue下载通过bold如何下载
3. 一些细节:页脚、页眉、水印、每一页得样式添加
二、直接上代码【主要是一个记录下次开发更快】
模板位置
1. 导入pom包
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>5.0.5</version>
</dependency>
<!-- 中文字体支持 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>font-asian</artifactId>
<version>7.2.1</version>
</dependency>
2. 写工具类
[1] 页眉工具类
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.properties.TextAlignment;
import org.springframework.util.StringUtils;
import java.net.MalformedURLException;
import java.net.URL;
public class HeaderMarkerEventHandler implements IEventHandler {
/**
* pdf字体
*/
private final PdfFont pdfFont;
/**
* 页眉显示
*/
private final String title;
/**
* logo地址
*/
private String logoUrl;
public HeaderMarkerEventHandler(PdfFont pdfFont, String title) {
this.pdfFont = pdfFont;
this.title = title;
}
public HeaderMarkerEventHandler(PdfFont pdfFont, String title, String logoUrl) {
this.pdfFont = pdfFont;
this.title = title;
this.logoUrl = logoUrl;
}
@Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdf = docEvent.getDocument();
PdfPage page = docEvent.getPage();
Rectangle pageSize = page.getPageSize();
PdfCanvas pdfCanvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), pdf);
Canvas canvas = new Canvas(pdfCanvas, pageSize);
float x = pageSize.getRight() - 60;
float y = pageSize.getTop() - 32;
Paragraph p = new Paragraph(title)
.setFontSize(9)
.setFont(pdfFont);
// 页眉字体显示得位置
canvas.showTextAligned(p, x, y, TextAlignment.RIGHT);
// 加载图片
if (!StringUtils.isEmpty(logoUrl)) {
try {
URL url = new URL(logoUrl);
Image logo = new Image(ImageDataFactory.create(url));
logo.scaleAbsolute(100, 20);
logo.setMarginLeft(30);
logo.setMarginTop(15);
canvas.add(logo);
} catch (MalformedURLException e) {
System.out.println("logo 无法解析: " + logoUrl);
}
}
canvas.close();
}
}
[2] 水印工具类
import com.itextpdf.kernel.colors.WebColors;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.layout.properties.VerticalAlignment;
import java.io.IOException;
/**
* 生成 pdf 水印
*/
public class WaterMarkEventHandler implements IEventHandler {
/**
* 水印内容
*/
private final String waterMarkContent;
/**
* 一页中有几列水印
*/
private final int waterMarkX;
/**
* 一页中每列有多少水印
*/
private final int waterMarkY;
public WaterMarkEventHandler(String waterMarkContent) {
this(waterMarkContent, 5, 5);
}
public WaterMarkEventHandler(String waterMarkContent, int waterMarkX, int waterMarkY) {
this.waterMarkContent = waterMarkContent;
this.waterMarkX = waterMarkX;
this.waterMarkY = waterMarkY;
}
@Override
public void handleEvent(Event event) {
PdfDocumentEvent documentEvent = (PdfDocumentEvent) event;
PdfDocument document = documentEvent.getDocument();
PdfPage page = documentEvent.getPage();
Rectangle pageSize = page.getPageSize();
PdfFont pdfFont = null;
try {
pdfFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H");
} catch (IOException e) {
throw new RuntimeException(e);
}
PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), document);
Paragraph waterMark = new Paragraph(waterMarkContent).setOpacity(0.5f);
Canvas canvas = new Canvas(pdfCanvas, pageSize)
.setFontColor(WebColors.getRGBColor("lightgray"))
.setFontSize(16)
.setFont(pdfFont);
for (int i = 0; i < waterMarkX; i++) {
for (int j = 0; j < waterMarkY; j++) {
canvas.showTextAligned(waterMark, (150 + i * 300), (160 + j * 150), document.getNumberOfPages(),
TextAlignment.CENTER, VerticalAlignment.BOTTOM, 120);
}
}
canvas.close();
}
}
[3]添加页脚工具类
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.properties.TextAlignment;
/**
* 生成 pdf 页码
*/
public class PageEventHandler implements IEventHandler {
/**
* pdf字体
*/
private final PdfFont pdfFont;
public PageEventHandler(PdfFont pdfFont) {
this.pdfFont = pdfFont;
}
@Override
public void handleEvent(Event event) {
PdfDocumentEvent documentEvent = (PdfDocumentEvent) event;
PdfDocument document = documentEvent.getDocument();
PdfPage page = documentEvent.getPage();
Rectangle pageSize = page.getPageSize();
PdfCanvas pdfCanvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), document);
Canvas canvas = new Canvas(pdfCanvas, pageSize);
float x = (pageSize.getLeft() + pageSize.getRight()) / 2;
float y = pageSize.getBottom() + 15;
Paragraph paragraph =
new Paragraph(""+document.getPageNumber(page) )
.setFontSize(10)
.setFont(pdfFont);
canvas.showTextAligned(paragraph, x, y, TextAlignment.CENTER);
canvas.close();
}
}
[4]工具类;重点说一下这个字体:
1. 本文第一个图里面font得字体路径在Windows电脑:C:\Windows\Fonts;看中哪个字体就直接拷贝进去进会自动生成ttc文件;我拷贝得是宋体和微软雅黑,主要解决加粗
2.前端
font-family: "MicrosoftYaHei-Bold";
这个字体得名字日下图:
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.kernel.events.PdfDocumentEvent;
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.font.FontProvider;
import org.apache.velocity.Template;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* PDF工具
*
* @author ppp
* @date 2022/8/5
*/
public class Html2PdfUtil {
static {
Velocity.setProperty(RuntimeConstants.INPUT_ENCODING, StandardCharsets.UTF_8);
Velocity.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
Velocity.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
Velocity.init();
}
/**
* 据模板生成pfd格式文件
*
* @param context 上下文对象
* @param template pdf模板
* @param outputStream 生成ofd文件输出流
*/
public static void pdfFile(Context context, String template, OutputStream outputStream, String watermarkText) throws IOException {
PdfWriter pdfWriter = null;
PdfDocument pdfDocument = null;
StringWriter writer = null;
try {
pdfWriter = new PdfWriter(outputStream);
pdfDocument = new PdfDocument(pdfWriter);
PageSize pageSize = new PageSize(842.0F,595.0F);
pdfDocument.setDefaultPageSize(pageSize);
ConverterProperties properties = new ConverterProperties();
// 添加字体
FontProvider fontProvider = new FontProvider();
// 字体设置,解决中文不显示问题
PdfFont sysFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H");
fontProvider.addFont(sysFont.getFontProgram(), "UniGB-UCS2-H");
// 添加宋体,html中使用SimSun,不指定则默认为第一个引入的字体
sysFont = PdfFontFactory.createFont("font/simsun.ttc,0", PdfEncodings.IDENTITY_H);
fontProvider.addFont(sysFont.getFontProgram(), PdfEncodings.IDENTITY_H);
// 添加微软雅黑,html中使用MicrosoftYaHei
sysFont = PdfFontFactory.createFont("font/msyh.ttc,0", PdfEncodings.IDENTITY_H);
fontProvider.addFont(sysFont.getFontProgram(), PdfEncodings.IDENTITY_H);
// 添加微软雅黑粗体,html中使用MicrosoftYaHei-Bold
sysFont = PdfFontFactory.createFont("font/msyhbd.ttc,0", PdfEncodings.IDENTITY_H);
fontProvider.addFont(sysFont.getFontProgram(), PdfEncodings.IDENTITY_H);
// 处理微软雅黑及粗体描述符错乱问题
processYaHeiFontDescriptor(fontProvider);
properties.setFontProvider(fontProvider);
// 添加页眉
// pdfDocument.addEventHandler(PdfDocumentEvent.START_PAGE, new HeaderMarkerEventHandler(sysFont, "学校"));
// 添加水印
pdfDocument.addEventHandler(PdfDocumentEvent.INSERT_PAGE, new WaterMarkEventHandler(watermarkText));
// 添加页脚
pdfDocument.addEventHandler(PdfDocumentEvent.END_PAGE, new PageEventHandler(sysFont));
// 读取html模板和赋值
Template pfdTemplate = Velocity.getTemplate(template, "UTF-8");
writer = new StringWriter();
pfdTemplate.merge(context, writer);
// 构建带有样式的 HTML 字符串
StringBuilder htmlWithStyles = new StringBuilder();
htmlWithStyles.append("<html><head><style>");
// 如果有边距参数,则添加到样式中
htmlWithStyles.append("@page { ");
htmlWithStyles.append("margin-bottom: ").append(30).append("mm; ");
htmlWithStyles.append("}");
htmlWithStyles.append("</style></head><body>").append("</body></html>");
// html转换PDF
HtmlConverter.convertToPdf(writer.toString(), pdfDocument, properties);
} catch (Exception e) {
throw new RuntimeException("PFD文件生成失败", e);
}finally {
pdfDocument.close();
writer.close();
pdfWriter.close();
}
}
}
3.html模板,里面那个分页,就是新开一页,估计有点问题
【1】、td 换行得自己加
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
</head>
<body>
<div class="contianer">
<div>
<div class="fourth_title">
<h4>2-2 专业群建设总目标</h4>
</div>
<div class="third_con">
<p class="pFontCon">
$!{xxxxmb.zyqjszmb}
</p>
</div>
</div>
<div>
#foreach($html in $canVasHtml)
<div class="page-break">
$html
</div>
#end
</div>
</div>
</body>
<style>
h1,h2 {
font-style: italic;
font-family: "MicrosoftYaHei-Bold";
text-align: center;
font-weight: bold; /* 加粗文本 */
}
td{
white-space:normal;
word-wrap:break-word;
word-break:break-all;
}
@media print {
.page-break {
page-break-before: always; /* 在元素之前插入分页 */
}
}
/* 每次新开一个分页 */
.page-break {
page-break-before: always; /* 在元素之前插入分页 */
}
</style
</html>
4. Service 只留了一部分代码
@Override
public void downloadAchievementsByPdf( HttpServletResponse response, HttpServletRequest request) throws IOException {
OutputStream outputStream = null;
try {
response.reset();
String fileName = schooleInfo.getXxmc();
// 这个是直接下载
response.setHeader("Content-Type", "application/pdf");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".pdf", "utf-8"));
// 这个是预览,访问接口页面打开得是一个FPD预览得页面
// response.setContentType("application/pdf");
// response.addHeader("Content-Disposition", "inline; filename=" + fileName);
VelocityContext context = new VelocityContext();
setBaseInfo(context, schooleInfo);
context.put("zyqxxList", zyqjbxxTS);
context.put("xxxxmb", zyqjbxxTInfoOne);
// 获取指标数据
List<Map<String, Object>> maps = (List<Map<String, Object>>) treeWithCanvas(schoolBookId, isTask, taskId, schoolId).get("data");
List<String> canVasHtml = PdfTemplate.getCanVasHtml(maps);
context.put("canVasHtml", canVasHtml);
outputStream = response.getOutputStream();
Html2PdfUtil.pdfFile(context, "templates/jxzpbPdf.html", outputStream, schooleInfo.getXxmc());
} catch (Exception e) {
e.printStackTrace();
}finally {
outputStream.close();
}
}
private void setBaseInfo(VelocityContext context, XxbcxxT schooleInfo) {
// 1. 查询学校基本信息
context.put("xmdw", schooleInfo.getXxmc());
context.put("xmmc", schooleInfo.getXmmc());
context.put("tbrq", DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD));
context.put("jbdw", schooleInfo.getJbdw());
context.put("szsf", schooleInfo.getSfbm() == null ? "" : ProvinceJson.getProName(schooleInfo.getSfbm()));
}
5. VUE代码
return request(url, {
...params,
method: 'GET',
responseType: 'blob'
}).then((data) => {
const aLink = document.createElement('a');
const blob = new Blob([data],{type: 'application/pdf'});
aLink.style.display = 'none';
aLink.href = URL.createObjectURL(blob);
aLink.setAttribute('download', fileName);
document.body.appendChild(aLink);
aLink.click();
URL.revokeObjectURL(aLink.href); // 清除引用
document.body.removeChild(aLink);
});
三、最后还有一个加粗得问题。思路就是需要引入字体文件