java 实现docx多行水印

前言

仅此记录对word、excel、pdf增加水印实现方案


一、方案一

1.1:maven依赖

	<dependency>
	    <groupId>org.apache.poi</groupId>
	    <artifactId>poi</artifactId>
	    <version>3.16</version>
	</dependency>
	<dependency>
	    <groupId>org.apache.poi</groupId>
	    <artifactId>poi-ooxml</artifactId>
	    <version>3.16</version>
	</dependency>
	<dependency>
	    <groupId>com.itextpdf</groupId>
	    <artifactId>itext-asian</artifactId>
	    <version>5.2.0</version>
	</dependency>
	<dependency>
	    <groupId>org.apache.pdfbox</groupId>
	    <artifactId>pdfbox</artifactId>
	    <version>2.0.18</version>
	</dependency>
	<dependency>
	    <groupId>com.itextpdf</groupId>
	    <artifactId>itextpdf</artifactId>
	    <version>5.5.13.1</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/org.springframework/spring-mock -->
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-mock</artifactId>
	    <version>2.0.8</version>
	</dependency>

1.2:构造工具类


import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.pdf.*;
import com.microsoft.schemas.office.office.CTLock;
import com.microsoft.schemas.vml.*;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageDecoder;
import org.apache.poi.hslf.usermodel.HSLFPictureShape;
import org.apache.poi.hslf.usermodel.HSLFSlide;
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.wp.usermodel.HeaderFooterType;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFPictureShape;
import org.apache.poi.xslf.usermodel.XSLFSlide;
import org.apache.poi.xssf.usermodel.XSSFRelation;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlide;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.stream.Stream;


public class WaterMarkUtils {

    // word字体
    private static final String fontName = "宋体";
    // 字体大小
    private static final String fontSize = "0.2pt";
    // 字体颜色
    private static final String fontColor = "#d0d0d0";
    // 一个字平均长度,单位pt,用于:计算文本占用的长度(文本总个数*单字长度)
    private static  final Integer widthPerWord = 10;
    // 与顶部的间距
    private static Integer styleTop = 0;
    // 文本旋转角度
    private static final String styleRotation = "45";

