poi word 添加水印

依赖

        <!--poi-tl-->
        <dependency>
            <groupId>com.deepoove</groupId>
            <artifactId>poi-tl</artifactId>
            <version>1.12.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.2</version>
        </dependency>

DocxUtil

import com.microsoft.schemas.office.office.CTLock;
import com.microsoft.schemas.vml.*;
import org.apache.poi.wp.usermodel.HeaderFooterType;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
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 java.io.InputStream;
import java.util.stream.Stream;

import static org.openxmlformats.schemas.officeDocument.x2006.sharedTypes.STTrueFalse.FALSE;
import static org.openxmlformats.schemas.officeDocument.x2006.sharedTypes.STTrueFalse.T;

/**
 * @author wangshuai9776
 * @date 2024/8/8 13:37
 */
public class DocxUtil {

//	public final static String DEFAULT_WATERMARK = "XXX科技有限公司"; // 后续按 系统设置获取
	public final static String DEFAULT_FONT_COLOR = "#d8d8d8";

	// 字体大小
	public static final String FONT_SIZE = "0.5pt";
	// 文本旋转角度
	public static final String STYLE_ROTATION = "-45";
	/**
	 * 水印参数
	 */
	// private static final String fontColor = "#D3D3D3"; // 字体颜色

	/**
	 * 艺术字水印参数
	 */
	private static final String fontName = "Microsoft YaHei"; // word字体
	// private static final String fontSize = "0.5pt"; // 字体大小
	private static final int widthPerWord = 10; // 一个字平均长度,单位pt,用于:计算文本占用的长度(文本总个数*单字长度)
	// private static final String styleRotation = "-45"; // 文本旋转角度
	 private static final String SHAPE_TYPE = "#_x0000_t136"; // 形状类型:多边形
	 private static final String SHAPE_SPID = "_x0000_s102";


	/**
	 * word文字水印(调用poi封装的createWatermark方法)
	 * @param doc XWPFDocument对象
	 * @param markStr 水印文字
	 */
	public static void setWordWaterMark(XWPFDocument doc, String markStr,String fontColor) {
		XWPFParagraph paragraph = doc.createParagraph();
		XWPFHeaderFooterPolicy headerFooterPolicy = doc.getHeaderFooterPolicy();
		if (headerFooterPolicy == null) {
			headerFooterPolicy = doc.createHeaderFooterPolicy();
		}
		// create default Watermark - fill color black and not rotated
		headerFooterPolicy.createWatermark(markStr);
		// get the default header
		// Note: createWatermark also sets FIRST and EVEN headers
		// but this code does not updating those other headers
		XWPFHeader header = headerFooterPolicy.getHeader(XWPFHeaderFooterPolicy.DEFAULT);
		paragraph = header.getParagraphArray(0);
//            // get com.microsoft.schemas.vml.CTShape where fill color and rotation is set
		paragraph.getCTP().newCursor();
		org.apache.xmlbeans.XmlObject[] xmlobjects = paragraph.getCTP().getRArray(0).getPictArray(0).selectChildren(
				new javax.xml.namespace.QName("urn:schemas-microsoft-com:vml", "shape"));
		if (xmlobjects.length > 0) {
			CTShape ctshape = (CTShape) xmlobjects[0];
			ctshape.setFillcolor(fontColor);
			ctshape.setStyle(ctshape.getStyle() + ";rotation:315");
		}
	}

	/**
	 * 以艺术字方式加上水印(平铺)
	 * @param docx XWPFDocument对象
	 * @param customText 水印文字
	 */
	public static void makeFullWaterMarkByWordArt(XWPFDocument docx, String customText,String fontColor,String fontSize,String styleRotation) {
		customText = customText + repeatString(" ", 16); // 水印文字之间使用8个空格分隔
		customText = repeatString(customText, 3); // 一行水印重复水印文字次数 //重复次数不能太多,内容过长会导致字体展示不出来。
		String styleTop = "0pt";  // 与顶部的间距

		if (docx == null) {
			return;
		}
		// 遍历文档,添加水印
		for (int lineIndex = -10; lineIndex < 20; lineIndex++) {
			styleTop = 200 * lineIndex + "pt";
			waterMarkDocXDocument(docx, customText, styleTop, 1,fontColor, fontSize ,styleRotation);
		}
	}

	/**
	 * 以艺术字方式加上水印(单个)
	 * @param docx XWPFDocument对象
	 * @param customText 水印文字
	 */
	public static void makeWaterMarkByWordArt(XWPFDocument docx, String customText,String fontColor,String fontSize,String rotation) {
		String styleTop = "0pt";  // 与顶部的间距

		if (docx == null) {
			return;
		}
		// 添加水印
		waterMarkDocXDocument(docx, customText, styleTop, 2,fontColor,fontSize,rotation);
	}

