Java poi给word添加水印,字体,颜色,倾斜度等

       之前使用poi-tl进行word模板生成word文件,在生成word之后,现在需求需要给word添加一个水印的功能,先贴图生成后的效果,部分内容涉及公司信息打了马赛克,

贴一下代码



/**
 * @author qiankun.hu
 * @version 1.0.0
 * @createTime 2022年05月30日 13:57:00
 * @Description 给word添加水印
 */
@Slf4j
@Service
public class WordWaterMarkServiceImpl implements WaterMarkService {


    private final static String DEFAULT_WATERMARK = "XXX科技有限公司";
    private final static String DEFAULT_FONT_COLOR = "#d8d8d8";

    // 字体大小
    private static final String FONT_SIZE = "0.5pt";
    // 文本旋转角度
    private static final String STYLE_ROTATION = "-30";

    /**
     * 添加水印 返回文件临时路径
     * @param is
     * @param waterMarkInfo
     * @param tempFileDirPath
     * @return
     */
    @Override
    public String writeWithWaterMark(InputStream is, TextWaterMark waterMarkInfo, String tempFileDirPath) {
        if (is == null) {
            throw new BusinessException(new ErrorInfo("-1", "文件为空"));
        }
        File tempFileDir = new File(tempFileDirPath);
        if (!tempFileDir.exists()) {
            log.info("目录不存在,创建文件夹{}", tempFileDirPath);
            tempFileDir.mkdirs();
        }
        //设置默认值
        String watermark = StringUtils.isBlank(waterMarkInfo.getText()) ? DEFAULT_WATERMARK : waterMarkInfo.getText();
        String color = StringUtils.isBlank(waterMarkInfo.getFontColor()) ? DEFAULT_FONT_COLOR : "#" + waterMarkInfo.getFontColor();
        String fontSize = (null == waterMarkInfo.getFontSize()) ? FONT_SIZE : waterMarkInfo.getFontSize() + "pt";
        String rotation = (null == waterMarkInfo.getRotation()) ? STYLE_ROTATION : "-" + waterMarkInfo.getRotation();
        //检查水印信息 去掉也行,暂时没用到里面的参数
        checkTextParam(waterMarkInfo);
        XWPFDocument doc = null;

        try {
            doc = new XWPFDocument(is);
            addWaterMark(doc, watermark, color, fontSize, rotation);
            String filePath = tempFileDirPath + File.separator +System.currentTimeMillis() + ".docx";
            OutputStream os = new FileOutputStream(filePath);
            doc.write(os);
            return filePath;
        } catch (IOException e) {
            log.error("water mark word error: {}", e);
            throw new BusinessException(new ErrorInfo("-1", "文件添加水印异常"));
        } finally {
            if (doc != null) {
                try {
                    doc.close();
                } catch (IOException e) {
                    log.error("water mark word close error: {}", e);
                }
            }
        }
    }

    /**
     * 添加水印信息
     * @param obj
     * @param watermark
     * @param color
     * @param fontSize
     * @param rotation
     */
    private void addWaterMark(Object obj, String watermark, String color, String fontSize, String rotation) {
        if (obj instanceof XWPFDocument) {
            XWPFDocument doc = (XWPFDocument) obj;
            DocxUtil.makeFullWaterMarkByWordArt(doc, watermark, color, fontSize, rotation);
        }
    }


}

 service的代码

/**
 * @author qiankun.hu
 * @version 1.0.0
 * @createTime 2022年05月30日 13:51:00
 * @Description TODO
 */
public interface WaterMarkService {

    int DEFAULT_HORIZONTAL = 150;
    int DEFAULT_VERTICAL = 150;
    int DEFAULT_ROTATION = 30;
    int DEFAULT_FONT_SIZE = 50;
    float DEFAULT_ALPHA = 0.5f;
    String DEFAULT_COLOR = "d8d8d8";
    String COLOR_REGEX = "^([0-9a-fA-F]{6})$";

    /**
     *
     * @param is
     * @param waterMarkInfo
     * @param tempFileDirPath
     * @return
     */
    String writeWithWaterMark(InputStream is, TextWaterMarkDto waterMarkInfo, String tempFileDirPath);

