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

1、前言(瞎扯)

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

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

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

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

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

3.1、我的代码结构

Java / Spring Boot + POI 给 Word 添加水印_第1张图片

3.2 、直接贴代码了

pom 依赖的版本不要改,修改版本可能会导致一些东西缺失

3.2.1、pom 依赖



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        3.2.2
         
    
    com.lyk
    springboot-word-finger
    0.0.1-SNAPSHOT
    springboot-word-finger
    springboot-word-finger
    
        17
    
    
        
            org.springframework.boot
            spring-boot-starter
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
            org.jsoup
            jsoup
            1.11.2
        


        
            org.apache.poi
            poi
            4.1.0
        
        
            org.apache.poi
            poi-scratchpad
            4.1.0
        

        
            org.apache.poi
            poi-ooxml
            4.1.0
        
        
            org.apache.poi
            poi-ooxml-schemas
            4.1.0
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    



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 完事~

拿去好好享用吧~

你可能感兴趣的:(工具使用,spring,boot,后端开发,java,spring,boot,word,POI)