如何使用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()); } } }
关键说明
- 递归遍历:不仅处理文档根段落,还会遍历表格单元格内的段落,同时处理嵌套的内容控件。
- 格式保留:通过
copyRunFormat方法复制字体、字号、加粗、斜体等所有文本格式,确保替换后的文本与原控件内格式一致。 - 集合安全遍历:将段落元素存入
ArrayList后再遍历,避免因集合结构变化导致的遍历异常。
处理后的文档可直接用于fr.opensagres.poi.xwpf.converter.pdf转换,控件内的带格式文本会被正常渲染。
内容的提问来源于stack exchange,提问作者Augusto
相关产品推荐
相关产品推荐

