目录
背景依赖实现代码效果背景
近期客户提出一个上传文件时自动添加文字水印的功能,其中关于word文档加水印,网上现有的解决方案都有问题,本文参考java实现word文档(docx)添加水印这篇博客,解决了文档存在分页符情况下无法给所有分页添加水印的问题。
由于POI对于doc格式支持较少,暂时未找到合适的解决doc格式文档添加水印的方法,本文只针对于docx格式文档,如果有大佬有可行方案,欢迎留言交流,感激不尽。
依赖
核心依赖就是POI相关包
<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>${poi.version}</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>${poi-ooxml.version}</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>${poi-ooxml-schemas.version}</version></dependency>
实现代码
说明:
使用本段代码需要确保服务器正确包含对应字体, 默认使用Arial字体本段代码参考了POI中org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy#createWatermark
的实现
import com.microsoft.schemas.office.office.CTLock;import com.microsoft.schemas.vml.CTGroup;import com.microsoft.schemas.vml.CTShape;import com.microsoft.schemas.vml.CTShapetype;import com.microsoft.schemas.vml.CTTextPath;import com.microsoft.schemas.vml.STExt;import com.microsoft.schemas.vml.STTrueFalse;import org.apache.poi.util.IOUtils;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.x.main.CTP;import org.openxmlformats.schemas.wordprocessingml.x.main.CTPPr;import org.openxmlformats.schemas.wordprocessingml.x.main.CTPicture;import org.openxmlformats.schemas.wordprocessingml.x.main.CTR;import org.openxmlformats.schemas.wordprocessingml.x.main.CTRPr;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.nio.file.Files;import java.nio.file.Paths;public class DocxTest {public void addWatermark(InputStream is, OutputStream os, String waterMark) throws IOException {XWPFDocument doc = new XWPFDocument(is);// 遍历文档,添加水印waterMarkDocxDocument(doc, waterMark);try {doc.write(os);}finally {IOUtils.closeQuietly(doc);}}/*** 为文档添加水印<br />* 实现参考了{@link org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy#createWatermark(String)}** @param doc 需要被处理的docx文档对象* @param waterMark 水印内容*/private void waterMarkDocxDocument(XWPFDocument doc, String waterMark) {// 获取文档的所有页眉,如果文档没有页眉,则新建一个默认页眉List<XWPFHeader> headerList = new ArrayList<>(doc.getHeaderList());if (headerList.isEmpty()) {headerList.add(doc.createHeader(HeaderFooterType.DEFAULT));}// 遍历所有的header,这里包含所有的分页for (XWPFHeader header : headerList) {int size = header.getParagraphs().size();if (size == 0) {header.createParagraph();}XWPFParagraph paragraph = header.getParagraphArray(0);CTP ctp = paragraph.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("#d8d8d8");// 字体设置为实心shape.setStroked(STTrueFalse.FALSE);// 绘制文本的路径CTTextPath shapeTextPath = shape.addNewTextpath();// 设置文本字体与大小shapeTextPath.setStyle("font-family:Arial;font-size:1pt");shapeTextPath.setString(waterMark);CTPicture pict = ctr.addNewPict();pict.set(group);}}/*** 构建Shape的样式参数*/private String getShapeStyle() {// 文本path绘制的定位方式return "position: " + "absolute" +";margin-top: " + 0 +";width: " + "415pt" +";height: " + "207.5pt" +";z-index: " + "-251654144" +";mso-wrap-edited: " + "f" +";mso-position-horizontal: " + "center" +";mso-position-horizontal-relative: " + "margin" +";mso-position-vertical-relative: " + "margin" +";mso-position-vertical: " + "center" +";rotation: " + 315;}public static void main(String[] args) throws IOException {InputStream is = Files.newInputStream(Paths.get("test.docx"));OutputStream os = Files.newOutputStream(Paths.get("testOut.docx"));DocxTest docxTest = new DocxTest();docxTest.addWatermark(is, os, "WATER MARK");}}