    /**
     * @param file:
     * @param markText:
     * @param type:
     * @return: org.springframework.web.multipart.MultipartFile
     * @description: word文档类型判断后添加水印
     */
    public static MultipartFile setWordWaterMark(MultipartFile file,String markText,String type){
        if (type.equals("docx")){
            try {
                XWPFDocument doc = new XWPFDocument(file.getInputStream());
                waterMarkDocXDocument(doc,markText);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();//二进制OutputStream
                doc.write(baos);//文档写入流
                ByteArrayInputStream in = new ByteArrayInputStream(baos.toByteArray());//OutputStream写入InputStream二进制流
                MultipartFile multipartFile = new MockMultipartFile(file.getName(),file.getOriginalFilename(),"", in);
                return multipartFile;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else if (type.equals("doc")){
            try {
                HWPFDocument doc1 = new HWPFDocument(file.getInputStream());
                Range range = doc1.getRange();
                return file;
               /* OfficeDrawings officeDrawing = doc1.getOfficeDrawingsMain();
                officeDrawing.
                XWPFDocument doc = new XWPFDocument(file.getInputStream());
                waterMarkDocXDocument(doc,markText);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();//二进制OutputStream
                doc.write(baos);//文档写入流
                ByteArrayInputStream in = new ByteArrayInputStream(baos.toByteArray());//OutputStream写入InputStream二进制流
                MultipartFile multipartFile = new MockMultipartFile(file.getName(),file.getOriginalFilename(),"", in);
                return multipartFile;*/
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else if (type.equals("jpg")||type.equals("jpeg")||type.equals("png")){
            return markImageByText(file,markText,45);
        } else if (type.equals("xlsx")||type.equals("xls")) {
            return setExcelWaterMark(file, markText,type);
        } else if (type.equals("pptx")||type.equals("ppt")){
            return setPPTWaterMark(file,markText,type);
        } else if (type.equals("pdf")){
            return setPDFWatermark(file,markText);
        }
        return file;
    }


    /**********************************---------------------docx添加水印----------------------**********************************/

    /**
     * 给文档添加水印
     * 此方法可以单独使用
     * @param doc
     * @param customText
     */
    public static void waterMarkDocXDocument(XWPFDocument doc,String customText){
        // 把整页都打上水印
        for (int lineIndex = -5; lineIndex < 20; lineIndex++) {
            styleTop = 100*lineIndex;
            waterMarkDocXDocument_0(doc,customText);
        }
    }



    /**
     * 为文档添加水印
     * @param doc 需要被处理的docx文档对象
     * @param customText 需要添加的水印文字
     */
    public static void waterMarkDocXDocument_0(XWPFDocument doc,String customText) {
        // 水印文字之间使用8个空格分隔
        customText = customText + repeatString(" ", 8);
        // 一行水印重复水印文字次数
        customText = repeatString(customText, 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(customText));
        shape.setFillcolor(fontColor);
        // 字体设置为实心
        shape.setStroked(STTrueFalse.FALSE);
        // 绘制文本的路径
        CTTextPath shapeTextPath = shape.addNewTextpath();
        // 设置文本字体与大小
        shapeTextPath.setStyle("font-family:" + fontName + ";font-size:" + fontSize);
        shapeTextPath.setString(customText);
        CTPicture pict = ctr.addNewPict();
        pict.set(group);
    }



    /**
     * 构建Shape的样式参数
     * @param customText
     * @return
     */
    private static String getShapeStyle(String customText) {
        StringBuilder sb = new StringBuilder();
        // 文本path绘制的定位方式
        sb.append("position: ").append("absolute");
        // 计算文本占用的长度(文本总个数*单字长度)
        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");
        // 设置水印的间隔,这是一个大坑,不能用top,必须要margin-top。
        sb.append(";margin-top: ").append(styleTop);
        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(styleRotation);
        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);
    }

    /**
     * MultipartFile 转 File
     *
     * @param file
     * @throws Exception
     */
    public static File multipartFileToFile(MultipartFile file) throws Exception {

        File toFile = null;
        if (file.equals("") || file.getSize() <= 0) {
            file = null;
        } else {
            InputStream ins = null;
            ins = file.getInputStream();
            toFile = new File(file.getOriginalFilename());
            inputStreamToFile(ins, toFile);
            ins.close();
        }
        return toFile;
    }

    //获取流文件
    private static void inputStreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    /**********************************---------------------图片添加水印----------------------**********************************/

    // 水印透明度
    private static float alpha = 0.5f;
    // 水印横向位置
    private static int positionWidth = 0;
    // 水印纵向位置
    private static int positionHeight = 300;
    // 水印文字字体
    private static Font font = new Font("宋体", Font.BOLD, 32);
    // 水印文字颜色
    private static Color color = Color.gray;

    /**
     *
     * @param alpha
     *            水印透明度
     * @param positionWidth
     *            水印横向位置
     * @param positionHeight
     *            水印纵向位置
     * @param font
     *            水印文字字体
     * @param color
     *            水印文字颜色
     */
    public static void setImageMarkOptions(float alpha, int positionWidth,
                                           int positionHeight, Font font, Color color) {
        if (alpha != 0.0f)
            WaterMarkUtils.alpha = alpha;
        if (positionWidth != 0)
            WaterMarkUtils.positionWidth = positionWidth;
        if (positionHeight != 0)
            WaterMarkUtils.positionHeight = positionHeight;
        if (font != null)
            WaterMarkUtils.font = font;
        if (color != null)
            WaterMarkUtils.color = color;
    }

    /**
     * 给图片添加水印文字、可设置水印文字的旋转角度
     *
     * @param logoText
     * @param multipartFile
     * @param degree
     */
    public static MultipartFile markImageByText(MultipartFile multipartFile, String logoText,Integer degree) {

        InputStream is = null;
        OutputStream os = null;
        try {
            // 1、源图片
            JPEGImageDecoder decoderFile = JPEGCodec.createJPEGDecoder(multipartFile.getInputStream());
            BufferedImage buffImg = decoderFile.decodeAsBufferedImage();
            //Image可以获得 输入图片的信息
            int width = buffImg.getWidth(null);
            int height = buffImg.getHeight(null);


            // 2、得到画笔对象
            Graphics2D g = buffImg.createGraphics();
            // 3、设置对线段的锯齿状边缘处理
            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g.drawImage(
                    buffImg.getScaledInstance(buffImg.getWidth(null),
                            buffImg.getHeight(null), buffImg.SCALE_SMOOTH), 0, 0,
                    null);
            // 4、设置水印旋转
            if (null != degree) {
                g.rotate(Math.toRadians(degree),
                        (double) buffImg.getWidth() / 2,
                        (double) buffImg.getHeight() / 2);
            }
            // 5、设置水印文字颜色
            g.setColor(color);
            // 6、设置水印文字Font
            g.setFont(font);
            // 7、设置水印文字透明度
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
                    alpha));
            // 8、第一参数->设置的内容,后面两个参数->文字在图片上的坐标位置(x,y)
            for(int i=0;i<9;i++){
                for(int j = 0;j<6;j++){
                    g.drawString(logoText, (int) (width - (width*j/3)), (int) (height - (height*i/5)));
                }
            }

            // 9、释放资源
            g.dispose();
            // 10、生成图片
            InputStream in = bufferedImageToInputStream(buffImg);
            MultipartFile file = new MockMultipartFile(multipartFile.getName(),multipartFile.getOriginalFilename(),"", in);
            System.out.println("图片完成添加水印文字");
            return file;

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != is)
                    is.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                if (null != os)
                    os.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }


    /**********************************---------------------Excel添加水印----------------------**********************************/

    /**
     * 将BufferedImage转换为InputStream
     * @param image
     * @return
     */
    public static InputStream bufferedImageToInputStream(BufferedImage image){
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            ImageIO.write(image, "png", os);
            InputStream input = new ByteArrayInputStream(os.toByteArray());
            return input;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 为Excel打上水印工具函数 请自行确保参数值,以保证水印图片之间不会覆盖。 在计算水印的位置的时候,并没有考虑到单元格合并的情况,请注意
     *
     * @param wb
     *            Excel Workbook
     * @param sheet
     *            需要打水印的Excel
     * @param bufferImg
     *            水印地址,classPath,目前只支持png格式的图片,
     *            因为非png格式的图片打到Excel上后可能会有图片变红的问题,且不容易做出透明效果。
     *            同时请注意传入的地址格式,应该为类似:"\\excelTemplate\\test.png"
     * @param startXCol
     *            水印起始列
     * @param startYRow
     *            水印起始行
     * @param betweenXCol
     *            水印横向之间间隔多少列
     * @param betweenYRow
     *            水印纵向之间间隔多少行
     * @param XCount
     *            横向共有水印多少个
     * @param YCount
     *            纵向共有水印多少个
     * @param waterRemarkWidth
     *            水印图片宽度为多少列
     * @param waterRemarkHeight
     *            水印图片高度为多少行
     * @throws IOException
     */
    public static void putWaterRemarkToExcel(Workbook wb, Sheet sheet,BufferedImage bufferImg, int startXCol,
                                             int startYRow, int betweenXCol, int betweenYRow, int XCount, int YCount, int waterRemarkWidth,
                                             int waterRemarkHeight) throws IOException {

        // 导出到字节流B
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(bufferImg, "png", os);
        // 加载图片
        // 开始打水印
        Drawing drawing = sheet.createDrawingPatriarch();

        // 按照共需打印多少行水印进行循环
        for (int yCount = 0; yCount < YCount; yCount++) {
            // 按照每行需要打印多少个水印进行循环
            for (int xCount = 0; xCount < XCount; xCount++) {
                // 创建水印图片位置
                int xIndexInteger = startXCol + (xCount * waterRemarkWidth) + (xCount * betweenXCol);
                int yIndexInteger = startYRow + (yCount * waterRemarkHeight) + (yCount * betweenYRow);
                /*
                 * 参数定义: 第一个参数是(x轴的开始节点); 第二个参数是(是y轴的开始节点); 第三个参数是(是x轴的结束节点);
                 * 第四个参数是(是y轴的结束节点); 第五个参数是(是从Excel的第几列开始插入图片,从0开始计数);
                 * 第六个参数是(是从excel的第几行开始插入图片,从0开始计数); 第七个参数是(图片宽度,共多少列);
                 * 第8个参数是(图片高度,共多少行);
                 */
                ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, xIndexInteger,
                        yIndexInteger, xIndexInteger + waterRemarkWidth, yIndexInteger + waterRemarkHeight);

                Picture pic = drawing.createPicture(anchor,
                        wb.addPicture(os.toByteArray(), Workbook.PICTURE_TYPE_PNG));
                pic.resize();
            }
        }
    }


    /**
     * excel设置水印
     * @param file
     * @param markStr
     */
    public static MultipartFile setExcelWaterMark(MultipartFile file, String markStr,String type){
        try {
            FontImage.Watermark watermark = new FontImage.Watermark();
            watermark.setText("水印测试");
            BufferedImage image = FontImage.createWatermarkImage(null);
            if (type.equals("xlsx")) {
                XSSFWorkbook workbook = new XSSFWorkbook(file.getInputStream());

                //获取excel sheet个数
                int sheets = workbook.getNumberOfSheets();
                //循环sheet给每个sheet添加水印
                for (int i = 0; i < sheets; i++) {
                    XSSFSheet sheet = workbook.getSheetAt(i);
                    // 导出到字节流B
                    ByteArrayOutputStream os = new ByteArrayOutputStream();
                    ImageIO.write(image, "png", os);

                    int pictureIdx = workbook.addPicture(os.toByteArray(), Workbook.PICTURE_TYPE_PNG);
                    //add relation from sheet to the picture data
                    String rID = sheet.addRelation(null, XSSFRelation.IMAGES, workbook.getAllPictures().get(pictureIdx)).getRelationship().getId();
                    //set background picture to sheet
                    sheet.getCTWorksheet().addNewPicture().setId(rID);
                }

                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                workbook.write(bos);
                byte[] barray = bos.toByteArray();
                InputStream is = new ByteArrayInputStream(barray);
                MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getOriginalFilename(), "", is);
                return multipartFile;
            } else {
                HSSFWorkbook wb = new HSSFWorkbook(file.getInputStream());
                //获取excel sheet个数
                int sheets = wb.getNumberOfSheets();
                //循环sheet给每个sheet添加水印
                for (int i = 0; i < sheets; i++) {
                    Sheet sheet = wb.getSheetAt(i);
                    putWaterRemarkToExcel(wb, sheet, image, 0, 0, 6, 10, 10, 10, 0, 0);
                }
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                wb.write(bos);
                byte[] barray = bos.toByteArray();
                InputStream is = new ByteArrayInputStream(barray);
                MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getOriginalFilename(), "", is);
                return multipartFile;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**********************************---------------------PPT添加水印----------------------**********************************/


    /**
     * @param file:
     * @param markStr:
     * @param fileType:
     * @return: void
     * @description: ppt 添加水印
     */
    public static MultipartFile setPPTWaterMark(MultipartFile file, String markStr, String fileType) {
        try {
            //设置水印图片
            FontImage.Watermark watermark = new FontImage.Watermark();
            watermark.setText("水印测试");
            BufferedImage image = FontImage.createWatermarkImage(null);
            if ("pptx".equals(fileType)) {
                XMLSlideShow slideShow = new XMLSlideShow(file.getInputStream());
                byte[] pictureData = imageToBytes(image);
                PictureData pictureData1 = slideShow.addPicture(pictureData, PictureData.PictureType.PNG);
                for (XSLFSlide slide : slideShow.getSlides()) {
                    for(int i =0 ; i<10;i++) {
                        for(int j = 0;j<8;j++) {
                            XSLFPictureShape pictureShape = slide.createPicture(pictureData1);
                            pictureShape.setAnchor(new java.awt.Rectangle(200 * i, 80 * j, 150, 50));
                        }
                    }
                    CTSlide ctSlide = slide.getXmlObject();
                    XmlObject[] allText = ctSlide.selectPath(
                            "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " +
                                    ".//a:t"
                    );
                }
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                slideShow.write(bos);
                slideShow.close();
                bos.close();
                byte[] barray = bos.toByteArray();
                InputStream is = new ByteArrayInputStream(barray);
                MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getOriginalFilename(), "", is);
                return multipartFile;
            } else if ("ppt".equals(fileType)) {
                HSLFSlideShow ppt = new HSLFSlideShow(file.getInputStream());
                byte[] pictureByte = imageToBytes(image);
                PictureData pictureData1 = ppt.addPicture(pictureByte, PictureData.PictureType.PNG);
                for (HSLFSlide slide : ppt.getSlides()) {
                    for(int i =0 ; i<10;i++) {
                        for(int j = 0;j<7;j++) {
                            HSLFPictureShape pictureShape = slide.createPicture(pictureData1);
                            pictureShape.setAnchor(new java.awt.Rectangle(200 * i, 80 * j, 150, 50));
                        }
                    }
                }
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ppt.write(bos);
                ppt.close();
                bos.close();
                byte[] barray = bos.toByteArray();
                InputStream is = new ByteArrayInputStream(barray);
                MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getOriginalFilename(), "", is);
                return multipartFile;
            }
        } catch (IOException e){
            e.printStackTrace();
        }
        return null;
    }


