You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Apache POI将所有Docx内容控件替换为内部文本

解决方案:用Apache POI 3.17替换Word内容控件为带格式文本

核心思路

Apache POI 3.17中AbstractXWPFSDT确实仅提供只读访问,但可以通过操作内容控件的底层XML结构、提取内部文本与格式,再将控件节点替换为对应内容的方式实现需求。需要递归遍历文档所有元素(段落、表格单元格),处理其中的内容控件。

实现代码

import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;

public class ContentControlReplacer {

    public static void main(String[] args) throws Exception {
        // 加载目标文档
        XWPFDocument doc = new XWPFDocument(new FileInputStream("input.docx"));
        // 替换所有内容控件
        replaceAllContentControls(doc);
        // 保存处理后的文档(也可直接用于PDF转换)
        doc.write(new FileOutputStream("processed.docx"));
        doc.close();
    }

    public static void replaceAllContentControls(XWPFDocument doc) {
        // 处理文档根段落
        List<XWPFParagraph> paragraphs = new ArrayList<>(doc.getParagraphs());
        for (XWPFParagraph para : paragraphs) {
            replaceSDTsInParagraph(para);
        }

        // 处理表格内的内容控件
        List<XWPFTable> tables = new ArrayList<>(doc.getTables());
        for (XWPFTable table : tables) {
            for (XWPFTableRow row : table.getRows()) {
                for (XWPFTableCell cell : row.getTableCells()) {
                    List<XWPFParagraph> cellParas = new ArrayList<>(cell.getParagraphs());
                    for (XWPFParagraph para : cellParas) {
                        replaceSDTsInParagraph(para);
                    }
                }
            }
        }
    }

    private static void replaceSDTsInParagraph(XWPFParagraph para) {
        List<IBodyElement> elements = new ArrayList<>(para.getBodyElements());
        for (IBodyElement element : elements) {
            if (element instanceof XWPFSDT) {
                XWPFSDT sdt = (XWPFSDT) element;
                int index = para.getBodyElements().indexOf(element);
                
                // 先移除原内容控件
                para.removeBodyElement(index);
                
                // 处理控件内部的嵌套内容控件(递归)
                List<XWPFParagraph> sdtParas = sdt.getContent().getParagraphs();
                for (XWPFParagraph sdtPara : sdtParas) {
                    replaceSDTsInParagraph(sdtPara);
                    
                    // 复制控件内的文本与格式到原位置
                    for (XWPFRun run : sdtPara.getRuns()) {
                        XWPFRun newRun = para.insertNewRun(index);
                        copyRunFormat(run, newRun);
                        newRun.setText(run.getText(0));
                        index++;
                    }
                    
                    // 保留原段落的换行/间距设置
                    if (sdtPara.getSpacingAfter() > 0) {
                        para.createRun().addBreak();
                    }
                }
            }
        }
    }

    private static void copyRunFormat(XWPFRun source, XWPFRun target) {
        // 复制基础格式
        target.setFontFamily(source.getFontFamily());
        target.setFontSize(source.getFontSize());
        target.setBold(source.isBold());
        target.setItalic(source.isItalic());
        target.setUnderline(source.getUnderline());
        target.setColor(source.getColor());
        
        // 复制完整的XML格式属性(覆盖更多细节)
        if (source.getCTR() != null && source.getCTR().getRPr() != null) {
            CTRPr rPr = source.getCTR().getRPr();
            target.getCTR().setRPr((CTRPr) rPr.copy());
        }
    }
}

关键说明

  1. 递归遍历:不仅处理文档根段落,还会遍历表格单元格内的段落,同时处理嵌套的内容控件。
  2. 格式保留:通过copyRunFormat方法复制字体、字号、加粗、斜体等所有文本格式,确保替换后的文本与原控件内格式一致。
  3. 集合安全遍历:将段落元素存入ArrayList后再遍历,避免因集合结构变化导致的遍历异常。

处理后的文档可直接用于fr.opensagres.poi.xwpf.converter.pdf转换,控件内的带格式文本会被正常渲染。

内容的提问来源于stack exchange,提问作者Augusto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 07:52:47