首页 > 解决方案 > 使用 Java Apache POI 从 docx 文件中更改特定单词的字体样式

问题描述

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

Lorem ipsum dolor sit amet, consectetur adipiscing elit。整数 pretium sodales nisl, ut ornare ligula vehicula vitae。Fusce non magna feugiat,sagittis massa at,发酵罐。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、非 pellentesque lectus ipsum ornare eros。Maecenas 在 magna nunc。

Nulla sagittis aliquam maximus。Cras faucibus id neque sed faucibus。Faucibus orci luctus et ultrices posuere cubilia curae 中的前庭前庭;Phasellus infantum 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。在 hac habitasse platea dictumst 中。类 aptent taciti socialsqu 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”一词的字体样式?

标签: javaapache-poidocxxwpf

解决方案


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

我已经在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这样的:

在此处输入图像描述


推荐阅读