当前位置: 首页 > news >正文

Java / Spring Boot + POI 给 Word 添加水印

1、前言(瞎扯)

有个需求:整一个给 Word 加水印的demo,于是我就网上找呗~
看到那个 Aspose 好像是收费的,然后就把目光转向了 POI,看到各种形形色色的也不知道哪个能用。整了一会,自己拷贝出一个比较精简的能用的 demo 了。

2、人狠话不多,上效果图

我一般都是直接上图的,先看效果图(每一页都有的):

水印的分布如果不理想,只能小伙伴们自行研究调整了~

在这里插入图片描述

3、人狠话不多,直接来代码

3.1、我的代码结构

目录结构

3.2 、直接贴代码了

pom 依赖的版本不要改,修改版本可能会导致一些东西缺失
代码你们可以直接复制这里的使用
或者在码云仓库:点击这里跳转

3.2.1、pom 依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.2</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.lyk</groupId><artifactId>springboot-word-finger</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot-word-finger</name><description>springboot-word-finger</description><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.11.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.0</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId><version>4.1.0</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.0</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>4.1.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

3.2.2、处理工具类

import com.microsoft.schemas.office.office.CTLock;
import com.microsoft.schemas.vml.*;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.wp.usermodel.HeaderFooterType;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.*;
import java.util.stream.Stream;/*** @author: lyk* @description: Word 添加水印工具类**/
public class WatermarkUtil {private static final Logger LOGGER = LoggerFactory.getLogger(WatermarkUtil.class);/** word字体 */private static final String FONT_NAME = "宋体";/** 字体大小 */private static final String FONT_SIZE = "0.2pt";/** 字体颜色 */private static final String FONT_COLOR = "#d0d0d0";/** 一个字平均长度,单位pt,用于:计算文本占用的长度(文本总个数*单字长度)*/private static final Integer WIDTH_PER_WORD = 10;/** 与顶部的间距 */private static Integer STYLE_TOP = 0;/** 文本旋转角度 */private static final String STYLE_ROTATION = "30";/*** @param inPutPath* @param putPutPath* @param fingerText* @author: lyk* @description: 添加水印入口方法* @date: 2024/1/25 23:42**/public static void waterMarkDocXDocument(String inPutPath, String putPutPath, String fingerText) {long beginTime = System.currentTimeMillis();try (OutputStream out = new FileOutputStream(putPutPath);InputStream in = new FileInputStream(inPutPath);OPCPackage srcPackage = OPCPackage.open(in);XWPFDocument doc = new XWPFDocument(srcPackage)) {// 把整页都打上水印for (int lineIndex = -5; lineIndex < 20; lineIndex++) {STYLE_TOP = 100 * lineIndex;waterMarkDocXDocument(doc, fingerText);}// 输出新文档doc.write(out);LOGGER.info("添加水印成功!,一共耗时" + (System.currentTimeMillis() - beginTime) + "毫秒");} catch (IOException e) {throw new RuntimeException(e);} catch (InvalidFormatException e) {throw new RuntimeException(e);}}/*** 为文档添加水印* @param doc        需要被处理的docx文档对象* @param fingerText 需要添加的水印文字*/public static void waterMarkDocXDocument(XWPFDocument doc, String fingerText) {// 水印文字之间使用8个空格分隔fingerText = fingerText + repeatString(" ", 8);// 一行水印重复水印文字次数fingerText = repeatString(fingerText, 10);// 如果之前已经创建过 DEFAULT 的Header,将会复用XWPFHeader header = doc.createHeader(HeaderFooterType.DEFAULT);int size = header.getParagraphs().size();if (size == 0) {header.createParagraph();}CTP ctp = header.getParagraphArray(0).getCTP();byte[] rsidr = doc.getDocument().getBody().getPArray(0).getRsidR();byte[] rsidrDefault = doc.getDocument().getBody().getPArray(0).getRsidRDefault();ctp.setRsidP(rsidr);ctp.setRsidRDefault(rsidrDefault);CTPPr ppr = ctp.addNewPPr();ppr.addNewPStyle().setVal("Header");// 开始加水印CTR ctr = ctp.addNewR();CTRPr ctrpr = ctr.addNewRPr();ctrpr.addNewNoProof();CTGroup group = CTGroup.Factory.newInstance();CTShapetype shapeType = group.addNewShapetype();CTTextPath shapeTypeTextPath = shapeType.addNewTextpath();shapeTypeTextPath.setOn(STTrueFalse.T);shapeTypeTextPath.setFitshape(STTrueFalse.T);CTLock lock = shapeType.addNewLock();lock.setExt(STExt.VIEW);CTShape shape = group.addNewShape();shape.setId("PowerPlusWaterMarkObject");shape.setSpid("_x0000_s102");shape.setType("#_x0000_t136");// 设置形状样式(旋转,位置,相对路径等参数)shape.setStyle(getShapeStyle(fingerText));shape.setFillcolor(FONT_COLOR);// 字体设置为实心shape.setStroked(STTrueFalse.FALSE);// 绘制文本的路径CTTextPath shapeTextPath = shape.addNewTextpath();// 设置文本字体与大小shapeTextPath.setStyle("font-family:" + FONT_NAME + ";font-size:" + FONT_SIZE);shapeTextPath.setString(fingerText);CTPicture pict = ctr.addNewPict();pict.set(group);}/*** 构建Shape的样式参数** @param fingerText* @return*/private static String getShapeStyle(String fingerText) {StringBuilder sb = new StringBuilder();// 文本path绘制的定位方式sb.append("position: ").append("absolute");// 计算文本占用的长度(文本总个数*单字长度)sb.append(";width: ").append(fingerText.length() * WIDTH_PER_WORD).append("pt");// 字体高度sb.append(";height: ").append("20pt");sb.append(";z-index: ").append("-251654144");sb.append(";mso-wrap-edited: ").append("f");// 设置水印的间隔,这是一个大坑,不能用top,必须要margin-top。sb.append(";margin-top: ").append(STYLE_TOP);sb.append(";mso-position-horizontal-relative: ").append("page");sb.append(";mso-position-vertical-relative: ").append("page");sb.append(";mso-position-vertical: ").append("left");sb.append(";mso-position-horizontal: ").append("center");sb.append(";rotation: ").append(STYLE_ROTATION);return sb.toString();}/*** 将指定的字符串重复repeats次.*/private static String repeatString(String pattern, int repeats) {StringBuilder buffer = new StringBuilder(pattern.length() * repeats);Stream.generate(() -> pattern).limit(repeats).forEach(buffer::append);return new String(buffer);}
}
/*** @author lyk* @version 1.0* @date 2024/1/25 23:16* @description*/
public class Main {public static void main(String[] args) {final String inPath = "src/main/java/com/lyk/finger/doc/aaaa.docx";final String outPath = "src/main/java/com/lyk/finger/doc/out.docx";// 添加水印WatermarkUtil.waterMarkDocXDocument(inPath, outPath, "落魄程序员在线炒粉");}}

4、OK 完事~

拿去好好享用吧~

相关文章:

Java / Spring Boot + POI 给 Word 添加水印

1、前言(瞎扯) 有个需求&#xff1a;整一个给 Word 加水印的demo&#xff0c;于是我就网上找呗~ 看到那个 Aspose 好像是收费的&#xff0c;然后就把目光转向了 POI&#xff0c;看到各种形形色色的也不知道哪个能用。整了一会&#xff0c;自己拷贝出一个比较精简的能用的 demo …...

Unity打包Android,jar文件无法解析的问题

Unity打包Android&#xff0c;jar无法解析的问题 介绍解决方案总结 介绍 最近在接入语音的SDK时&#xff0c;发现的这个问题. 当我默认导入这个插件的时候&#xff0c;插件内部的文件夹&#xff08;我下面话红框的文件夹&#xff09;名字原本为GCloudVoice&#xff0c;这时候我…...

postman之接口参数签名(js接口HMAC-SHA256签名)

文章目录 postman之接口参数签名&#xff08;js接口签名&#xff09;一、需求背景二、签名生成规则三、postman js接口签名步骤1. postman设置全局、或环境参数2. 配置Pre-request Scripts脚本 四、Pre-request Scripts脚本 常见工作整理1. js获取unix时间戳2. body json字符串…...

从c到c++——6:auto

在编写c程序时&#xff0c;需要在初始化变量时清楚地知道该变量的数据类型&#xff0c;有时这到这一点并不容易&#xff0c;在涉及到函数指针&#xff0c;多级指针时往往很难一下子给出准确的值。使用auto关键字很好的提高编程效率。 auto关键字会根据右边的类型自动生成适合的…...

前端面试题:字符串中字符出现的最多次数

前端基础算法面试题,一个字符串中,出现最多的字符以及出现的次数。 1.首先对字符串转换成字符串数组,然后对字符串数组进行排序,得到一个有序的数组 2.然后对排序后的字符串数组,进行查找 3.每次找到字符出现的最后的位置,然后进行计数 4.得到最终结果 function get…...

获取鼠标点击图片时候的坐标,以及利用html 中的useMap 和area 实现图片固定位置的点击事件

一 编写原因 应项目要求&#xff0c;需要对图片的固定几个位置分别做一个点击事件&#xff0c;响应不同的操作&#xff0c;如下图&#xff0c;需要点击红色区域&#xff0c;弹出不同的提示框&#xff1a; 二 获取点击图片时候的坐标 1. 说明 实现这以上功能的前提是需要确定需…...

webassembly003 TTS BARK.CPP

TTS task TTS&#xff08;Text-to-Speech&#xff09;任务是一种自然语言处理&#xff08;NLP&#xff09;任务&#xff0c;其中模型的目标是将输入的文本转换为声音&#xff0c;实现自动语音合成。具体来说&#xff0c;模型需要理解输入的文本并生成对应的语音输出&#xff0…...

HiveSQL题——排序函数(row_number/rank/dense_rank)

一、窗口函数的知识点 1.1 窗户函数的定义 窗口函数可以拆分为【窗口函数】。窗口函数官网指路&#xff1a; LanguageManual WindowingAndAnalytics - Apache Hive - Apache Software Foundationhttps://cwiki.apache.org/confluence/display/Hive/LanguageManual%20Windowin…...

【C语言】(9)分支结构

一.if-else 语句 if-else 适用于简单和复杂的条件判断。 a. 基本 if 语句 用途&#xff1a;基本的条件测试。语法&#xff1a;if (condition) {// 代码块 }示例&#xff1a;if (score > 60) {printf("及格\n"); }b. if-else 语句 用途&#xff1a;二选一的条件…...

Flink 集成 Debezium Confluent Avro ( format=debezium-avro-confluent )

博主历时三年精心创作的《大数据平台架构与原型实现:数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行,点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,京东购书链接:https://item.jd.com/12677623.html,扫描左侧二维…...

R语言(数据导入,清洗,可视化,特征工程,建模)

记录一下痛失的超级轻松的数据分析实习&#xff08;线上&#xff09;&#xff0c;hr问我有没有相关经历&#xff0c;我说我会用jupyter book进行数据导入&#xff0c;清洗&#xff0c;可视化&#xff0c;特征工程&#xff0c;建模&#xff0c;python学和用的比较多&#xff0c;…...

springboot 整合 Activiti6

1.添加maven依赖 <dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter-basic</artifactId><version>6.0.0</version> </dependency>2.添加配置 spring:activiti:check-process-definitio…...

微信小程序canvas画布实现直线自由缩放、移动功能

目录 实现效果 一、获取画布信息并绘制背景 二、绘制直线...

Cesium数据加载

文章目录 0.引言1.影像加载1.1Bing地图1.2天地图1.3ArcGIS在线地图1.4高德地图1.5OSM影像1.6MapBox影像 2.OGC地图服务2.1WMS2.2WMTS2.3TMS 3.GeoJSON数据加载4.KML数据加载5.TIFF数据加载6.点云数据加载7.地形数据加载7.1在线地形数据加载7.2本地地形数据加载 8.倾斜摄影模型数…...

【C++历练之路】探秘C++三大利器之一——多态

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; 前言&#x1f354;: 在计算机科学的广袤领域中&#xff0c;C多态性是一门令人着迷的技术艺术&#xff0c;它赋予我们的代码更强大的灵活性和可维护性。想象一下&#xff0c;你正在构建一个程序&#xff0c;需要适应不断…...

业务逐字稿

1.WEB端旅游线路发布模块 旅游公司在Web端点击新增旅游线路按钮&#xff0c;浏览器请求发送到Nginx&#xff0c;Nginx反向代理到网关&#xff0c;网关去找微服务&#xff0c;微服务实现具体的旅游线路发布功能 旅游公司工作人员在Web端点击新增旅游线路按钮&#xff0c;浏览器…...

微服务舞台上的“三步曲“:Spring Cloud 服务注册、服务发现与服务调用

在当今软件开发的舞台上&#xff0c;微服务架构已然成为引领潮流的主角。而在这场微服务的大戏中&#xff0c;Spring Cloud 以其强大的工具集成为关键演员&#xff0c;为我们呈现了一个完美的"三步曲"&#xff1a;服务注册、服务发现与服务调用。 第一步&#xff1a…...

中间件

在 Java 开发中&#xff0c;中间件是指位于应用程序和操作系统之间的软件层&#xff0c;它提供了一些通用的功能和服务&#xff0c;帮助简化开发和部署过程&#xff0c;提高系统的可靠性、性能和可扩展性。 常见的 Java 中间件包括&#xff1a; 1.应用服务器&#xff08;Appl…...

4D毫米波雷达——ADCNet 原始雷达数据 目标检测与可行驶区域分割

前言 本文介绍使用4D毫米波雷达&#xff0c;基于原始雷达数据&#xff0c;实现目标检测与可行驶区域分割&#xff0c;它是来自2023-12的论文。 会讲解论文整体思路、输入分析、模型框架、设计理念、损失函数等&#xff0c;还有结合代码进行分析。 论文地址&#xff1a;ADCNe…...

「优选算法刷题」:提莫攻击

一、题目 在《英雄联盟》的世界中&#xff0c;有一个叫 “提莫” 的英雄。他的攻击可以让敌方英雄艾希&#xff08;编者注&#xff1a;寒冰射手&#xff09;进入中毒状态。 当提莫攻击艾希&#xff0c;艾希的中毒状态正好持续 duration 秒。 正式地讲&#xff0c;提莫在 t 发…...

260:vue+openlayers 通过webgl方式加载矢量图层

第260个 点击查看专栏目录 本示例介绍如何在vue+openlayers中通过webgl方式加载矢量图层。在做这个示例的时候,采用vite的方式而非webpack的方式。这里的基础设置需要改变一下。 ol的版本7.5.2或者更高。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果 文…...

Android 8.1 相关修改

一些常用修改&#xff0c;做个记录&#xff0c;为了节约时间和防止踩坑。 一、修改默认中文 修改位置&#xff1a; build\make\target\product\full_base.mk 修改内容&#xff1a; # Put en_US first in the list, so make it default. PRODUCT_LOCALES : zh_…...

EG-2121CA (晶体振荡器 低抖动表面声波(SAW)振荡器)

在当今高度数字化的时代&#xff0c;稳定的信号传输显得尤为重要。若要实现信号的稳定传输&#xff0c;晶体振荡器必不可少。EG-2121CA&#xff0c;它是一款低抖动表面声波&#xff08;SAW&#xff09;振荡器设计的产品&#xff0c;凭借其出色的频率范围、稳定的电源电压和可靠…...

Django知识随笔

目录 1.如何再ajax中传输post数据&#xff1f; 2.在form表单中使用jquery序列化&#xff0c;input框过多。 1.如何再ajax中传输post数据&#xff1f; 在ajax传递的那个网址&#xff0c;会调用你路由的视图函数&#xff0c;在视图函数上面加一句 csrf_exempt 。写上之后会有提…...

Facebook 广告帐户:多账号运营如何防止封号?

Facebook目前是全球最受欢迎的社交媒体平台之一&#xff0c;拥有超过27亿活跃用户。因此&#xff0c;它已成为个人和企业向全球受众宣传其产品和服务的重要平台。 然而&#xff0c;Facebook 制定了广告商必须遵守的严格政策和准则&#xff0c;以确保其广告的质量和相关性&…...

酷开会员 | 用酷开系统点亮多彩休闲时刻

现代人的休闲方式多种多样&#xff0c;随着科技的发展和生活水平的提高&#xff0c;人们有了更多的选择。而电视&#xff0c;作为一个休闲娱乐的载体&#xff0c;在生活中扮演着重要的角色。 以前&#xff0c;电视是家家户户休闲娱乐的重要组成部分&#xff0c;现在&#xff0…...

分配土地(100%用例)C卷(JavaPythonC++Node.jsC语言)

从前有个村庄,村民们喜欢在各种田地上插上小旗子,旗子上标识了各种不同的数字。某天集体村民决定将覆盖相同数字的最小矩阵形的土地的分配给为村里做出巨大贡献的村民,请问,此次分配士地,做出贡献的村民中最大会分配多大面积? 输入描述: 第一行输入m和n,m代表村子的土…...

电源的纹波

【电源的“纹波”到底是什么&#xff1f;-哔哩哔哩】 https://b23.tv/28IIbtU 严谨的说&#xff0c;只有中间这种是纹波。 两类电源 线性电源通过自动调节内部电阻&#xff0c;修改输出电压&#xff0c;可以降低输入电压的波动。开关电源的原理&#xff0c;就会产生纹波。纹波…...

85.网游逆向分析与插件开发-物品使用-物品使用的逆向分析与C++代码的封装

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;项目需求与需求拆解-CSDN博客 码云地址&#xff08;ui显示角色数据 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;453dd83d54140d2e1ee65c9…...

JVM系列——垃圾收集器

对象存活判断 引用计数法 在对象中添加一个引用计数器&#xff0c;每当有一个地方引用它时&#xff0c;计数器值就加一&#xff1b;当引用失效时&#xff0c;计数器值就减一&#xff1b;任何时刻计数器为零的对象就是不可能再被使用的。 可达性分析算法 通过一系列称为“GC …...