    /**
     * BufferedImage转byte[]
     *
     * @param bImage BufferedImage对象
     * @return byte[]
     */
    private static byte[] imageToBytes(BufferedImage bImage) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            ImageIO.write(bImage, "png", out);
        } catch (IOException e) {
        }
        return out.toByteArray();
    }

    /**********************************---------------------PDF添加水印----------------------**********************************/

    /**
     *
     * @param file
     *            原PDF文件
     * @param waterMarkName
     *            页脚添加水印
     * @throws DocumentException
     * @throws IOException
     */
    public static MultipartFile setPDFWatermark(MultipartFile file,String waterMarkName) {
        try {

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            PdfReader reader = new PdfReader(file.getInputStream());
            PdfStamper stamper = new PdfStamper(reader, bos);

            // 获取总页数 +1, 下面从1开始遍历
            int total = reader.getNumberOfPages() + 1;
            // 使用classpath下面的字体库
            BaseFont base = null;
            try {
                base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
            } catch (Exception e) {
                // 日志处理
                e.printStackTrace();
            }

            // 间隔
            int interval = -5;
            // 获取水印文字的高度和宽度
            int textH = 0, textW = 0;
            JLabel label = new JLabel();
            label.setText(waterMarkName);
            FontMetrics metrics = label.getFontMetrics(label.getFont());
            textH = metrics.getHeight();
            textW = metrics.stringWidth(label.getText());
            System.out.println("textH: " + textH);
            System.out.println("textW: " + textW);

            // 设置水印透明度
            PdfGState gs = new PdfGState();
            gs.setFillOpacity(0.2f);
            gs.setStrokeOpacity(0.2f);

            com.itextpdf.text.Rectangle pageSizeWithRotation = null;
            PdfContentByte content = null;
            for (int i = 1; i < total; i++) {
                // 在内容上方加水印
                content = stamper.getOverContent(i);
                // 在内容下方加水印
                // content = stamper.getUnderContent(i);
                content.saveState();
                content.setGState(gs);

                // 设置字体和字体大小
                content.beginText();
                content.setFontAndSize(base, 20);

                // 获取每一页的高度、宽度
                pageSizeWithRotation = reader.getPageSizeWithRotation(i);
                float pageHeight = pageSizeWithRotation.getHeight();
                float pageWidth = pageSizeWithRotation.getWidth();

                // 根据纸张大小多次添加, 水印文字成30度角倾斜
                for (int height = interval + textH; height < pageHeight; height = height + textH * 3) {
                    for (int width = interval + textW; width < pageWidth + textW; width = width + textW * 2) {
                        content.showTextAligned(Element.ALIGN_LEFT, waterMarkName, width - textW, height - textH, 30);
                    }
                }

                content.endText();
            }

            // 关流
            stamper.close();
            reader.close();
            bos.close();
            InputStream is = new ByteArrayInputStream(bos.toByteArray());
            MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getOriginalFilename(), "", is);
            return multipartFile;
        } catch (DocumentException e){
            e.printStackTrace();
        } catch (IOException e){
            e.printStackTrace();
        }
        return null;
    }

}