	/**
	 * 将指定的字符串重复repeats次.
	 * @param pattern 字符串
	 * @param repeats 重复次数
	 * @return 生成的字符串
	 */
	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);
	}

	/**
	 * 为文档添加水印
	 * 实现参考了{@link XWPFHeaderFooterPolicy#(String, int)}
	 * @param doc 需要被处理的docx文档对象
	 * @param customText 水印文本
	 * @param type 类型:1.平铺;2.单个
	 */
	private static void waterMarkDocXDocument(XWPFDocument doc, String customText, String styleTop, int type,String fontColor,String fontSize,String rotation) {
		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");
		for (int i = 0; i < 3; i++) {
			// 开始加水印
			CTR ctr = ctp.addNewR();
			CTRPr ctrpr = ctr.addNewRPr();
			ctrpr.addNewNoProof();
			CTGroup group = CTGroup.Factory.newInstance();
			CTShape shape = group.addNewShape();
			shape.setSpid(SHAPE_SPID);
			shape.setType(SHAPE_TYPE);
			if(type != 2){
				shape.setStyle(getShapeStyle(customText, styleTop,rotation)); // 设置形状样式(旋转,位置,相对路径等参数)
			}else{
				shape.setStyle(getShapeStyle()); // 设置形状样式(旋转,位置,相对路径等参数)
			}
			shape.setFillcolor(fontColor);
			shape.setStroked(FALSE); // 字体设置为实心

			CTLock lock = shape.addNewLock();
			lock.setExt(STExt.VIEW);

			CTTextPath shapeTextPath = shape.addNewTextpath(); // 绘制文本的路径
			shapeTextPath.setOn(T);
			shapeTextPath.setFitshape(T);
			shapeTextPath.setStyle("font-family:" + fontName + ";font-size:" + fontSize); // 设置文本字体与大小
			shapeTextPath.setString(customText+i);
			CTPicture pict = ctr.addNewPict();
			pict.set(group);
		}
	}

	/**
	 * 加载docx格式的word文档
	 * @param inputStream
	 * @return
	 */
	private static XWPFDocument loadDocXDocument(InputStream inputStream) {
		XWPFDocument doc;
		try {
			doc = new XWPFDocument(inputStream);
		} catch (Exception e) {
			throw new RuntimeException("文档加载失败!!");
		}
		return doc;
	}

	/**
	 * 构建Shape的样式参数
	 * @param customText 水印文本
	 * @return
	 */
	private static String getShapeStyle(String customText, String styleTop,String styleRotation) {
		StringBuilder sb = new StringBuilder();
		sb.append("position: ").append("absolute"); // 文本path绘制的定位方式
		sb.append(";width: ").append(customText.length() * widthPerWord).append("pt"); // 计算文本占用的长度(文本总个数*单字长度)
		sb.append(";height: ").append("20pt"); // 字体高度
		sb.append(";z-index: ").append("-251654144");
		sb.append(";mso-wrap-edited: ").append("f");
		sb.append(";margin-top: ").append(styleTop);
		sb.append(";mso-position-horizontal-relative: ").append("margin");
		sb.append(";mso-position-horizontal: ").append("center");
		sb.append(";mso-position-vertical-relative: ").append("margin");
		sb.append(";mso-position-vertical: ").append("left");
		sb.append(";rotation: ").append(styleRotation);
		return sb.toString();
	}

	/**
	 * 构建Shape的样式参数
	 * @return
	 */
	private static String getShapeStyle() {
		StringBuilder sb = new StringBuilder();
		sb.append("position: ").append("absolute"); // 文本path绘制的定位方式
		sb.append(";left: ").append("opt");
		sb.append(";width: ").append("500pt"); // 计算文本占用的长度(文本总个数*单字长度)
		sb.append(";height: ").append("150pt"); // 字体高度
		sb.append(";z-index: ").append("-251654144");
		sb.append(";mso-wrap-edited: ").append("f");
		sb.append(";margin-left: ").append("-50pt");
		sb.append(";margin-top: ").append("270pt");
		sb.append(";mso-position-horizontal-relative: ").append("margin");
		sb.append(";mso-position-vertical-relative: ").append("margin");
		sb.append(";mso-width-relative: ").append("page");
		sb.append(";mso-height-relative: ").append("page");
		sb.append(";rotation: ").append("-2949120f");
		return sb.toString();
	}

}

调用

DocxUtil.makeFullWaterMarkByWordArt(doc, watermark, DocxUtil.DEFAULT_FONT_COLOR, DocxUtil.FONT_SIZE, DocxUtil.STYLE_ROTATION);

遇到的问题

部分客户给的word无法添加水印

  • 在选取模式下 有水印
    在这里插入图片描述
  • 编辑模式下就看不见水印了
    在这里插入图片描述

水印文案 过长会导致字变小变形 超过一定长度就会显示异常。消失等情况

在这里插入图片描述

在使用POI操作Word文档之前,我们需要先添加POI的依赖,可以在pom.xml中添加如下代码: ```xml <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency> ``` 接下来,我们可以使用POI添加水印。代码示例如下: ```java import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; import org.apache.xmlbeans.XmlCursor; public class AddWatermark { public static void main(String[] args) throws Exception { File file = new File("example.docx"); FileInputStream fis = new FileInputStream(file); XWPFDocument document = new XWPFDocument(fis); fis.close(); String watermark = "My Watermark"; for (XWPFParagraph p : document.getParagraphs()) { for (XWPFRun r : p.getRuns()) { XmlCursor cursor = r.getCTR().newCursor(); cursor.toParent(); cursor.insertElement("w:pPr"); cursor.toParent(); cursor.insertElement("w:r"); XWPFRun run = new XWPFRun(cursor, r.getCTR().copy()); run.setText(watermark); run.getCTR().getRPr().getRFonts().setAscii("Times New Roman"); run.getCTR().getRPr().getRFonts().setHAnsi("Times New Roman"); run.getCTR().getRPr().getRFonts().setCs("Times New Roman"); run.getCTR().getRPr().setColor(r.getColor()); run.getCTR().getRPr().setSz(r.getFontSize()); run.getCTR().getRPr().setB(r.isBold()); run.getCTR().getRPr().setI(r.isItalic()); r.setText("", 0); } } FileOutputStream fos = new FileOutputStream(file); document.write(fos); fos.close(); } } ``` 在代码中,我们首先通过FileInputStream读取Word文档,然后遍历所有的段落和文本内容,在每个文本内容上添加水印。最后,通过FileOutputStream将更新后的Word文档写回到磁盘上。 需要注意的是,POI目前还没有提供直接添加水印的API,我们需要通过在文本内容前插入一个段落来实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值