我正在尝试使用DOCX
类用Apache POI
替换模板XWPFDocument
文档。我在doc和JSON
文件中有标签来读取替换数据。我的问题是,当我将其扩展名更改为DOCX
文件并打开ZIP
时,文本行似乎在document.xml
中以某种方式分开。例如,[MEMBER_CONTACT_INFO]
文字分别变为[MEMBER_CONTACT_INFO
和]
。 POI
以相同的方式读取此内容,因为DOCX
原文是这样的。这会在段落中创建2个XWPFRun
个对象,分别将文本显示为[MEMBER_CONTACT_INFO
和]
。
我的问题是,有没有办法强制POI
通过合并相关的运行或类似的东西来运行Word?或者我该如何解决这个问题?我在替换时匹配运行文本,我找不到我的标签,因为它被分成两个不同的运行对象。
最佳
答案 0 :(得分:5)
这浪费了我很多时间......
基本上,XWPFParagraph
由多个XWPFRun
组成,而XWPFRun是一个具有固定相同风格的传染性文本。
因此,当您尝试在MS-Word中编写类似“[PLACEHOLDER_NAME]”的内容时,它将创建一个XWPFRun。但是,如果你以某种方式添加更多内容,然后你返回并将“[PLACEHOLDER_NAME]”更改为其他内容,则永远无法保证它将保持单一XWPFRun
它很可能会分成两个运行。 AFAIK这就是MS-Word的工作原理。
如何在这种情况下避免分裂运行?
解决方案:我知道有两种解决方案:
将文字“[PLACEHOLDER_NAME]”复制到记事本或其他内容。进行必要的修改并将其复制并粘贴到您的word文件中,而不是“[PLACEHOLDER_NAME]”,这样整个“[PLACEHOLDER_NAME]”将被替换为新文本,避免拆分XWPFRnns。
选择“[PLACEHOLDER_NAME]”,然后点击MS-Word“替换”选项并替换为“[你的新编辑的占位符]”,这将保证你的新占位符将消耗一个XWPFRun
如果您必须再次更改新的占位符,请按照步骤1或2进行操作。
答案 1 :(得分:1)
以下是修复单独文本行问题的java代码。它还将处理多格式字符串替换。
public static void replaceString(XWPFDocument doc, String search, String replace) throws Exception{
for (XWPFParagraph p : doc.getParagraphs()) {
List<XWPFRun> runs = p.getRuns();
List<Integer> group = new ArrayList<Integer>();
if (runs != null) {
String groupText = search;
for (int i=0 ; i<runs.size(); i++) {
XWPFRun r = runs.get(i);
String text = r.getText(0);
if (text != null)
if(text.contains(search)) {
String safeToUseInReplaceAllString = Pattern.quote(search);
text = text.replaceAll(safeToUseInReplaceAllString, replace);
r.setText(text, 0);
}
else if(groupText.startsWith(text)){
group.add(i);
groupText = groupText.substring(text.length());
if(groupText.isEmpty()){
runs.get(group.get(0)).setText(replace, 0);
for(int j = 1; j<group.size(); j++){
p.removeRun(group.get(j));
}
group.clear();
groupText = search;
}
}else{
group.clear();
groupText = search;
}
}
}
}
for (XWPFTable tbl : doc.getTables()) {
for (XWPFTableRow row : tbl.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
for (XWPFParagraph p : cell.getParagraphs()) {
for (XWPFRun r : p.getRuns()) {
String text = r.getText(0);
if (text.contains(search)) {
String safeToUseInReplaceAllString = Pattern.quote(search);
text = text.replaceAll(safeToUseInReplaceAllString, replace);
r.setText(text);
}
}
}
}
}
}
}
答案 2 :(得分:0)
前几天我也遇到过这个问题,我找不到任何解决方案。我选择使用PLACEHOLDER_NAME而不是[PLACEHOLDER_NAME]。这对我来说很好,它看起来像一个XWPFRun对象。
答案 3 :(得分:0)
对我来说,它没有按我的预期(每次)运行。在我的案例中,我在文本中使用了“ $ {PLACEHOLDER}。首先,我们需要看一下Apache Poi如何识别要通过Runs进行迭代的每个段落。如果深入研究docx文件的构造,您将知道“运行”是具有相同字体样式/字体大小/颜色/粗体/斜体等的文本字符序列。这样,占位符有时被分成几部分,或者有时整个段落都被视为一个“运行”,并且不可能遍历单词。
我要做的是在模板文档中用粗体占位符名称。比遍历RUN时,我可以遍历整个占位符名称 $ {PLACEHOLDER} 。当我用
for (XWPFRun r : p.getRuns()) {
String text = r.getText(0);
if (text != null && text.contains("originalText")) {
text = text.replace("originalText", "newText");
r.setText(text,0);
}
}
我仅在setText之后添加了r.isBold(false);
。
这样,占位符被认为是另一种运行方式->我可以替换特定的占位符,并且在已处理的文档中,我没有加粗字体,只是纯文本。
对我来说,另一个好处是视觉效果好能够更快地找到文本中的占位符。
所以最后,上面的循环看起来像这样:
for (XWPFRun r : p.getRuns()) {
String text = r.getText(0);
if (text != null && text.contains("originalText")) {
text = text.replace("originalText", "newText");
r.setText(text,0);
r.isBold(false);
}
}
我希望这会对某人有所帮助,而我为此花了太多时间:)