美文网首页
文件处理(四):word文本替换、添加水印等

文件处理(四):word文本替换、添加水印等

作者: alex很累 | 来源:发表于2022-02-17 00:22 被阅读0次

    一、概述

    在这篇博客中,整理了一些word的处理方法:替换普通段落文本、替换表格中的文本、添加文字水印等。

    二、依赖

    依赖和上一篇博客相同,在这里不再赘述。

    三、相关工具类

    DocxUtil
    可直接食用,也可以根据自己的需求做一些修改。

    public class DocxUtil {
        /**
         * 水印参数
         */
        private static final String fontColor = "#D3D3D3"; // 字体颜色
    
        /**
         * 艺术字水印参数
         */
        private static final String fontName = "华文行楷"; // 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) {
            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) {
                com.microsoft.schemas.vml.CTShape ctshape = (com.microsoft.schemas.vml.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) {
            customText = customText + repeatString(" ", 8); // 水印文字之间使用8个空格分隔
            customText = repeatString(customText, 10); // 一行水印重复水印文字次数
            String styleTop = "0pt";  // 与顶部的间距
    
            if (docx == null) {
                return;
            }
            // 遍历文档,添加水印
            for (int lineIndex = -10; lineIndex < 20; lineIndex++) {
                styleTop = 100 * lineIndex + "pt";
                waterMarkDocXDocument(docx, customText, styleTop, 1);
            }
        }
    
        /**
         * 以艺术字方式加上水印(单个)
         * @param docx XWPFDocument对象
         * @param customText 水印文字
         */
        public static void makeWaterMarkByWordArt(XWPFDocument docx, String customText) {
            String styleTop = "0pt";  // 与顶部的间距
    
            if (docx == null) {
                return;
            }
            // 添加水印
            waterMarkDocXDocument(docx, customText, styleTop, 2);
        }
    
        /**
         * 将指定的字符串重复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 org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy#getWatermarkParagraph(String, int)}
         * @param doc 需要被处理的docx文档对象
         * @param customText 水印文本
         * @param type 类型:1.平铺;2.单个
         */
        private static void waterMarkDocXDocument(XWPFDocument doc, String customText, String styleTop, int type) {
            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)); // 设置形状样式(旋转,位置,相对路径等参数)
            }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) {
            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();
        }
    
        /**
         * 替换word段落文本
         * @param docx
         * @param datamap
         */
        public static void replaceTextData(XWPFDocument docx, Map<String, Object> datamap){
            // 遍历所有的段落对象,将标记好的文本替换成我们想要的数据(这里无法处理表格,表格要另外处理)
            // 获取所有的段落
            List<XWPFParagraph> paragraphs = docx.getParagraphs();
            // 遍历所有的段落
            for (int i = 0; i < paragraphs.size(); i++) {
                // 获取该段所有的文本对象
                List<XWPFRun> runs = paragraphs.get(i).getRuns();
                for (int j = 0; j < runs.size(); j++) {
                    XWPFRun run = runs.get(j);
                    // 匹配内容,进行替换
                    if(run != null && StrUtil.isNotEmpty(run.toString())){
                        for(String key : datamap.keySet()){
                            if(run.toString().contains(key)){
                                run.setText(run.toString().replace(key, datamap.get(key).toString()), 0);
                            }
                        }
                    }
                }
            }
        }
    
        /**
         * 替换word表格内容
         * @param table XWPFTable对象
         * @param datamap 数据
         */
        public static void replaceTableData(XWPFTable table, Map<String, Object> datamap){
            // 获取这个表格所有的行
            List<XWPFTableRow> rows = table.getRows();
            // 遍历每一行
            for (XWPFTableRow xwpfTableRow : rows) {
                // 获取当前行所有的单元格
                List<XWPFTableCell> cells = xwpfTableRow.getTableCells();
                for (XWPFTableCell xwpfTableCell : cells) {
                    // 获取单元格中的文本段落
                    List<XWPFParagraph> para = xwpfTableCell.getParagraphs();
                    for (XWPFParagraph xwpfParagraph : para) {
                        List<XWPFRun> runs = xwpfParagraph.getRuns();
                        // 遍历文本段落,替换成我们想要的数据
                        for (int i = 0; i < runs.size(); i++) {
                            XWPFRun run = runs.get(i);
                            if(run != null && StrUtil.isNotEmpty(run.toString())){
                                // 匹配内容,进行替换
                                for(String key : datamap.keySet()){
                                    if(run.toString().contains(key)){
                                        run.setText(run.toString().replace(key, datamap.get(key).toString()), 0);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    

    四、示例

    1. 替换word段落文本(即非表格中的文本)

    效果

    替换前
    替换后
    示例代码
        // 1.获取word需要的数据(这里简化操作,手动造数据)
        Order order = new Order("XF-001", "xxxx", "xxxx", "xxxx",
                "xxxx", "xxxx", "xxxx", "xx", "987654321",
                "xx", "xx");
        // 2.将数据转成map
        HashMap<String, Object> datamap = JSONObject.parseObject(JSONObject.toJSONString(order), HashMap.class);
        // 3.获取docx模板,创建XWPFDocument对象,操作word
        InputStream is = new FileInputStream(xfConfig.rsrootpath + xfConfig.filepath + "/order_document.docx");
        XWPFDocument docx = new XWPFDocument(is);
        is.close();
        // 4.替换文本
        DocxUtil.replaceTextData(docx, datamap);
    

    关键方法
    DocxUtil.replaceTextData(XWPFDocument docx, Map<String, Object> datamap);
    用map的方式,找到文本进行替换。

    2.替换word表格内容

    效果

    替换前 替换后

    示例代码

    // 1.获取word需要的数据(这里简化操作,手动造数据)
    Order order = new Order("XF-001", "xxxx", "xxxx", "xxxx",
                "xxxx", "xxxx", "xxxx", "xx", "987654321",
                "xx", "xx");
    // 2.将数据转成map
    HashMap<String, Object> datamap = JSONObject.parseObject(JSONObject.toJSONString(order), HashMap.class);
    // 3.获取docx模板,创建XWPFDocument对象,操作word
    InputStream is = new FileInputStream(xfConfig.rsrootpath + xfConfig.filepath + "/order_document.docx");
    XWPFDocument docx = new XWPFDocument(is);
    is.close();
    // 4.处理表格数据
    // 获取word的所有表格
    List<XWPFTable> tables = docx.getTables();
    // 获取我们想要操作的表格(表格从上到下,序号依次为0、1、2...)
    XWPFTable infotable = tables.get(0);
    // 替换数据
    DocxUtil.replaceTableData(infotable, datamap);
    

    关键方法
    DocxUtil.replaceTableData(XWPFTable table, Map<String, Object> datamap);

    3.添加水印

    A.单个文字水印(调用poi方法)

    效果


    示例代码
    // 1.获取docx模板,创建XWPFDocument对象,操作word
    InputStream is = new FileInputStream(xfConfig.rsrootpath + xfConfig.filepath + "/order_document.docx");
    XWPFDocument docx = new XWPFDocument(is);
    is.close();
    // 2.添加水印
    DocxUtil.setWordWaterMark(docx, "内部资料");
    

    关键方法
    DocxUtil.setWordWaterMark(XWPFDocument doc, String markStr);

    缺陷
    仔细看这个方法的实现,可以发现调用了poi封装的createWatermark方法;这里存在一些问题,在poi4.1的版本中,水印无法平铺;以及遇到带页眉的word添加不上去。(具体记不清了。。。这是很久以前写的)

    B.单个文字水印(重写poi方法)

    效果

    image.png
    示例代码
    // 1.获取源文件
    InputStream is = new FileInputStream(xfConfig.rsrootpath + xfConfig.filepath + "/order_document.docx");
    XWPFDocument temp = new XWPFDocument(is);
    // 2.添加水印
    DocxUtil.makeWaterMarkByWordArt(temp, "内部资料");
    
    C.平铺文字水印(重写poi方法)

    效果

    image.png

    示例代码

    // 1.获取源文件
    InputStream is = new FileInputStream(xfConfig.rsrootpath + xfConfig.filepath + "/order_document.docx");
    XWPFDocument temp = new XWPFDocument(is);
    // 2.添加水印
    DocxUtil.makeFullWaterMarkByWordArt(temp, "内部资料");
    

    相关文章

      网友评论

          本文标题:文件处理(四):word文本替换、添加水印等

          本文链接:https://www.haomeiwen.com/subject/tpdjlrtx.html