package com.fehorizon.hmwf.integration.common.utils;

import com.alibaba.druid.util.StringUtils;
import lombok.Data;

import java.awt.*;
import java.awt.image.BufferedImage;

/**
 * 根据文字生成水印图片
 */
public class FontImage {

    @Data
    public static class Watermark {
        private Boolean enable;
        private String text;
        private String dateFormat;
        private String color;
    }

    public static BufferedImage createWatermarkImage(Watermark watermark) {
        if (watermark == null) {
            watermark = new FontImage.Watermark();
            watermark.setEnable(true);
//            watermark.setText("userName");
            watermark.setText("水印测试");
            watermark.setColor("#C5CBCF");
            watermark.setDateFormat("yyyy-MM-dd HH:mm");
        } else {
            if (StringUtils.isEmpty(watermark.getDateFormat())) {
                watermark.setDateFormat("yyyy-MM-dd HH:mm");
            } else if (watermark.getDateFormat().length() == 16) {
                watermark.setDateFormat("yyyy-MM-dd HH:mm");
            } else if (watermark.getDateFormat().length() == 10) {
                watermark.setDateFormat("yyyy-MM-dd");
            }
            if (StringUtils.isEmpty(watermark.getText())) {
                watermark.setText("水印测试");
            }
            if (StringUtils.isEmpty(watermark.getColor())) {
                watermark.setColor("#C5CBCF");
            }
        }
        String[] textArray = watermark.getText().split("\n");
        Font font = new Font("microsoft-yahei", Font.PLAIN, 50);
        Integer width = 200;
        Integer height = 200;

        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // 背景透明 开始
        Graphics2D g = image.createGraphics();
        image = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
        g.dispose();
        // 背景透明 结束
        g = image.createGraphics();
        g.setColor(new Color(Integer.parseInt(watermark.getColor().substring(1), 16)));// 设定画笔颜色
        g.setFont(font);// 设置画笔字体
        g.shear(0.1, 0.5);// 设定倾斜度

//        设置字体平滑
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        int y = 50;
        y = y + font.getSize();
//        for (int i = 0; i < textArray.length; i++) {
//            g.drawString(textArray[i], 0, y);// 画出字符串
//            y = y + font.getSize();
//        }
        g.drawString(watermark.getText(), 0, y);// 画出字符串

        g.dispose();// 释放画笔
        return image;

    }

}

二、方案二

限制高版本的docx

2.0:过程

