asp做的网站,js网站开发工具,好的网站分享,seo黑帽教程视频在 Java 中使用 Apache POI 为 Word 文档添加水印
在日常办公中#xff0c;我们经常需要给 Word 文档添加水印#xff0c;以标明文件的机密性或归属权。本文将介绍如何使用 Apache POI 库在 Java 中给 Word 文档添加水印。
技术栈
Apache POI#xff1a;用于操作 Word 文…在 Java 中使用 Apache POI 为 Word 文档添加水印
在日常办公中我们经常需要给 Word 文档添加水印以标明文件的机密性或归属权。本文将介绍如何使用 Apache POI 库在 Java 中给 Word 文档添加水印。
技术栈
Apache POI用于操作 Word 文档.docxVML矢量标记语言用于绘制水印文本
实现思路
读取 Word 文档在页眉中插入水印处理水印样式如字体、颜色、透明度等生成新文件并保存
代码实现
以下是一个完整的 WordWatermarkUtils 工具类它支持向 .docx 文件中添加水印。
public class WordWatermarkUtils {private String customText; // 水印文字private String fontName 微软雅黑; // 字体private int fontSize 14; // 字体大小private String fontColor #616161; // 字体颜色private String styleRotation 0; // 旋转角度public WordWatermarkUtils(String customText) {this.customText customText;}public void addWatermarkToDoc(XWPFDocument doc) {XWPFHeader header doc.createHeader(HeaderFooterType.DEFAULT);CTP ctp header.createParagraph().getCTP();CTR ctr ctp.addNewR();CTGroup group CTGroup.Factory.newInstance();CTShape shape group.addNewShape();shape.setStyle(getShapeStyle());shape.setFillcolor(fontColor);shape.addNewTextpath().setString(customText);ctr.addNewPict().set(group);}private String getShapeStyle() {return position:absolute;width:300pt;height:50pt;rotation: styleRotation ;fill-opacity:0.3;}
}如何使用
try (FileInputStream fis new FileInputStream(input.docx);FileOutputStream fos new FileOutputStream(output.docx)) {XWPFDocument doc new XWPFDocument(fis);WordWatermarkUtils watermark new WordWatermarkUtils(机密文件);watermark.addWatermarkToDoc(doc);doc.write(fos);
}完整代码
package com.demo;import com.microsoft.schemas.office.office.CTLock;
import com.microsoft.schemas.vml.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.poi.wp.usermodel.HeaderFooterType;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import weaver.general.BaseBean;import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.UUID;/*** 微软office Word 水印机.*/
public class WordWatermarkUtils {private String customText; // 水印文字private String fontName 微软雅黑; // word字体private int fontSize 14; // 字体大小private String fontColor #616161; // 字体颜色private int widthPerWord 3; // 一个字平均长度单位pt用于计算文本占用的长度文本总个数*单字长度private String styleTop 10pt; // 与顶部的间距private String styleRotation 0; // 文本旋转角度如果不需要旋转水印保持为 0private String source; // 源文件路径private String dest D:\\WEAVER\\doc_tmp; // 临时文件存储路径BaseBean bean new BaseBean();public WordWatermarkUtils(String customText, String sourcePath) {customText customText repeatString( , 8); // 水印文字之间使用8个空格分隔this.customText repeatString(customText, 1); // 一行水印重复水印文字次数this.source sourcePath;}/*** 【核心方法】将输入流中的docx文档加载添加水印后输出到输出流中.** param inputStream docx文档输入流* param //outputStream 添加水印后docx文档的输出流*/public String makeSlopeWaterMark(InputStream inputStream, String filename) throws IOException {// 创建临时文件Path tempFile createTempFile(inputStream);if (tempFile null || !Files.exists(tempFile) || Files.size(tempFile) 0) {throw new RuntimeException(-------- 输入文件为空或者无法被创建);}Path sourcePath Paths.get(this.source); // 获取源文件路径Path destinationDir Paths.get(this.dest); // 将原文件复制到临时目标文件夹路径// 确保目标文件夹存在if (!Files.exists(destinationDir)) {try {Files.createDirectories(destinationDir);} catch (IOException e) {throw new RuntimeException(-------- 无法创建目标文件夹: e);}}String unescapedFilenameString StringEscapeUtils.unescapeHtml4(filename);// 转义字符复原String tmpFileName unescapedFilenameString.substring(0, unescapedFilenameString.lastIndexOf(.)) _watermark.docx;Path tmpPath destinationDir.resolve(tmpFileName); // 临时文件路径Files.copy(sourcePath, tmpPath, StandardCopyOption.REPLACE_EXISTING);bean.writeLog(-------- 通用合同右上角水印原文件复制成功并加上后缀 .docx: tmpPath);try (BufferedInputStream buffIn new BufferedInputStream(Files.newInputStream(tempFile));FileOutputStream fileOutputStream new FileOutputStream(tmpPath.toFile())) {// 加载文档XWPFDocument doc loadDocXDocument(buffIn, fileOutputStream);if (doc null) {throw new RuntimeException(-------- 加载文档失败);}// 添加水印try {// 遍历文档添加水印for (int lineIndex -10; lineIndex 10; lineIndex) {styleTop 200 * lineIndex pt;waterMarkDocXDocument(doc);}// 写出添加水印后的文档doc.write(fileOutputStream);// 关闭doc.close();buffIn.close();fileOutputStream.close();return tmpPath.toString();} catch (Exception e) {throw new RuntimeException(-------- 水印操作失败: e);}} catch (Exception e) {throw new RuntimeException(-------- 操作失败 e);} finally {// 删除原始文件// deleteFile(sourcePath);// 重命名临时文件为原文件名// try {// Files.move(tmpPath, sourcePath, StandardCopyOption.REPLACE_EXISTING);// } catch (IOException e) {// throw new RuntimeException(-------- 重命名失败: e);// }// 删除临时文件deleteFile(tempFile);}}/*** 为文档添加水印br /* 实现参考了{link org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy# getWatermarkParagraph(String, int)}** param doc 需要被处理的docx文档对象*/private void waterMarkDocXDocument(XWPFDocument doc) {int size doc.getHeaderList().size();if (size 0) {addWatermarkToHeader(doc);} else {// 遍历所有的节Sectionfor (Object header : doc.getHeaderList()) {// 保留原有内容for (Object paragraph : ((XWPFHeader) header).getParagraphs()) {// 保持原有段落内容((XWPFParagraph)paragraph).getText();}// 在页眉上添加水印具体代码见上文addWatermarkToHeader((XWPFHeader)header);}}}/*** 页眉存在其他内容* param header*/private void addWatermarkToHeader(XWPFHeader header) {int size header.getParagraphs().size();if (size 0) {header.createParagraph();}CTP ctp header.getParagraphArray(0).getCTP();byte[] rsidr ctp.getRsidR();byte[] rsidrdefault ctp.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()); // 设置形状样式旋转位置相对路径等参数shape.setFillcolor(fontColor);shape.setStroked(STTrueFalse.FALSE); // 字体设置为实心CTTextPath shapeTextPath shape.addNewTextpath(); // 绘制文本的路径shapeTextPath.setStyle(font-family: fontName ;font-size: fontSize pt); // 设置文本字体与大小shapeTextPath.setString(customText);CTPicture pict ctr.addNewPict();pict.set(group);}/*** 页眉不存在其他内容* param doc*/private void addWatermarkToHeader(XWPFDocument doc) {XWPFHeader header doc.createHeader(HeaderFooterType.DEFAULT); // 如果之前已经创建过 DEFAULT 的Header将会复用之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()); // 设置形状样式旋转位置相对路径等参数shape.setFillcolor(fontColor);shape.setStroked(STTrueFalse.FALSE); // 字体设置为实心CTTextPath shapeTextPath shape.addNewTextpath(); // 绘制文本的路径shapeTextPath.setStyle(font-family: fontName ;font-size: fontSize pt); // 设置文本字体与大小shapeTextPath.setString(customText);CTPicture pict ctr.addNewPict();pict.set(group);}/*** 构建Shape的样式参数.** return*/private String getShapeStyle() {StringBuilder sb new StringBuilder();sb.append(position: ).append(absolute); // 文本path绘制的定位方式sb.append(;width: ).append(customText.length() * widthPerWord).append(pt); // 计算文本占用的长度文本总个数*单字长度sb.append(;height: ).append(fontSize).append(pt); // 字体高度sb.append(;z-index: ).append(-251654144);// 控制层级sb.append(;mso-wrap-edited: ).append(f);sb.append(;margin-top: ).append(30);// 距离页面顶部10ptsb.append(;left: ).append(auto); // 防止左对齐sb.append(;margin-right: ).append(30); // 距离页面右侧10ptsb.append(;mso-position-horizontal-relative: ).append(page);// 水平基准为页面sb.append(;mso-position-vertical-relative: ).append(page);// 垂直基准为页面sb.append(;mso-position-horizontal: ).append(right); // // 水平居右//sb.append(;mso-position-vertical: ).append(bottom); // 设置水印在底部sb.append(;mso-position-vertical: ).append(other); // 垂直居上sb.append(;rotation: ).append(styleRotation);// 设置文本旋转角度sb.append(;fill-opacity: ).append(0.6); // 设置水印的透明度为 60%return sb.toString();}/*** 加载docx格式的word文档.** param inputStream* param outputStream* return*/private XWPFDocument loadDocXDocument(InputStream inputStream, OutputStream outputStream) {XWPFDocument doc;try {doc new XWPFDocument(inputStream);} catch (Exception e) {throw new RuntimeException(文档加载失败);}return doc;}/*** 创建临时文件.** param inputStream docx文档输入流*/private Path createTempFile(InputStream inputStream) {Path tempFilePath null;inputStream (inputStream null) ? new ByteArrayInputStream(new byte[0]) : inputStream; // 如果传入了null输入流转换成空数组流BufferedInputStream bufferedInputStream new BufferedInputStream(inputStream);bufferedInputStream.mark(0); // 输入流头部打上Mark方便重读// 创建临时文件try {if (inputStream null || inputStream.available() 0) {throw new RuntimeException(输入流为空或为 null.);}String uuid UUID.randomUUID().toString();tempFilePath Files.createTempFile(dapeng_ uuid, .docx);// 向临时文件写入数据try (OutputStream tempout Files.newOutputStream(tempFilePath)) {IOUtils.copy(inputStream, tempout);} catch (Exception e) { // 如果拷贝异常删除临时文件deleteFile(tempFilePath);throw new RuntimeException(写入临时文件时出错., e);}inputStream.close();} catch (Exception e) {// 这里表示创建临时文件失败tempFilePath null;}return tempFilePath;}/*** 删除指定path的文件.** param path*/private void deleteFile(Path path) {if (path ! null Files.exists(path)) {try {Files.deleteIfExists(path);} catch (IOException e) {bean.writeLog(-------- 删除文件失败: path e);}}}/*** 将指定字符串重复多次 (适配Java 1.8).*/private String repeatString(String pattern, int repeats) {StringBuilder buffer new StringBuilder(pattern.length() * repeats);for (int i 0; i repeats; i) {buffer.append(pattern);}return buffer.toString();}
}
总结
使用 Apache POI 和 VML我们可以轻松地在 Word 文档中添加水印。该方法适用于各种办公场景如合同文件、内部文档等。如果你有更复杂的需求如图片水印或动态水印也可以扩展此方法。
希望本文对你有所帮助欢迎交流讨论 文章转载自: http://www.morning.mgkb.cn.gov.cn.mgkb.cn http://www.morning.hffpy.cn.gov.cn.hffpy.cn http://www.morning.drggr.cn.gov.cn.drggr.cn http://www.morning.qhmgq.cn.gov.cn.qhmgq.cn http://www.morning.rtqyy.cn.gov.cn.rtqyy.cn http://www.morning.rfbpq.cn.gov.cn.rfbpq.cn http://www.morning.htjwz.cn.gov.cn.htjwz.cn http://www.morning.jhtrb.cn.gov.cn.jhtrb.cn http://www.morning.tfwr.cn.gov.cn.tfwr.cn http://www.morning.dnqpq.cn.gov.cn.dnqpq.cn http://www.morning.mnbcj.cn.gov.cn.mnbcj.cn http://www.morning.zfhwm.cn.gov.cn.zfhwm.cn http://www.morning.gftnx.cn.gov.cn.gftnx.cn http://www.morning.psgbk.cn.gov.cn.psgbk.cn http://www.morning.bsrp.cn.gov.cn.bsrp.cn http://www.morning.tdmr.cn.gov.cn.tdmr.cn http://www.morning.tdwjj.cn.gov.cn.tdwjj.cn http://www.morning.dhnqt.cn.gov.cn.dhnqt.cn http://www.morning.cbynh.cn.gov.cn.cbynh.cn http://www.morning.mwbqk.cn.gov.cn.mwbqk.cn http://www.morning.gcfrt.cn.gov.cn.gcfrt.cn http://www.morning.mhnxs.cn.gov.cn.mhnxs.cn http://www.morning.c7512.cn.gov.cn.c7512.cn http://www.morning.qwfq.cn.gov.cn.qwfq.cn http://www.morning.kphyl.cn.gov.cn.kphyl.cn http://www.morning.qjngk.cn.gov.cn.qjngk.cn http://www.morning.frfnb.cn.gov.cn.frfnb.cn http://www.morning.nqbpz.cn.gov.cn.nqbpz.cn http://www.morning.rgrys.cn.gov.cn.rgrys.cn http://www.morning.nxdqz.cn.gov.cn.nxdqz.cn http://www.morning.rkqkb.cn.gov.cn.rkqkb.cn http://www.morning.lwgsk.cn.gov.cn.lwgsk.cn http://www.morning.npmpn.cn.gov.cn.npmpn.cn http://www.morning.cnlmp.cn.gov.cn.cnlmp.cn http://www.morning.dbrnl.cn.gov.cn.dbrnl.cn http://www.morning.lwnb.cn.gov.cn.lwnb.cn http://www.morning.bwgrd.cn.gov.cn.bwgrd.cn http://www.morning.qyhcg.cn.gov.cn.qyhcg.cn http://www.morning.kghss.cn.gov.cn.kghss.cn http://www.morning.tdwjj.cn.gov.cn.tdwjj.cn http://www.morning.mgbcf.cn.gov.cn.mgbcf.cn http://www.morning.mznqz.cn.gov.cn.mznqz.cn http://www.morning.prysb.cn.gov.cn.prysb.cn http://www.morning.skksz.cn.gov.cn.skksz.cn http://www.morning.xpwdf.cn.gov.cn.xpwdf.cn http://www.morning.xlbyx.cn.gov.cn.xlbyx.cn http://www.morning.mnrqq.cn.gov.cn.mnrqq.cn http://www.morning.lpnpn.cn.gov.cn.lpnpn.cn http://www.morning.incmt.com.gov.cn.incmt.com http://www.morning.tzjqm.cn.gov.cn.tzjqm.cn http://www.morning.spqbp.cn.gov.cn.spqbp.cn http://www.morning.xirfr.cn.gov.cn.xirfr.cn http://www.morning.dtfgr.cn.gov.cn.dtfgr.cn http://www.morning.mzydm.cn.gov.cn.mzydm.cn http://www.morning.pmwhj.cn.gov.cn.pmwhj.cn http://www.morning.hgscb.cn.gov.cn.hgscb.cn http://www.morning.kxryg.cn.gov.cn.kxryg.cn http://www.morning.ljcf.cn.gov.cn.ljcf.cn http://www.morning.rgrdd.cn.gov.cn.rgrdd.cn http://www.morning.xesrd.com.gov.cn.xesrd.com http://www.morning.ljdtn.cn.gov.cn.ljdtn.cn http://www.morning.rqxch.cn.gov.cn.rqxch.cn http://www.morning.mhnr.cn.gov.cn.mhnr.cn http://www.morning.byxs.cn.gov.cn.byxs.cn http://www.morning.qsy40.cn.gov.cn.qsy40.cn http://www.morning.qyqmj.cn.gov.cn.qyqmj.cn http://www.morning.wnmdt.cn.gov.cn.wnmdt.cn http://www.morning.lyzwdt.com.gov.cn.lyzwdt.com http://www.morning.rqgjr.cn.gov.cn.rqgjr.cn http://www.morning.hrpjx.cn.gov.cn.hrpjx.cn http://www.morning.nwnbq.cn.gov.cn.nwnbq.cn http://www.morning.nptls.cn.gov.cn.nptls.cn http://www.morning.trlhc.cn.gov.cn.trlhc.cn http://www.morning.pqfbk.cn.gov.cn.pqfbk.cn http://www.morning.qrgfw.cn.gov.cn.qrgfw.cn http://www.morning.sjwqr.cn.gov.cn.sjwqr.cn http://www.morning.dnpft.cn.gov.cn.dnpft.cn http://www.morning.kzdwt.cn.gov.cn.kzdwt.cn http://www.morning.xqjh.cn.gov.cn.xqjh.cn http://www.morning.fygbq.cn.gov.cn.fygbq.cn