如何使用 Apache POI 突出显示替换的单词

How to Highlight Replaced Word Using Apache POI

如何突出显示替换的单词: 我正在使用此代码使用 Apache POI 查找和替换 docx 文件中的单词, 但我希望被替换的单词突出显示或更改其颜色。

       XWPFDocument doc = new XWPFDocument(
         OPCPackage.open("d:\1\rpt.docx"));
       for (XWPFParagraph p : doc.getParagraphs()) {
        List<XWPFRun> runs = p.getRuns();
        if (runs != null) {
         for (XWPFRun r : runs) {
          String text = r.getText(0);
          if (text != null && text.contains("$$key$$")) {
           text = text.replace("$$key$$", "ABCD");//your content
           r.setText(text, 0);
      }
     }
    }
   }

您的代码无法在所有情况下替换文本“$$key$$”。仅当文本完全包含在 Word 中的一个文本 运行 中时才有效。但在某些情况下,文本会被拆分成多个 运行。那么你的代码将不起作用。

我已经使用 TextSegment 提供了替换代码。参见 , and

但是由于额外的要求是格式化被替换的文本,所以只有将文本放入其自己的文本中才能解决这个问题运行。在 Word 中只有文本 运行 提供格式设置。

以下代码提供了一种方法 List<XWPFRun> getTextSegmentsInOwnRuns(XWPFParagraph paragraph, String textToFind),它能够为给定段落中每次出现的 textToFind 创建自己的文本 运行。它将 returns 那些文本 运行 作为 List<XWPFRun> 。此列表随后可用于替换文本和格式化。

完整示例:

import java.io.*;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.util.List;
import java.util.ArrayList;

public class WordReplaceTextSegmentAndFormat {
    
 static void cloneRunProperties(XWPFRun source, XWPFRun dest) { // clones the underlying w:rPr element
  CTR tRSource = source.getCTR();
  CTRPr rPrSource = tRSource.getRPr();
  if (rPrSource != null) {
   CTRPr rPrDest = (CTRPr)rPrSource.copy();
   CTR tRDest = dest.getCTR();
   tRDest.setRPr(rPrDest);
  }
 }
     
 static List<XWPFRun> getTextSegmentsInOwnRuns(XWPFParagraph paragraph, String textToFind) {
  TextSegment foundTextSegment = null;
  PositionInParagraph startPos = new PositionInParagraph(0, 0, 0); // start at position 0
  List<XWPFRun> resultRuns = new ArrayList<XWPFRun>();
  while((foundTextSegment = paragraph.searchText(textToFind, startPos)) != null) { // search all text segments having text to find

//System.out.println(foundTextSegment.getBeginRun()+":"+foundTextSegment.getBeginText()+":"+foundTextSegment.getBeginChar());
//System.out.println(foundTextSegment.getEndRun()+":"+foundTextSegment.getEndText()+":"+foundTextSegment.getEndChar());

   int posOfBeginRun = foundTextSegment.getBeginRun();
   // maybe there is text before textToFind in begin run
   XWPFRun beginRun = paragraph.getRuns().get(posOfBeginRun);
   String textInBeginRun = beginRun.getText(foundTextSegment.getBeginText());
   String textBefore = textInBeginRun.substring(0, foundTextSegment.getBeginChar()); // we only need the text before

   int posOfEndRun = foundTextSegment.getEndRun();
   // maybe there is text after textToFind in end run
   XWPFRun endRun = paragraph.getRuns().get(posOfEndRun);
   String textInEndRun = endRun.getText(foundTextSegment.getEndText());
   String textAfter = textInEndRun.substring(foundTextSegment.getEndChar() + 1); // we only need the text after
   
   XWPFRun resultRun = null;
   if (posOfEndRun == posOfBeginRun) { // there is only one run, split it up
    beginRun.setText(textBefore, foundTextSegment.getBeginText()); // begin run only contains text before textToFind
    resultRun = paragraph.insertNewRun(++posOfBeginRun); posOfEndRun++; // a new run added containing textToFind
    cloneRunProperties(beginRun, resultRun);
    resultRun.setText(textToFind, 0);
    resultRuns.add(resultRun);
    XWPFRun newEndRun = paragraph.insertNewRun(++posOfBeginRun); posOfEndRun++; // a new run added containing text after textToFind
    cloneRunProperties(beginRun, newEndRun);
    newEndRun.setText(textAfter, 0);
   } else { // there are multiple runs already, use beginRund and endRun, insert new run for textToFind
    beginRun.setText(textBefore, foundTextSegment.getBeginText()); // begin run only contains text before textToFind
    resultRun = paragraph.insertNewRun(++posOfBeginRun); posOfEndRun++; // a new run added containing textToFind
    cloneRunProperties(beginRun, resultRun);
    resultRun.setText(textToFind, 0);
    resultRuns.add(resultRun);
    endRun.setText(textAfter, foundTextSegment.getEndText()); // end run only contains text after textToFind
   }

   // runs between begin run and end run needs to be removed
   for (int runBetween = posOfEndRun - 1; runBetween > posOfBeginRun; runBetween--) {
    paragraph.removeRun(runBetween); // remove not needed runs
   }
   
   // start searchText from new position
   startPos = new PositionInParagraph(posOfEndRun, 0, 0);

  }
  return resultRuns;
 }
 
 public static void main(String[] args) throws Exception {

  String sourcePath = "./source.docx";
  String resultPath = "./result.docx";
  String textToFind = "$$key$$";

  try ( XWPFDocument document = new XWPFDocument(new FileInputStream(sourcePath));
        FileOutputStream out = new FileOutputStream(resultPath);
      ) {

   for (XWPFParagraph paragraph : document.getParagraphs()) {
    if (paragraph.getText().contains(textToFind)) {
     List<XWPFRun> runsWithTextToFind = getTextSegmentsInOwnRuns(paragraph, textToFind);
     System.out.println(runsWithTextToFind);
     for (XWPFRun run : runsWithTextToFind) {
      run.setText("Replaced", 0);
      //run.setUnderline(UnderlinePatterns.DOUBLE);
      run.getCTR().addNewRPr().addNewHighlight().setVal(STHighlightColor.YELLOW);
     }     
    }
   }  
   document.write(out); 
  }
 }
}