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

Tags: , , ,



I’m using Apache POI XWPF to manipulate a docx file, I need to update some words of paragraph and change the font style of it. For updating a single word, it’s ok, let’s assume that my docx content has the paragraphs bellow:

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.

I just do something like it, in this case, the code works, I just want to update the word “ipsum” to “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();

But if I try to change the text style, like bold, or even change the font color, all of “run” will change, not only the world that I’ve searched. What I have to do to change the font style only of the word “ipsum” in the example text?

Answer

You have concluded already that the text run is the lowest text entity which may have text formatting. So the need is taking a word or words which shall be formatted differently into their own text runs. Then one can format those text runs.

I have answered this already in Split a XWPFRun into multiple runs. But I will show it again for your special case.

Having the souce.docx like this:

enter image description here

Code:

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

 }
}

This code leads to a result.docx like this:

enter image description here



Source: stackoverflow