SpringBoot 集成 html2Pdf

devtools/2024/11/25 1:57:15/

一、概述:

        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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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中使用MicrosoftYaHeisysFont = PdfFontFactory.createFont("font/msyh.ttc,0", PdfEncodings.IDENTITY_H);fontProvider.addFont(sysFont.getFontProgram(), PdfEncodings.IDENTITY_H);// 添加微软雅黑粗体,html中使用MicrosoftYaHei-BoldsysFont = 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转换PDFHtmlConverter.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);});

三、最后还有一个加粗得问题。思路就是需要引入字体文件


http://www.ppmy.cn/devtools/136715.html

相关文章

docker使用阿里云容器镜像服务下载公共镜像

写在当所有的加速镜像源都失效之后…… 1&#xff0c;登录阿里云进入容器镜像服务 阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台 2&#xff0c;搜索开源的镜像 3&#xff0c;查看下载连接 4&#xff0c;拉取镜像 5&#xff0c;自己建私服 阿里镜像仓库…

机器学习实战记录(1)

决策树——划分数据集 def splitDataSet(dataSet, axis, value): retDataSet [] #创建返回的数据集列表for featVec in dataSet: #遍历数据集if featVec[axis] value:reducedFeatVec featVec[:axis] #去掉axis特征reducedFeatVec.extend(featVec[axis1…

【Golang】手搓DES加密

代码非常长 有六百多行 参考一位博主的理论实现 通俗易懂&#xff0c;十分钟读懂DES 还有很多不足的地方 感觉只是个思路 S盒&#xff08;理论既定&#xff09; package src// 定义S - 盒的置换表 var SBoxes [8][4][16]int{{{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, …

C#使用实体类Entity Framework Core操作mysql入门:从数据库反向生成模型2 处理连接字符串

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

Redis | Redis常用命令及示例总结(API)

前言 参考文档&#xff1a;http://doc.redisfans.com/index.html 本篇主要总结Redis的常用命令&#xff0c;笔者在记录命令时的格式如下&#xff1a; 命令关键字&#xff1a;命令示例&#xff1b;命令说明&#xff1b; 命令参数解释及一些说明&#xff1b; 其中命令关键字使用…

云计算实训室建设的必要性

一、云计算发展的背景 云计算作为一种新型的信息技术服务模式&#xff0c;通过互联网提供动态易扩展且通常是虚拟化的资源&#xff0c;涵盖了从基础设施服务&#xff08;IaaS&#xff09;、平台服务&#xff08;PaaS&#xff09;到软件服务&#xff08;SaaS&#xff09;等多个…

CentOS 9 无法启动急救方法

方法一&#xff1a;通过单用户安全模式启动 开机按上下方向键&#xff0c;选择需要启动的内核&#xff0c;按e键进入配置模式 修改配置 ro 改 rw 删除 rhgb quiet 末尾增加 init/bin/bash 按 Ctrlx 启动单用户模式 如果想重新启动&#xff0c;重启电脑 执行 exec /sbin/in…

聚焦IOC容器刷新环节postProcessBeanFactory(BeanFactory后置处理)专项

目录 一、IOC容器的刷新环节快速回顾 二、postProcessBeanFactory源码展示分析 &#xff08;一&#xff09;模版方法postProcessBeanFactory &#xff08;二&#xff09;AnnotationConfigServletWebServerApplicationContext 调用父类的 postProcessBeanFactory 包扫描 …