7

我正在尝试通过使用该类将模板DOCX文档替换为 Apache 。我在文档中有标签和一个文件来读取替换数据。我的问题是,当我将其扩展名更改为file 和 open时,文本行似乎以某种方式分开。例如文本变成和分开。以同样的方式阅读它,因为原版是这样的。这会在段落中创建 2 个对象,分别显示文本。POIXWPFDocumentJSONDOCXZIPdocument.xml[MEMBER_CONTACT_INFO][MEMBER_CONTACT_INFO]POIDOCXXWPFRun[MEMBER_CONTACT_INFO]

我的问题是,有没有办法POI通过合并相关运行或类似的东西来强制像 Word 一样运行?或者我该如何解决这个问题?我在替换时匹配运行文本,但我找不到我的标签,因为它被分成 2 个不同的运行对象。

最好的

4

4 回答 4

14

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

基本上,anXWPFParagraph是由多个XWPFRuns组成的,而XWPFRun是具有固定相同样式的具有传染性的文本。

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

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

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

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

  2. 选择“[PLACEHOLDER_NAME]”,然后单击 MS-Word 的“替换”选项并替换为“[Your-new-edited-placeholder]”,这将保证您的新占位符将使用单个 XWPFRun。

如果您必须再次更改新占位符,请按照步骤 1 或 2。

于 2015-07-08T07:56:41.517 回答
1

对我来说,它没有像我预期的那样工作(每次)。在我的例子中,我在文本中使用了 "${PLACEHOLDER}。首先,我们需要看看 Apache Poi 如何识别我们想要使用 Runs 迭代的每个段落。如果你更深入地了解 docx 文件构造,你会知道那个run 是具有相同字体样式/字体大小/颜色/粗体/斜体等的文本字符序列。这样,占位符有时被分成几部分,或者有时整个段落被识别为一个 Run,并且不可能遍历单词。
我所做的是在模板文档中加粗占位符名称。比通过 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);
     }
  }

r.isBold(false);在 setText 之后添加了。
这样,占位符被识别为不同的运行->我可以替换特定的占位符,并且在处理的文档中我没有粗体,只有纯文本。
对我来说,另一个优势是视觉上我能够更快地在文本中找到占位符。所以最后上面的循环看起来像这样:

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);
         }
      }

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

于 2020-02-04T07:34:26.183 回答
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);
              }
            }
         }
      }
   }
}

}

于 2017-07-19T14:53:26.747 回答
0

几天前我也遇到了这个问题,我找不到任何解决方案。我选择使用 PLACEHOLDER_NAME 而不是 [PLACEHOLDER_NAME]。这对我来说很好,它看起来像一个 XWPFRun 对象。

于 2013-12-06T15:05:51.607 回答