使用 Java Apache POI 更改 docx 文件中特定单词的字体样式

Change font style in a specific word from docx file using Java Apache POI

我正在使用 Apache POI XWPF 操作一个 docx 文件,我需要更新段落的一些单词并更改它的字体样式。对于更新单个单词,没关系,假设我的 docx 内容有以下段落:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer pretium sodales nisl, ut ornare ligula vehicula vitae. Fusce non magna feugiat, sagittis massa at, fermentum nibh. Curabitur auctor leo vitae sem tempus, facilisis feugiat orci vestibulum. Mauris molestie sem sem, id venenatis arcu congue id. Duis nulla quam, commodo vel dolor eget, tempor varius sem. Pellentesque gravida, lectus eu mollis pretium, sapien nibh consectetur lacus, non pellentesque lectus ipsum ornare eros. Maecenas at magna nunc.

Nulla sagittis aliquam maximus. Cras faucibus id neque sed faucibus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Phasellus fermentum nulla sed nibh varius maximus. Pellentesque dui lorem, luctus non lorem a, blandit lacinia arcu. Nunc porttitor erat ut elit hendrerit malesuada. Sed ut ex ultricies, rutrum est ut, vulputate orci. Suspendisse vitae diam ullamcorper, pulvinar tellus vitae, feugiat ex. In hac habitasse platea dictumst. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Proin massa lectus, venenatis eget massa a, fringilla molestie nisl.

我只是做了类似的事情,在这种情况下,代码有效,我只想将单词“ipsum”更新为“hello world”:

   File file = new File("document.docx");
   FileInputStream fis = new FileInputStream(file.getAbsolutePath());

   XWPFDocument document = new XWPFDocument(fis);

   List<XWPFParagraph> paragraphs = document.getParagraphs();

    for (XWPFParagraph p: paragraphs) {
        List<XWPFRun> runs = p.getRuns();
        for (XWPFRun r: runs) {
           String endText = r.getText(0).replaceFirst("ipsum" , "hello world");
           r.setText(endText,0);
        }
    }

        document.write(new FileOutputStream(uuid + ".docx"));
        fis.close();
        document.close();

但如果我尝试更改文本样式,例如粗体,甚至更改字体颜色,则所有“运行”都会发生变化,而不仅仅是我搜索过的世界。我需要做些什么才能仅更改示例文本中单词“ipsum”的字体样式?

您已经得出结论,文本 运行 是可能具有文本格式的最低文本实体。因此,需要将一个或多个格式不同的单词放入它们自己的文本 运行s 中。然后可以格式化这些文本 运行s.

我已经在 Split a XWPFRun into multiple runs 中回答了这个问题。但我会针对您的特殊情况再次展示它。

souce.docx 是这样的:

代码:

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

import java.util.*;
import java.awt.Desktop;

public class WordFormatWords {

 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 void formatWord(XWPFParagraph paragraph, String keyword, Map<String, String> formats) {
  int runNumber = 0;
  while (runNumber < paragraph.getRuns().size()) { //go through all runs, we cannot use for each since we will possibly insert new runs
   XWPFRun run = paragraph.getRuns().get(runNumber);
   XWPFRun run2 = run;
   String runText = run.getText(0);
   if (runText != null && runText.contains(keyword)) { //if we have a run with keyword in it, then

    char[] runChars = runText.toCharArray(); //split run text into characters
    StringBuffer sb = new StringBuffer();
    for (int charNumber = 0; charNumber < runChars.length; charNumber++) { //go through all characters in that run
     sb.append(runChars[charNumber]); //buffer all characters
     runText = sb.toString();
     if (runText.endsWith(keyword)) { //if the bufferend character stream ends with the keyword  
      //set all chars, which are current buffered, except the keyword, as the text of the actual run
      run.setText(runText.substring(0, runText.length() - keyword.length()), 0); 
      run2 = paragraph.insertNewRun(++runNumber); //insert new run for the formatted keyword
      cloneRunProperties(run, run2); // clone the run properties from original run
      run2.setText(keyword, 0); // set the keyword in run
      for (String toSet : formats.keySet()) { // do the additional formatting
       if ("color".equals(toSet)) {
        run2.setColor(formats.get(toSet));
       } else if ("bold".equals(toSet)) {
        run2.setBold(Boolean.valueOf(formats.get(toSet)));
       }
      }
      run2 = paragraph.insertNewRun(++runNumber); //insert a new run for the next characters
      cloneRunProperties(run, run2); // clone the run properties from original run
      run = run2;
      sb = new StringBuffer(); //empty the buffer
     } 
    }
    run.setText(sb.toString(), 0); //set all characters, which are currently buffered, as the text of the actual run

   }
   runNumber++;
  }
 }


 public static void main(String[] args) throws Exception {

  XWPFDocument doc = new XWPFDocument(new FileInputStream("source.docx"));

  String[] keywords = new String[]{"ipsum"};
  Map<String, String> formats = new HashMap<String, String>();
  formats.put("bold", "true");
  formats.put("color", "DC143C");

  for (XWPFParagraph paragraph : doc.getParagraphs()) { //go through all paragraphs
   for (String keyword : keywords) {
    formatWord(paragraph, keyword, formats);
   }
  }

  FileOutputStream out = new FileOutputStream("result.docx");
  doc.write(out);
  out.close();
  doc.close();

  System.out.println("Done");
  Desktop.getDesktop().open(new File("result.docx"));

 }
}

此代码导致 result.docx 如下所示: