基于SpringBoot用iText7将HTML转成PDF添加页眉页脚水印

news/2024/4/29 8:11:00/文章来源:https://blog.csdn.net/CodeBlues/article/details/137002072

 一、依赖

        <!-- itext7 --><dependency><groupId>com.itextpdf</groupId><artifactId>kernel</artifactId><version>7.2.4</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>io</artifactId><version>7.2.4</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>forms</artifactId><version>7.2.4</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>layout</artifactId><version>7.2.4</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>svg</artifactId><version>7.2.4</version></dependency><!-- font-asian 用于itext使用东亚字体 --><dependency><groupId>com.itextpdf</groupId><artifactId>font-asian</artifactId><version>7.2.4</version></dependency><!-- html2pdf 用于转换html为pdf --><dependency><groupId>com.itextpdf</groupId><artifactId>html2pdf</artifactId><version>4.0.1</version></dependency>

二、代码

2.1、通用方法

/**** @param pdf_path PDF文件保存路径* @param pdf_name PDF文件名* @param cookie 用户信息* @param waterContent 水印内容* @return* @throws AppException*/public PmsLXPDF createHtmlToPDF(String pdf_path, String pdf_name, String cookie, String waterContent) throws AppException {logger.info("开始createPDF");PmsLXPDF pdf = new PmsLXPDF();String pdfViewUrl = "这里是接口路径,生成jsp模板";String url = Config.getMeurl();logger.info("createPDF方法获取meurl[{}]",url);ItextHtmlToPdf.convert(url + pdfViewUrl, pdf_path, cookie, waterContent);File file = new File(pdf_path);if (file == null || !file.exists()) {throw new AppException("立项报告生成pdf失败!");}try {//可以做一些保存的操作} catch (Exception e) {logger.error("Exception happened", e);}//pdf.setSuccess(StringUtils.isNotBlank(id));logger.info("createPDF结束");return pdf;}/*** html转pdf** @param srcPath html路径,可以是硬盘上的路径,也可以是网络路径* @param destPath pdf保存路径* @return 转换成功返回true*/public static boolean convert(String srcPath, String destPath, String cookie, String waterContent) {if (!FileUtils.createFile(destPath)) {LOGGER.error("目标路径PDF文件已存在,生成PDF失败:" + destPath);return false;}LOGGER.info("convert方法传入的srcPath[{}],cookie[{}]",srcPath,cookie);InputStream in = null;OutputStream out = null;HttpURLConnection connection = null;try {if (srcPath.startsWith("http")) {LOGGER.info("convert方法传入的srcPath[{}],进入了需要cookie的分支",srcPath);URL url = new URL(srcPath);int connectTimeout = Integer.parseInt(ConfigUtil.getConfig("xxx", "30000"));int readTimeout = Integer.parseInt(ConfigUtil.getConfig("xxx", "30000"));connection = (HttpURLConnection) url.openConnection();// 设置连接主机服务器的超时时间connection.setConnectTimeout(connectTimeout);// 设置读取远程返回的数据时间connection.setReadTimeout(readTimeout);if (StringUtils.isNotBlank(cookie)) {// 设置CookieString cookies = "SESSION=".concat(cookie);connection.setRequestProperty("Cookie", cookies);}// 连接和获取响应connection.connect();in = connection.getInputStream();} else {LOGGER.info("convert方法传入的srcPath[{}],进入了不需要登录的分支",srcPath);in = new FileInputStream(srcPath);}out = new ByteArrayOutputStream();HtmlPrittifier.fixHtml(in, out);} catch (Throwable t) {LOGGER.error("读取该文件失败:" + srcPath, t);return false;} finally {IOUtils.closeQuietly(in);IOUtils.closeQuietly(out);if (connection != null) {connection.disconnect();// 关闭远程连接}}String html = out.toString();converCreatPdf(destPath, cookie, html, waterContent);return true;}private static boolean converCreatPdf(String destPath, String cookie, String html, String waterContent) {try (OutputStream fos = new FileOutputStream(destPath)) {//将html转换成pdfPdfWriter pdfWriter = new PdfWriter(fos);PdfDocument pdfDoc = new PdfDocument(pdfWriter);// 统一设置页眉String header = "我是页眉";Header headerHandler = new Header(header);pdfDoc.addEventHandler(PdfDocumentEvent.START_PAGE, headerHandler);// 统一设置页脚Footer footerHandler = new Footer();pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, footerHandler);if (StringUtils.isNotBlank(waterContent)) {// 添加水印PdfWaterMarker pdfWaterMarker = new PdfWaterMarker(waterContent);pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, pdfWaterMarker);}// 需要先生成document,而不是直接生成pdf文件(因直接生成pdf文件会关闭流)ConverterProperties converterProperties = getConverterProperties(cookie);Document document = HtmlConverter.convertToDocument(html, pdfDoc, converterProperties);// flush触发写操作,此时才会触发已经注册的事件处理器document.flush();// 待document对象写完后,才能开始写入总页码LOGGER.debug("PDF总页数:" + document.getPdfDocument().getNumberOfPages());footerHandler.writeTotal(pdfDoc);document.close();} catch (Throwable t) {LOGGER.error("生成PDF失败:" + destPath, t);return false;}return true;}public static ConverterProperties getConverterProperties(String cookie) {ConverterProperties converterProperties = new ConverterProperties();FontProvider fontProvider = getFontProvider();converterProperties.setFontProvider(fontProvider);CookieResourceRetriever myResourceRetriever = new CookieResourceRetriever(cookie);converterProperties.setResourceRetriever(myResourceRetriever);return converterProperties;}public void writeTotal(PdfDocument pdf) {Canvas canvas = new Canvas(placeholder, pdf);canvas.setFontSize(8);if (font != null) {// 设置支持中文canvas.setFont(this.font);// 在占位符写入总页数canvas.showTextAligned(String.format("/共[%d]页", pdf.getNumberOfPages()), 0, descent,TextAlignment.LEFT);}canvas.close();}

2.2、工具类:

public class HtmlPrittifier {/*** 将HTML标准化,补全缺失的闭标签** @param in* @param out*/public static void fixHtml(InputStream in, OutputStream out) {// obtain a new Tidy instanceTidy tidy = new Tidy();// set desired config options using tidy setterstidy.setXHTML(true);tidy.setInputEncoding("utf8");tidy.setShowWarnings(true);tidy.setWraplen(1024);tidy.setSmartIndent(true);tidy.setQuiet(true);tidy.setPrintBodyOnly(false);tidy.setOutputEncoding("utf8");tidy.setTidyMark(false);// output document even if errors were found. 防止有些自定义tag匹配不上导致pdf不输出tidy.setForceOutput(true);// 不转换uri,防止uri中有中文,会将非ascii码转为十六进制,导致找不到中文图片链接。tidy.setFixUri(false);tidy.parse(in, out);}/*** 将HTML标准化,补全缺失的闭标签* * @param htmlString*/@SneakyThrowspublic static String fixHtml(String htmlString) {ByteArrayInputStream in = new ByteArrayInputStream(htmlString.getBytes("UTF-8"));ByteArrayOutputStream out = new ByteArrayOutputStream();fixHtml(in, out);return out.toString();}}

2.3、水印实现方法

private static PdfFont createDefaultFont() throws IOException {return PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
}protected static class PdfWaterMarker implements IEventHandler {private PdfFont font;private String waterContent;public PdfWaterMarker(String waterContent) {this.waterContent = waterContent;try {this.font = createDefaultFont();} catch (IOException e) {LOGGER.error("PDF Header设置中文字体失败", e);}}@Overridepublic void handleEvent(Event event) {PdfDocumentEvent docEvent = (PdfDocumentEvent) event;PdfDocument pdfDoc = docEvent.getDocument();PdfPage page = docEvent.getPage();Rectangle pageSize = page.getPageSize();PdfCanvas pdfCanvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), pdfDoc);Canvas canvas = new Canvas(pdfCanvas, pageSize);// 设置水印文字内容Paragraph waterMarker = new Paragraph(waterContent).setFont(font).setOpacity(0.15f)// 设置透明度.setFontSize(16);// 文字大小/*for (int i = 0; i < 6; i++) {for (int j = 0; j < 6; j++) {canvas.showTextAligned(waterMarker, (150 + i * 300), (160 + j * 150), pdfDoc.getNumberOfPages(),TextAlignment.CENTER, VerticalAlignment.MIDDLE, 0.3f);}}*/// 计算水印的起始位置和间隔float xStart = 1;float yStart = 1;float stepX = 150;float stepY = 150;// 在页面上循环绘制水印文字for (float x = xStart; x < pageSize.getWidth(); x += stepX) {for (float y = yStart; y < pageSize.getHeight(); y += stepY) {// 绘制水印文字canvas.showTextAligned(waterMarker, x, y, pdfDoc.getNumberOfPages(),TextAlignment.LEFT, VerticalAlignment.MIDDLE, 0.6f);}}// 关闭流canvas.close();}}

2.4、页眉实现方法

private static PdfFont createDefaultFont() throws IOException {return PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
}protected static class Header implements IEventHandler {private String header;private PdfFont font;public Header(String header) {this.header = header;try {this.font = createDefaultFont();} catch (IOException e) {LOGGER.error("PDF Header设置中文字体失败", e);}}@Overridepublic void handleEvent(Event event) {PdfDocumentEvent docEvent = (PdfDocumentEvent)event;PdfDocument pdf = docEvent.getDocument();PdfPage page = docEvent.getPage();Rectangle pageSize = page.getPageSize();Document doc = new Document(pdf);float leftMargin = doc.getLeftMargin();float rightMargin = doc.getRightMargin();float bottomMargin = doc.getBottomMargin();float topMargin = doc.getTopMargin();float height = pageSize.getHeight();float width = pageSize.getWidth();Canvas canvas = new Canvas(new PdfCanvas(page), pageSize);if (font != null) {// 设置支持中文canvas.setFont(this.font);}canvas.setFontSize(8);// Creates drawing canvasPdfCanvas pdfCanvas = new PdfCanvas(page);pdfCanvas.setLineWidth(0.1f);pdfCanvas.moveTo(leftMargin, height - topMargin + 3).lineTo(width - rightMargin, height - topMargin + 3).stroke();// Write text at positioncanvas.showTextAligned(header, width / 2, height - 30, TextAlignment.CENTER);canvas.close();}}

2.5、页脚实现方法

protected static class Footer implements IEventHandler {// 写总页码的占位符protected PdfFormXObject placeholder;// 页脚占位符大小private float side = 36;// 页脚占位符位置向右调整移动1,向下调整移动3private float space = 1;private float descent = 3;private PdfFont font;public Footer() {this.placeholder = new PdfFormXObject(new Rectangle(0, 0, side, side));try {this.font = createDefaultFont();} catch (IOException e) {LOGGER.error("PDF Footer设置中文字体失败", e);}}@Overridepublic void handleEvent(Event event) {PdfDocumentEvent docEvent = (PdfDocumentEvent)event;PdfDocument pdf = docEvent.getDocument();PdfPage page = docEvent.getPage();int pageNumber = pdf.getPageNumber(page);Rectangle pageSize = page.getPageSize();LOGGER.debug(String.format("当前处理第[%d]页", pageNumber));Document doc = new Document(pdf);float leftMargin = doc.getLeftMargin();float rightMargin = doc.getRightMargin();float bottomMargin = doc.getBottomMargin();float height = page.getPageSize().getHeight();float width = page.getPageSize().getWidth();// 页脚的位置float x = width / 2;float y = bottomMargin / 2;// Creates drawing canvasPdfCanvas pdfCanvas = new PdfCanvas(page);Canvas canvas = new Canvas(pdfCanvas, pageSize);if (font != null) {// 设置支持中文canvas.setFont(this.font);}canvas.setFontSize(8);// 设置支持横线// canvas.setStrokeWidth(0.1f);pdfCanvas.setLineWidth(0.1f);pdfCanvas.moveTo(leftMargin, bottomMargin - 3).lineTo(width - rightMargin, bottomMargin - 3).stroke();// 设置支持页码Paragraph p = new Paragraph().add(String.format("第[%d]页", pageNumber));canvas.showTextAligned(p, x, y, TextAlignment.RIGHT);canvas.close();// 添加占位符,用于写入总页码pdfCanvas.addXObjectAt(placeholder, x + space, y - descent);pdfCanvas.release();}

三、jsp实现水印(不影响之前已经生成模板的代码)

<style>#watermark div {/* color: rgba(255, 0, 0, .1); */color: rgba(0, 0, 255, .1);position: absolute;font-size: 24px;white-space: nowrap;transform: rotate(-30deg);-ms-transform: rotate(-30deg); /* IE 9 */-moz-transform: rotate(-30deg); /* Firefox */-webkit-transform: rotate(-30deg); /* Safari 和 Chrome */-o-transform: rotate(-30deg); /* Opera */}
</style>
<div id="watermark"style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: transparent; pointer-events: none; z-index: 99999999;">
</div>
<script>var operatorName = '${requestScope.xx.xxx}';var erpId = '${requestScope.xx.xxx}';(function() {var w = screen.width;var h = screen.height;var r = Math.sqrt(w * w + h * h);var i = 0;var j = 0;var watermark = document.querySelector('#watermark');var div;var top, left;for (var i = 0; i < 10; ++i) {for (var j = 0; j < 10; ++j) {div = document.createElement('div');div.innerText = '禁止外传:' + operatorName + '(' + erpId + ')';top = i * 100 + 'px';left = 500 * j - 50 - (i % 2 == 0 ? 250 : 0) + 'px';div.style.setProperty('top', top);div.style.setProperty('left', left);watermark.appendChild(div);}}})();
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_1027927.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大话设计模式之简单工厂模式

简单工厂模式&#xff08;Simple Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;属于工厂模式的一种。在简单工厂模式中&#xff0c;有一个工厂类负责根据输入参数的不同来创建不同类的实例。 简单工厂模式包含以下几个要素&#xff1a; 1. **工厂类&#xff0…

答题小程序功能细节揭秘:如何提升用户体验和满足用户需求?

答题小程序功能细节体现 随着移动互联网的快速发展&#xff0c;答题小程序成为了用户获取知识、娱乐休闲的重要平台。一款优秀的答题小程序不仅应该具备简洁易用的界面设计&#xff0c;更应该在功能细节上做到极致&#xff0c;以提升用户体验和满足用户需求。本文将从题库随机…

【大数据运维】minio 常见shell操作

文章目录 1. 安装2. 入门操作3. 命令帮助 1. 安装 下载 https://dl.min.io/client/mc/release/linux-amd64/ 赋权与使用 cp mc /usr/bin && chmod x /usr/bin/mc ./mc --help 2. 入门操作 # 添加minio到mc mc config host add minio_alias_name endpoint_adress …

网络安全-文件包含

一、php://input 我们先来看一个简单的代码 <meta charset"utf8"> <?php error_reporting(0); $file $_GET["file"]; if(stristr($file,"php://filter") || stristr($file,"zip://") || stristr($file,"phar://&quo…

广和通发布基于高通高算力芯片的具身智能机器人开发平台Fibot

3月29日&#xff0c;为助力机器人厂商客户快速复现及验证斯坦福Mobile ALOHA机器人的相关算法&#xff0c;广和通发布具身智能机器人开发平台Fibot。作为首款国产Mobile ALOHA机器人的升级配置版本&#xff0c;开发平台采用全向轮底盘设计、可拆卸式训练臂结构&#xff0c;赋予…

ActiveMQ Artemis 系列| High Availability 主备模式(消息复制) 版本2.19.1

一、ActiveMQ Artemis 介绍 Apache ActiveMQ Artemis 是一个高性能的开源消息代理&#xff0c;它完全符合 Java Message Service (JMS) 2.0 规范&#xff0c;并支持多种通信协议&#xff0c;包括 AMQP、MQTT、STOMP 和 OpenWire 等。ActiveMQ Artemis 由 Apache Software Foun…

PDF转成二维码分享

在制作电子产品册之前&#xff0c;你需要思考以下几个问题&#xff1a;你的电子产品册是面向什么人群的&#xff1f;是宣传册、使用手册还是产品介绍册&#xff1f;明确目标与定位有助于我们更好地规划产品册的内容和风格。 一、收集素材与整理信息 在开始制作之前&#xff0c…

AI 异构计算机设计方案:902-基于6U VPX 高带宽PCIe的GPU AI 异构计算机

基于6U VPX 高带宽PCIe的GPU AI 异构计算机 一、产品概述 基于6U 6槽 VPX 高带宽PCIe的GPU AI 异构计算机以PCIe总线为架构&#xff0c;通过高带宽的PCIe互联&#xff0c;实现主控计算板、GPU AI板卡&#xff0c;FPGA接口板&#xff0c;存储板的PCIe高带宽互联访问&…

是德科技安捷伦 E5052B信号源分析仪

181/2461/8938产品概述&#xff1a; Keysight E5052B&#xff08;安捷伦&#xff09;信号源分析仪&#xff0c;10 MHz 至 7 GHz&#xff0c;具有许多增强的性能特性。它在表征 VCO 或其他类型的高频信号源以及高速数据通信系统中的时钟抖动评估方面提供了世界上最高的测量吞吐…

SpringBoot中的异步多线程使用及避坑指南

处理请求时需要考虑到系统的性能和响应速度。特别是在处理大量请求或者需要进行耗时操作时&#xff0c;采用异步多线程处理是一种常见的解决方案。Spring Boot提供了Async注解来支持异步方法调用&#xff0c;结合合适的线程池配置&#xff0c;可以很容易地实现异步多线程处理&a…

SQL Server 实验二:数据库视图的创建和使用

目录 第一关 相关知识 什么是表 操作数据表 创建数据表 插入数据 修改表结构 删除数据表 编程要求 第一关实验代码&#xff1a; 第二关 相关知识 视图是什么 视图的优缺点 视图的优点 视图的缺点 操作视图 创建视图 通过视图向基本表中插入数据 通过视图修改基本表的…

是德科技KEYSIGHT E5071C网络分析仪

181/2461/8938产品概述&#xff1a; Keysight E5071C&#xff08;安捷伦&#xff09;网络分析仪提供同类产品中最高的射频性能和最快的速度&#xff0c;并具有宽频率范围和多功能功能。E5071C 是制造和研发工程师评估频率范围高达 20 GHz 的射频元件和电路的理想解决方案。 有…

阿里云2024年优惠券(代金券)领取方法汇总

随着云计算技术的不断发展&#xff0c;阿里云作为国内领先的云服务提供商&#xff0c;为广大用户提供了高效、稳定的云服务。为了回馈用户&#xff0c;阿里云会定期发放各种优惠券&#xff0c;帮助用户节省上云成本。本文将为大家汇总阿里云2024年优惠券的领取方法&#xff0c;…

几种Yolo图像标注工具

Yolo可以识别的标注为txt 1.LabelImg 最常用&#xff0c;但经常莫名其妙地闪退&#xff0c;稳定性差 2.Yolo_Label 我都打算自己写程序了&#xff0c;网上找到了这个工具&#xff0c;看来早有人无法忍受现存的标记工具&#xff0c;自己动手写了个标注工具&#xff0c;比Label…

安防监控视频汇聚平台EasyCVR在银河麒麟V10系统中的启动异常及解决方法

安防监控视频平台EasyCVR具备较强的兼容性&#xff0c;它可以支持国标GB28181、RTSP/Onvif、RTMP&#xff0c;以及厂家的私有协议与SDK&#xff0c;如&#xff1a;海康ehome、海康sdk、大华sdk、宇视sdk、华为sdk、萤石云sdk、乐橙sdk等。平台兼容性强&#xff0c;支持Windows系…

钡铼技术R40工业路由器赋能智慧电网实现远程智能采集数据

在当今的智能化时代&#xff0c;智慧电网作为能源互联网的重要基础设施&#xff0c;其建设和升级离不开先进的通信技术和设备的支持。钡铼技术R40工业路由器凭借其强大的数据传输能力、稳定可靠的性能以及卓越的环境适应性&#xff0c;在赋能智慧电网实现远程智能采集数据方面发…

1. Java概述

文章目录 1.Java语言概述1.1 Java介绍1.1.1 软件开发概述1.1.2 计算机语言1.1.3 Java 简史1.1.4 Java 技术体系平台1.1.5 Java在各领域的应用1.1.6 Java语言特点1.1.7 Java核心机制一-Java虚拟机1.1.8 Java核心机制二-垃圾回收1.1.9 Java开发工具 1.2 Java环境搭建1.2.1 JDK、J…

ElasticSearch理论指导

引子 本文致力于ElasticSearch理论体系构建&#xff0c;从基本概念和术语讲起&#xff0c;具体阐述了倒排索引和TransLog&#xff0c;接着讲了ElasticSearch的增删改查的流程和原理&#xff0c;最后讲了讲集群的选举和脑裂问题。 前言 大碗宽面-Kafka一本道万事通&#xff0…

设计模式-设配器模式

目录 &#x1f38a;1.适配器模式介绍 &#x1f383;2.适配器类型 &#x1f38f;3.接口适配器 &#x1f390;4.类的适配器 &#x1f38e;5.优缺点 1.适配器模式介绍 适配器模式&#xff08;Adapter Pattern&#xff09;是作为两个不兼容的接口之间的桥梁。这种类型的设…

自动化测试 —— Pytest fixture及conftest详解

前言 fixture是在测试函数运行前后&#xff0c;由pytest执行的外壳函数。fixture中的代码可以定制&#xff0c;满足多变的测试需求&#xff0c;包括定义传入测试中的数据集、配置测试前系统的初始状态、为批量测试提供数据源等等。fixture是pytest的精髓所在&#xff0c;类似u…