目录
- 背景
- 依赖
- 实现代码
- 效果
背景
最近,客户在上传文件时提出了自动添加文本水印的功能,包括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.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPicture;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.openxmlformats.schemas.wordprocessingml.x2006.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");
}
}