    /**
     * 可以去掉,没用到
     */
    default void checkTextParam(TextWaterMarkDto waterMark) {
        if (waterMark != null) {

            if (StringUtils.isBlank(waterMark.getText())) {
                throw new BusinessException(ErrorCodeEnum.PARAM_NULL.toErrorInfos(Placeholder.PARAM_WATER_TEXT.getCode()));
            }
            if (waterMark.getIntervalHorizontal() == null) {
                waterMark.setIntervalHorizontal(DEFAULT_HORIZONTAL);
            } else if (waterMark.getIntervalHorizontal() < 0) {
                throw new BusinessException(ErrorCodeEnum.PARAM_WATER_MARK_ERR.toErrorInfos(Placeholder.IntervalHorizontal.getCode(), " > 0"));
            }
            if (waterMark.getIntervalVertical() == null) {
                waterMark.setIntervalVertical(DEFAULT_VERTICAL);
            } else if (waterMark.getIntervalVertical() < 0) {
                throw new BusinessException(ErrorCodeEnum.PARAM_WATER_MARK_ERR.toErrorInfos(Placeholder.IntervalVertical.getCode(), " > 0"));
            }
            if (waterMark.getFontSize() == null) {
                waterMark.setFontSize(DEFAULT_FONT_SIZE);
            } else if (waterMark.getFontSize() < 0) {
                throw new BusinessException(ErrorCodeEnum.PARAM_WATER_MARK_ERR.toErrorInfos(Placeholder.FontSize.getCode(), "> 0"));
            }
            if (waterMark.getRotation() == null) {
                waterMark.setRotation(DEFAULT_ROTATION);
            } else if (waterMark.getRotation() > 90 || waterMark.getRotation() < 0) {
                throw new BusinessException(ErrorCodeEnum.PARAM_WATER_MARK_ERR.toErrorInfos(Placeholder.Rotation.getCode(), "0-90"));
            }
            if (waterMark.getAlpha() == null) {
                waterMark.setAlpha(DEFAULT_ALPHA);
            } else if (waterMark.getAlpha() > 1 || waterMark.getAlpha() < 0) {
                throw new BusinessException(ErrorCodeEnum.PARAM_WATER_MARK_ERR.toErrorInfos(Placeholder.Alpha.getCode(), "0-1"));
            }
            if (StringUtils.isBlank(waterMark.getFontColor())) {
                waterMark.setFontColor(DEFAULT_COLOR);
            } else {
                Pattern p = Pattern.compile(COLOR_REGEX);
                Matcher m = p.matcher(waterMark.getFontColor());
                if (m.matches()) {
                    waterMark.setFontColor(m.group(1));
                } else {
                    throw new BusinessException(ErrorCodeEnum.PARAM_WATER_MARK_ERR.toErrorInfos(Placeholder.FontColor.getCode(), "000000 - FFFFFF"));
                }
            }
        }
    }

}

单元测试测一下,完美输出

    @Test
    public void tempExport() throws Exception{
        String path ="C:\\Users\\qiankun.hu\\AppData\\Local\\Temp\eTemp-1653899322026";
        String name ="/1653899360909.docx";
        String modelPath = path+name;//设置需要加水印的文件路径
        //输入的docx文档
        InputStream in = new FileInputStream(new File(modelPath));

        TextWaterMarkDto waterMarkInfo = new TextWaterMarkDto();
        waterMarkInfo.setText("hello");
        waterMarkService.writeWithWaterMark(in,waterMarkInfo, path);

    }

工具类的代码贴一下



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;

/**
 * @author qiankun.hu
 * @version 1.0.0
 * @createTime 2022年05月30日 13:57:00
 * @Description word水印工具
 */
public class DocxUtil {
    /**
     * 水印参数
     */
   // private static final String fontColor = "#D3D3D3"; // 字体颜色

    /**
     * 艺术字水印参数
     */
    private static final String fontName = "PingFang SC"; // word字体
   // private static final String fontSize = "0.5pt"; // 字体大小
    private static final int widthPerWord = 10; // 一个字平均长度,单位pt,用于:计算文本占用的长度(文本总个数*单字长度)
   // private static final String styleRotation = "-45"; // 文本旋转角度


    /**
     * 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, 10); // 一行水印重复水印文字次数
        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");
        // 开始加水印
        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");
        if(type != 2){
            shape.setStyle(getShapeStyle(customText, styleTop,rotation)); // 设置形状样式(旋转,位置,相对路径等参数)
        }else{
            shape.setStyle(getShapeStyle()); // 设置形状样式(旋转,位置,相对路径等参数)
        }
        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);
    }

    /**
     * 加载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-vertical-relative: ").append("margin");
        sb.append(";mso-position-vertical: ").append("left");
        sb.append(";mso-position-horizontal: ").append("center");
        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();
    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值