Apache POI XWPFRun对象中的分隔文本行

时间:2013-10-02 13:16:01

标签: apache-poi

我正在尝试使用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?或者我该如何解决这个问题?我在替换时匹配运行文本,我找不到我的标签,因为它被分成两个不同的运行对象。

最佳

4 个答案:

答案 0 :(得分:5)

这浪费了我很多时间......

基本上,XWPFParagraph由多个XWPFRun组成,而XWPFRun是一个具有固定相同风格的传染性文本。

因此,当您尝试在MS-Word中编写类似“[PLACEHOLDER_NAME]”的内容时,它将创建一个XWPFRun。但是,如果你以某种方式添加更多内容,然后你返回并将“[PLACEHOLDER_NAME]”更改为其他内容,则永远无法保证它将保持单一XWPFRun它很可能会分成两个运行。 AFAIK这就是MS-Word的工作原理。

如何在这种情况下避免分裂运行?

解决方案:我知道有两种解决方案:

  1. 将文字“[PLACEHOLDER_NAME]”复制到记事本或其他内容。进行必要的修改并将其复制并粘贴到您的word文件中,而不是“[PLACEHOLDER_NAME]”,这样整个“[PLACEHOLDER_NAME]”将被替换为新文本,避免拆分XWPFRnns。

  2. 选择“[PLACEHOLDER_NAME]”,然后点击MS-Word“替换”选项并替换为“[你的新编辑的占位符]”,这将保证你的新占位符将消耗一个XWPFRun

  3. 如果您必须再次更改新的占位符,请按照步骤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);
         }
      }

我希望这会对某人有所帮助,而我为此花了太多时间:)