一、解决poi给word添加水印遇到的问题:
	1、遇到有页眉的word时水印添加失败
	2、水印最底层被图片表格覆盖
二、解决思路:
	1、把原来的页眉删除或者覆盖
	2、水印只能在底层,那么我给它加多个水印
三、结果:

在这里插入图片描述

2.1:maven依赖

<dependency>
   <groupId>org.apache.poi</groupId>
   <artifactId>poi</artifactId>
   <version>4.1.2</version>
</dependency>
<!--2007 docx文件读取,需要导入poi-ooxml-->
<dependency>
   <groupId>org.apache.poi</groupId>
   <artifactId>poi-ooxml</artifactId>
   <version>4.1.2</version>
</dependency>

2.2:构造工具类

import com.feng.bean.ResultCode;
import com.feng.error.MyException;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
 
import java.io.*;
 
@Slf4j
public class WaterMarkUtils {
 
    /**
     * word文字水印
     *
     * @param inputPath
     * @param outPath
     * @param markStr
     */
    public static void setWordWaterMark(String inputPath, String outPath, String markStr) {
        File inputFile = new File(inputPath);
        //2003doc 用HWPFDocument  ; 2007doc 用 XWPFDocument
        XWPFDocument doc = null;
        try {
            // 延迟解析比率
            ZipSecureFile.setMinInflateRatio(-1.0d);
            doc = new XWPFDocument(new FileInputStream(inputFile));
        } catch (FileNotFoundException e) {
            throw new MyException(ResultCode.FAILURE, "源文件不存在");
        } catch (IOException e) {
            throw new MyException(ResultCode.FAILURE, "读取源文件IO异常");
        } catch (Exception e) {
            throw new MyException(ResultCode.FAILURE, "不支持的文档格式");
        }
        XWPFHeaderFooterPolicy headerFooterPolicy = doc.getHeaderFooterPolicy();
        if (headerFooterPolicy == null) {
            headerFooterPolicy = doc.createHeaderFooterPolicy();
        }
        //添加水印
        headerFooterPolicy.createWatermark(markStr);
        File file = new File(outPath);
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                throw new MyException(ResultCode.FAILURE, "创建输出文件失败");
            }
        }
        OutputStream os = null;
        try {
            os = new FileOutputStream(outPath);
            doc.write(os);
        } catch (FileNotFoundException e) {
            throw new MyException(ResultCode.FAILURE, "创建输出文件失败");
        } catch (Exception e) {
            throw new MyException(ResultCode.FAILURE, "添加文档水印失败");
        } finally {
               if (os != null) {
                    try {
                        os.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (doc != null) {
                    try {
                        doc.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    }
}

2.3:重写 XWPFHeaderFooterPolicy

	新建org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
 
package org.apache.poi.xwpf.model;
 
import com.microsoft.schemas.office.office.CTLock;
import com.microsoft.schemas.office.office.STConnectType;
import com.microsoft.schemas.vml.CTFormulas;
import com.microsoft.schemas.vml.CTGroup;
import com.microsoft.schemas.vml.CTH;
import com.microsoft.schemas.vml.CTHandles;
import com.microsoft.schemas.vml.CTPath;
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 java.util.Iterator;
 
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ooxml.POIXMLDocumentPart.RelationPart;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFFactory;
import org.apache.poi.xwpf.usermodel.XWPFFooter;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.apache.poi.xwpf.usermodel.XWPFHeaderFooter;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRelation;
import org.apache.xmlbeans.impl.values.XmlValueOutOfRangeException;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef;
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 org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.FtrDocument;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.HdrDocument;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.HdrDocument.Factory;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr.Enum;
 
public class XWPFHeaderFooterPolicy {
    public static final Enum DEFAULT;
    public static final Enum EVEN;
    public static final Enum FIRST;
    private XWPFDocument doc;
    private XWPFHeader firstPageHeader;
    private XWPFFooter firstPageFooter;
    private XWPFHeader evenPageHeader;
    private XWPFFooter evenPageFooter;
    private XWPFHeader defaultHeader;
    private XWPFFooter defaultFooter;
 
    public XWPFHeaderFooterPolicy(XWPFDocument doc) {
        this(doc, (CTSectPr) null);
    }
 
    public XWPFHeaderFooterPolicy(XWPFDocument doc, CTSectPr sectPr) {
        if (sectPr == null) {
            CTBody i = doc.getDocument().getBody();
            sectPr = i.isSetSectPr() ? i.getSectPr() : i.addNewSectPr();
        }
 
        this.doc = doc;
 
        CTHdrFtrRef ref;
        POIXMLDocumentPart relatedPart;
        Enum type;
        int var11;
        for (var11 = 0; var11 < sectPr.sizeOfHeaderReferenceArray(); ++var11) {
            ref = sectPr.getHeaderReferenceArray(var11);
            relatedPart = doc.getRelationById(ref.getId());
            XWPFHeader ftr = null;
            if (relatedPart != null && relatedPart instanceof XWPFHeader) {
                ftr = (XWPFHeader) relatedPart;
            }
 
            try {
                type = ref.getType();
            } catch (XmlValueOutOfRangeException var10) {
                type = STHdrFtr.DEFAULT;
            }
 
            this.assignHeader(ftr, type);
        }
 
        for (var11 = 0; var11 < sectPr.sizeOfFooterReferenceArray(); ++var11) {
            ref = sectPr.getFooterReferenceArray(var11);
            relatedPart = doc.getRelationById(ref.getId());
            XWPFFooter var12 = null;
            if (relatedPart != null && relatedPart instanceof XWPFFooter) {
                var12 = (XWPFFooter) relatedPart;
            }
 
            try {
                type = ref.getType();
            } catch (XmlValueOutOfRangeException var9) {
                type = STHdrFtr.DEFAULT;
            }
 
            this.assignFooter(var12, type);
        }
 
    }
 
    private void assignFooter(XWPFFooter ftr, Enum type) {
        if (type == STHdrFtr.FIRST) {
            this.firstPageFooter = ftr;
        } else if (type == STHdrFtr.EVEN) {
            this.evenPageFooter = ftr;
        } else {
            this.defaultFooter = ftr;
        }
 
    }
 
    private void assignHeader(XWPFHeader hdr, Enum type) {
        if (type == STHdrFtr.FIRST) {
            this.firstPageHeader = hdr;
        } else if (type == STHdrFtr.EVEN) {
            this.evenPageHeader = hdr;
        } else {
            this.defaultHeader = hdr;
        }
 
    }
 
    public XWPFHeader createHeader(Enum type) {
        return this.createHeader(type, (XWPFParagraph[]) null);
    }
 
    public XWPFHeader createHeader(Enum type, XWPFParagraph[] pars) {
        XWPFHeader header = this.getHeader(type);
//存在hdader则覆盖
//        if(header == null) {
        HdrDocument hdrDoc = Factory.newInstance();
        XWPFRelation relation = XWPFRelation.HEADER;
        int i = this.getRelationIndex(relation);
        XWPFHeader wrapper = (XWPFHeader) this.doc.createRelationship(relation, XWPFFactory.getInstance(), i);
        wrapper.setXWPFDocument(this.doc);
        CTHdrFtr hdr = this.buildHdr(type, wrapper, pars);
        wrapper.setHeaderFooter(hdr);
        hdrDoc.setHdr(hdr);
        this.assignHeader(wrapper, type);
        header = wrapper;
//        }
 
        return header;
    }
 
    public XWPFFooter createFooter(Enum type) {
        return this.createFooter(type, (XWPFParagraph[]) null);
    }
 
    public XWPFFooter createFooter(Enum type, XWPFParagraph[] pars) {
        XWPFFooter footer = this.getFooter(type);
//        if (footer == null) {
        FtrDocument ftrDoc = org.openxmlformats.schemas.wordprocessingml.x2006.main.FtrDocument.Factory.newInstance();
        XWPFRelation relation = XWPFRelation.FOOTER;
        int i = this.getRelationIndex(relation);
        XWPFFooter wrapper = (XWPFFooter) this.doc.createRelationship(relation, XWPFFactory.getInstance(), i);
        wrapper.setXWPFDocument(this.doc);
        CTHdrFtr ftr = this.buildFtr(type, wrapper, pars);
        wrapper.setHeaderFooter(ftr);
        ftrDoc.setFtr(ftr);
        this.assignFooter(wrapper, type);
        footer = wrapper;
//        }
 
        return footer;
    }
 
    private int getRelationIndex(XWPFRelation relation) {
        int i = 1;
        Iterator var3 = this.doc.getRelationParts().iterator();
 
        while (var3.hasNext()) {
            RelationPart rp = (RelationPart) var3.next();
            if (rp.getRelationship().getRelationshipType().equals(relation.getRelation())) {
                ++i;
            }
        }
 
        return i;
    }
 
    private CTHdrFtr buildFtr(Enum type, XWPFHeaderFooter wrapper, XWPFParagraph[] pars) {
        CTHdrFtr ftr = this.buildHdrFtr(pars, wrapper);
        this.setFooterReference(type, wrapper);
        return ftr;
    }
 
    private CTHdrFtr buildHdr(Enum type, XWPFHeaderFooter wrapper, XWPFParagraph[] pars) {
        CTHdrFtr hdr = this.buildHdrFtr(pars, wrapper);
        this.setHeaderReference(type, wrapper);
        return hdr;
    }
 
    private CTHdrFtr buildHdrFtr(XWPFParagraph[] paragraphs, XWPFHeaderFooter wrapper) {
        CTHdrFtr ftr = wrapper._getHdrFtr();
        if (paragraphs != null) {
            for (int i = 0; i < paragraphs.length; ++i) {
                ftr.addNewP();
                ftr.setPArray(i, paragraphs[i].getCTP());
            }
        }
 
        return ftr;
    }
 
    private void setFooterReference(Enum type, XWPFHeaderFooter wrapper) {
        CTHdrFtrRef ref = this.doc.getDocument().getBody().getSectPr().addNewFooterReference();
        ref.setType(type);
        ref.setId(this.doc.getRelationId(wrapper));
    }
 
    private void setHeaderReference(Enum type, XWPFHeaderFooter wrapper) {
        CTHdrFtrRef ref = this.doc.getDocument().getBody().isSetSectPr() ? this.doc.getDocument().getBody().getSectPr().addNewHeaderReference() : this.doc.getDocument().getBody().addNewSectPr().addNewHeaderReference();
        ref.setType(type);
        ref.setId(this.doc.getRelationId(wrapper));
    }
 
    public XWPFHeader getFirstPageHeader() {
        return this.firstPageHeader;
    }
 
    public XWPFFooter getFirstPageFooter() {
        return this.firstPageFooter;
    }
 
    public XWPFHeader getOddPageHeader() {
        return this.defaultHeader;
    }
 
    public XWPFFooter getOddPageFooter() {
        return this.defaultFooter;
    }
 
    public XWPFHeader getEvenPageHeader() {
        return this.evenPageHeader;
    }
 
    public XWPFFooter getEvenPageFooter() {
        return this.evenPageFooter;
    }
 
    public XWPFHeader getDefaultHeader() {
        return this.defaultHeader;
    }
 
    public XWPFFooter getDefaultFooter() {
        return this.defaultFooter;
    }
 
    public XWPFHeader getHeader(int pageNumber) {
        return pageNumber == 1 && this.firstPageHeader != null ? this.firstPageHeader : (pageNumber % 2 == 0 && this.evenPageHeader != null ? this.evenPageHeader : this.defaultHeader);
    }
 
    public XWPFHeader getHeader(Enum type) {
        return type == STHdrFtr.EVEN ? this.evenPageHeader : (type == STHdrFtr.FIRST ? this.firstPageHeader : this.defaultHeader);
    }
 
    public XWPFFooter getFooter(int pageNumber) {
        return pageNumber == 1 && this.firstPageFooter != null ? this.firstPageFooter : (pageNumber % 2 == 0 && this.evenPageFooter != null ? this.evenPageFooter : this.defaultFooter);
    }
 
    public XWPFFooter getFooter(Enum type) {
        return type == STHdrFtr.EVEN ? this.evenPageFooter : (type == STHdrFtr.FIRST ? this.firstPageFooter : this.defaultFooter);
    }
 
    public void createWatermark(String text) {
        XWPFParagraph[] pars = new XWPFParagraph[]{this.getWatermarkParagraph(text, 1)};
        this.createHeader(DEFAULT, pars);
        pars[0] = this.getWatermarkParagraph(text, 2);
        this.createHeader(FIRST, pars);
        pars[0] = this.getWatermarkParagraph(text, 3);
        this.createHeader(EVEN, pars);
    }
 
    /**
     * 原方法备份
     * @param text
     * @param idx
     * @return
     */
    private XWPFParagraph getWatermarkParagraph123(String text, int idx) {
        CTP p = org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP.Factory.newInstance();
        byte[] rsidr = this.doc.getDocument().getBody().getPArray(0).getRsidR();
        byte[] rsidrdefault = this.doc.getDocument().getBody().getPArray(0).getRsidRDefault();
        p.setRsidP(rsidr);
        p.setRsidRDefault(rsidrdefault);
        CTPPr pPr = p.addNewPPr();
        pPr.addNewPStyle().setVal("Header");
        CTR r = p.addNewR();
        CTRPr rPr = r.addNewRPr();
        rPr.addNewNoProof();
        CTPicture pict = r.addNewPict();
        CTGroup group = com.microsoft.schemas.vml.CTGroup.Factory.newInstance();
        CTShapetype shapetype = group.addNewShapetype();
        shapetype.setId("_x0000_t136");
        shapetype.setCoordsize("1600,21600");
        shapetype.setSpt(136.0F);
        shapetype.setAdj("10800");
        shapetype.setPath2("m@7,0l@8,0m@5,21600l@6,21600e");
        CTFormulas formulas = shapetype.addNewFormulas();
        formulas.addNewF().setEqn("sum #0 0 10800");
        formulas.addNewF().setEqn("prod #0 2 1");
        formulas.addNewF().setEqn("sum 21600 0 @1");
        formulas.addNewF().setEqn("sum 0 0 @2");
        formulas.addNewF().setEqn("sum 21600 0 @3");
        formulas.addNewF().setEqn("if @0 @3 0");
        formulas.addNewF().setEqn("if @0 21600 @1");
        formulas.addNewF().setEqn("if @0 0 @2");
        formulas.addNewF().setEqn("if @0 @4 21600");
        formulas.addNewF().setEqn("mid @5 @6");
        formulas.addNewF().setEqn("mid @8 @5");
        formulas.addNewF().setEqn("mid @7 @8");
        formulas.addNewF().setEqn("mid @6 @7");
        formulas.addNewF().setEqn("sum @6 0 @5");
        CTPath path = shapetype.addNewPath();
        path.setTextpathok(STTrueFalse.T);
        path.setConnecttype(STConnectType.CUSTOM);
        path.setConnectlocs("@9,0;@10,10800;@11,21600;@12,10800");
        path.setConnectangles("270,180,90,0");
        CTTextPath shapeTypeTextPath = shapetype.addNewTextpath();
        shapeTypeTextPath.setOn(STTrueFalse.T);
        shapeTypeTextPath.setFitshape(STTrueFalse.T);
        CTHandles handles = shapetype.addNewHandles();
        CTH h = handles.addNewH();
        h.setPosition("#0,bottomRight");
        h.setXrange("6629,14971");
        CTLock lock = shapetype.addNewLock();
        lock.setExt(STExt.EDIT);
        CTShape shape = group.addNewShape();
        shape.setId("PowerPlusWaterMarkObject" + idx);
        shape.setSpid("_x0000_s102" + (4 + idx));
        shape.setType("#_x0000_t136");
        shape.setStyle("position:absolute;margin-left:0;margin-top:0;width:415pt;height:207.5pt;z-index:1;mso-wrap-edited:f;mso-position-horizontal:center;mso-position-horizontal-relative:margin;mso-position-vertical:center;mso-position-vertical-relative:margin");
        shape.setWrapcoords("616 5068 390 16297 39 16921 -39 17155 7265 17545 7186 17467 -39 17467 18904 17467 10507 17467 8710 17545 18904 17077 18787 16843 18358 16297 18279 12554 19178 12476 20701 11774 20779 11228 21131 10059 21248 8811 21248 7563 20975 6316 20935 5380 19490 5146 14022 5068 2616 5068");
        shape.setFillcolor("black");
        shape.setStroked(STTrueFalse.FALSE);
        CTTextPath shapeTextPath = shape.addNewTextpath();
        shapeTextPath.setStyle("font-family:&quot;Cambria&quot;;font-size:1pt");
        shapeTextPath.setString(text);
        pict.set(group);
        return new XWPFParagraph(p, this.doc);
    }
 
    /**
     * 重写方法
     * @param text
     * @param idx
     * @return
     */
    private XWPFParagraph getWatermarkParagraph(String text, int idx) {
        CTP p = org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP.Factory.newInstance();
        byte[] rsidr = this.doc.getDocument().getBody().getPArray(0).getRsidR();
        byte[] rsidrdefault = this.doc.getDocument().getBody().getPArray(0).getRsidRDefault();
        p.setRsidP(rsidr);
        p.setRsidRDefault(rsidrdefault);
        CTPPr pPr = p.addNewPPr();
        pPr.addNewPStyle().setVal("Header");
        CTR r = p.addNewR();
        if (idx == 1) {
            //margin-left和margin-top调整位置,height和width调整大小
            //左上角
            setCTR(r, text, idx, "position:absolute;left:0pt;margin-left:-120pt;margin-top:108pt;mso-position-horizontal-relative:margin;mso-position-vertical-relative:margin;rotation:-2949120f;height:60pt;width:360pt;mso-width-relative:page;mso-height-relative:page");
            //右上角
            setCTR(r, text, idx, "position:absolute;left:0pt;margin-left:190pt;margin-top:108pt;mso-position-horizontal-relative:margin;mso-position-vertical-relative:margin;rotation:-2949120f;height:60pt;width:360pt;mso-width-relative:page;mso-height-relative:page");
            //左下角
            setCTR(r, text, idx, "position:absolute;left:0pt;margin-left:-120pt;margin-top:495pt;mso-position-horizontal-relative:margin;mso-position-vertical-relative:margin;rotation:-2949120f;height:60pt;width:360pt;mso-width-relative:page;mso-height-relative:page");
            //右下角
            setCTR(r, text, idx, "position:absolute;left:0pt;margin-left:190pt;margin-top:495pt;mso-position-horizontal-relative:margin;mso-position-vertical-relative:margin;rotation:-2949120f;height:60pt;width:360pt;mso-width-relative:page;mso-height-relative:page");
        } else {
            setCTR(r, text, idx, "position:absolute;left:0pt;mso-position-horizontal-relative:margin;mso-position-vertical-relative:margin;rotation:-2949120f;height:60pt;width:360pt;mso-width-relative:page;mso-height-relative:page");
        }
        return new XWPFParagraph(p, this.doc);
    }
 
    /**
     * 添加水印
     * @param r
     * @param text
     * @param idx
     * @param style
     */
    private void setCTR(CTR r, String text, int idx, String style) {
        CTRPr rPr = r.addNewRPr();
        rPr.addNewNoProof();
        CTPicture pict = r.addNewPict();
        CTGroup group = com.microsoft.schemas.vml.CTGroup.Factory.newInstance();
        CTShapetype shapetype = group.addNewShapetype();
        shapetype.setId("_x0000_t136");
        shapetype.setCoordsize("1600,21600");
        shapetype.setSpt(136.0F);
        shapetype.setAdj("10800");
        shapetype.setPath2("m@7,0l@8,0m@5,21600l@6,21600e");
        CTFormulas formulas = shapetype.addNewFormulas();
        formulas.addNewF().setEqn("sum #0 0 10800");
        formulas.addNewF().setEqn("prod #0 2 1");
        formulas.addNewF().setEqn("sum 21600 0 @1");
        formulas.addNewF().setEqn("sum 0 0 @2");
        formulas.addNewF().setEqn("sum 21600 0 @3");
        formulas.addNewF().setEqn("if @0 @3 0");
        formulas.addNewF().setEqn("if @0 21600 @1");
        formulas.addNewF().setEqn("if @0 0 @2");
        formulas.addNewF().setEqn("if @0 @4 21600");
        formulas.addNewF().setEqn("mid @5 @6");
        formulas.addNewF().setEqn("mid @8 @5");
        formulas.addNewF().setEqn("mid @7 @8");
        formulas.addNewF().setEqn("mid @6 @7");
        formulas.addNewF().setEqn("sum @6 0 @5");
        CTPath path = shapetype.addNewPath();
        path.setTextpathok(STTrueFalse.T);
        path.setConnecttype(STConnectType.CUSTOM);
        path.setConnectlocs("@9,0;@10,10800;@11,21600;@12,10800");
        path.setConnectangles("270,180,90,0");
        CTTextPath shapeTypeTextPath = shapetype.addNewTextpath();
        shapeTypeTextPath.setOn(STTrueFalse.T);
        shapeTypeTextPath.setFitshape(STTrueFalse.T);
        CTHandles handles = shapetype.addNewHandles();
        CTH h = handles.addNewH();
        h.setPosition("#0,bottomRight");
        h.setXrange("6629,14971");
        CTLock lock = shapetype.addNewLock();
        lock.setExt(STExt.EDIT);
        CTShape shape = group.addNewShape();
        shape.setId("PowerPlusWaterMarkObject" + idx);
        shape.setSpid("_x0000_s102" + (4 + idx));
        shape.setType("#_x0000_t136");
        shape.setStyle(style);
        shape.setWrapcoords("616 5068 390 16297 39 16921 -39 17155 7265 17545 7186 17467 -39 17467 18904 17467 10507 17467 8710 17545 18904 17077 18787 16843 18358 16297 18279 12554 19178 12476 20701 11774 20779 11228 21131 10059 21248 8811 21248 7563 20975 6316 20935 5380 19490 5146 14022 5068 2616 5068");
        //添加水印颜色
        shape.setFillcolor("#D3D3D3");
        shape.setStroked(STTrueFalse.FALSE);
        CTTextPath shapeTextPath = shape.addNewTextpath();
        shapeTextPath.setStyle("font-family:&quot;Cambria&quot;;font-size:1pt");
        shapeTextPath.setString(text);
        pict.set(group);
    }
 
    static {
        DEFAULT = STHdrFtr.DEFAULT;
        EVEN = STHdrFtr.EVEN;
        FIRST = STHdrFtr.FIRST;
    }
}

相关参考资料
https://www.jianshu.com/p/646df60423f5
https://blog.csdn.net/z172989496/article/details/110492356


总结

以上就是两种对docx文件增加水印得解决方案,本文仅仅简单整理下相关代码。其中代码片段部分来自网络。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值