如何使用 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);
}
}
}
如何突出显示替换的单词: 我正在使用此代码使用 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
提供了替换代码。参见
但是由于额外的要求是格式化被替换的文本,所以只有将文本放入其自己的文本中才能解决这个问题运行。在 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);
}
}
}