Search code examples
javaapache-poi

Apache-poi Java: How can I change the font name and size of a list numbering in a WORD document?


I wrote a method to add numbering to some paragraphs of a word document, however I do not seem to be able to customize the font name and font size of the list numbering (example: a. my item 1, b. my item 2). I am able to customize the font in the paragraphs, however I am not able to customize the font name and font size of the list numbering. Is there anything I might be missing in the code below?

    public static BigInteger listNumbering(XWPFDocument doc, BigInteger abstractNumID) {
      CTAbstractNum cTAbstractNum = CTAbstractNum.Factory.newInstance();
      cTAbstractNum.setAbstractNumId(abstractNumID);

      CTLvl cTLvl = cTAbstractNum.addNewLvl();
      cTLvl.addNewNumFmt().setVal(STNumberFormat.DECIMAL);

      cTLvl.addNewLvlText().setVal("%1.");
      cTLvl.addNewStart().setVal(BigInteger.valueOf(1));

      XWPFAbstractNum abstractNum = new XWPFAbstractNum(cTAbstractNum);

      XWPFNumbering numbering = doc.createNumbering();

      abstractNumID = numbering.addAbstractNum(abstractNum);

      BigInteger numID = numbering.addNum(abstractNumID);

      return numID;
     }

Solution

  • Your code only creates a decimal numbering format for the document and gets the appropriate NumID. That NumID then needs to be applied to paragraphs in the document which shall be numbered. The format is determined by the paragraphs.

    The format of the numbering itself is determined by the paragraph's run properties. Unfortunately there is no method in XWPFParagraph to set paragraph run properties until now. So we need using low level ooxml-schemas classes to do so. In following code the method setParagraphRunProperties(XWPFParagraph paragraph, String fontFamily, int fontSize) is doing this.

    The format of the numbered paragraphs is determined by the format of it's text runs.

    Complete example:

    import java.io.FileOutputStream;
    
    import org.apache.poi.xwpf.usermodel.*;
    
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTAbstractNum;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTLvl;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.STNumberFormat;
    
    import java.math.BigInteger;
    
    public class CreateWordNumberingsFormatted {
    
     static BigInteger getNewDecimalNumberingId(XWPFDocument document, BigInteger abstractNumID) {
      CTAbstractNum cTAbstractNum = CTAbstractNum.Factory.newInstance();
      cTAbstractNum.setAbstractNumId(abstractNumID);
    
      CTLvl cTLvl = cTAbstractNum.addNewLvl();
      cTLvl.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
      cTLvl.addNewLvlText().setVal("%1.");
      cTLvl.addNewStart().setVal(BigInteger.valueOf(1));
    
      XWPFAbstractNum abstractNum = new XWPFAbstractNum(cTAbstractNum);
    
      XWPFNumbering numbering = document.createNumbering();
    
      abstractNumID = numbering.addAbstractNum(abstractNum);
    
      BigInteger numID = numbering.addNum(abstractNumID);
    
      return numID;
     }
    
     static void setParagraphRunProperties(XWPFParagraph paragraph, String fontFamily, int fontSize) {
      if (!paragraph.getCTP().isSetPPr()) paragraph.getCTP().addNewPPr();
      if (!paragraph.getCTP().getPPr().isSetRPr()) paragraph.getCTP().getPPr().addNewRPr();
      if (!paragraph.getCTP().getPPr().getRPr().isSetRFonts()) paragraph.getCTP().getPPr().getRPr().addNewRFonts();
      if (!paragraph.getCTP().getPPr().getRPr().isSetSz()) paragraph.getCTP().getPPr().getRPr().addNewSz();
      if (!paragraph.getCTP().getPPr().getRPr().isSetSzCs()) paragraph.getCTP().getPPr().getRPr().addNewSzCs();
      paragraph.getCTP().getPPr().getRPr().getRFonts().setAscii(fontFamily);
      paragraph.getCTP().getPPr().getRPr().getRFonts().setHAnsi(fontFamily);
      paragraph.getCTP().getPPr().getRPr().getSz().setVal(BigInteger.valueOf(fontSize*2)); //measurement unit is half pt
      paragraph.getCTP().getPPr().getRPr().getSzCs().setVal(BigInteger.valueOf(fontSize*2)); //measurement unit is half pt
     }
    
    
     public static void main(String[] args) throws Exception {
    
      XWPFDocument document = new XWPFDocument();
    
      XWPFParagraph paragraph = document.createParagraph();
      XWPFRun run = paragraph.createRun();  
      run.setText("The paragraph before first numbering:");
    
      int abstractNumID = 0;
    
      //get NumID for first decimal numbering
      BigInteger numID = getNewDecimalNumberingId(document, BigInteger.valueOf(abstractNumID++));
    
      //now apply that NumID to first list
      String fontFamily = "Times New Roman";
      int fontSize = 24;
      //first paragraph
      paragraph = document.createParagraph();
      //apply NumID
      paragraph.setNumID(numID);
      //set paragraph run properties
      setParagraphRunProperties(paragraph, fontFamily, fontSize);
      //create text runs and format
      run = paragraph.createRun(); 
      run.setFontFamily(fontFamily); 
      run.setFontSize(fontSize); 
      run.setText("One");
      //further numbered paragraphs
      paragraph = document.createParagraph();
      paragraph.setNumID(numID);
      setParagraphRunProperties(paragraph, fontFamily, fontSize);
      run = paragraph.createRun(); 
      run.setFontFamily(fontFamily); 
      run.setFontSize(fontSize); 
      run.setText("Two");
      paragraph = document.createParagraph();
      paragraph.setNumID(numID);
      setParagraphRunProperties(paragraph, fontFamily, fontSize);
      run = paragraph.createRun(); 
      run.setFontFamily(fontFamily); 
      run.setFontSize(fontSize); 
      run.setText("Three");
    
      paragraph = document.createParagraph();
    
      //get NumID forsecond decimal numbering
      numID = getNewDecimalNumberingId(document, BigInteger.valueOf(abstractNumID++));
    
      //now apply that NumID to second list having different font
      fontFamily = "Courier New";
      fontSize = 32;
      paragraph = document.createParagraph();
      paragraph.setNumID(numID);
      setParagraphRunProperties(paragraph, fontFamily, fontSize);
      run = paragraph.createRun(); 
      run.setFontFamily(fontFamily); 
      run.setFontSize(fontSize); 
      run.setText("One");
      paragraph = document.createParagraph();
      paragraph.setNumID(numID);
      setParagraphRunProperties(paragraph, fontFamily, fontSize);
      run = paragraph.createRun(); 
      run.setFontFamily(fontFamily); 
      run.setFontSize(fontSize); 
      run.setText("Two");
      paragraph = document.createParagraph();
      paragraph.setNumID(numID);
      setParagraphRunProperties(paragraph, fontFamily, fontSize);
      run = paragraph.createRun(); 
      run.setFontFamily(fontFamily); 
      run.setFontSize(fontSize); 
      run.setText("Three");
    
      paragraph = document.createParagraph();
    
      FileOutputStream out = new FileOutputStream("CreateWordNumberingsFormatted.docx");
      document.write(out);
      out.close();
      document.close();
    
     }
    }
    

    For usage with Apache POI 5

    Microsoft has changed its XSD which now states that there may be multiple font settings and multiple size settings in one run property. That's why XMLBeans now generates code for Lists and/or Arrays of those elements in CTRPr: CTRPr.getRFontsList now vs. CTRPr.getRFonts in past, CTRPr.getSzList now vs. CTRPr.getSz in past, ...

    So if the need is to check whether CTRPr has font settings, one needs to check whether CTRPr.getRFontsList returns a java.util.List<CTFonts> having size greater than 0. And to get the CTFonts, one needs to get the first item in that list then. Same for java.util.List<CTHpsMeasure> got from CTRPr.getSzList.

    See How to use Low Level CTRPr & CTPPr Classes of latest Apache POI 5.2.2 and Missing method after POI version upgrade from 4.x to 5.x for details.

    Following needs to be changed in above code to work using Apache POI 5:

     // version up to Apache POI 4
     // static void setParagraphRunProperties(XWPFParagraph paragraph, String fontFamily, int fontSize) {
      // if (!paragraph.getCTP().isSetPPr()) paragraph.getCTP().addNewPPr();
      // if (!paragraph.getCTP().getPPr().isSetRPr()) paragraph.getCTP().getPPr().addNewRPr();
      // if (!paragraph.getCTP().getPPr().getRPr().isSetRFonts()) paragraph.getCTP().getPPr().getRPr().addNewRFonts();
      // if (!paragraph.getCTP().getPPr().getRPr().isSetSz()) paragraph.getCTP().getPPr().getRPr().addNewSz();
      // if (!paragraph.getCTP().getPPr().getRPr().isSetSzCs()) paragraph.getCTP().getPPr().getRPr().addNewSzCs();
      // paragraph.getCTP().getPPr().getRPr().getRFonts().setAscii(fontFamily);
      // paragraph.getCTP().getPPr().getRPr().getRFonts().setHAnsi(fontFamily);
      // paragraph.getCTP().getPPr().getRPr().getSz().setVal(BigInteger.valueOf(fontSize*2)); //measurement unit is half pt
      // paragraph.getCTP().getPPr().getRPr().getSzCs().setVal(BigInteger.valueOf(fontSize*2)); //measurement unit is half pt
     // }
     // version from Apache POI 5
     static void setParagraphRunProperties(XWPFParagraph paragraph, String fontFamily, int fontSize) {
      if (!paragraph.getCTP().isSetPPr()) paragraph.getCTP().addNewPPr();
      if (!paragraph.getCTP().getPPr().isSetRPr()) paragraph.getCTP().getPPr().addNewRPr();
      if (paragraph.getCTP().getPPr().getRPr().getRFontsList().size() == 0) paragraph.getCTP().getPPr().getRPr().addNewRFonts();
      if (paragraph.getCTP().getPPr().getRPr().getSzList().size() == 0) paragraph.getCTP().getPPr().getRPr().addNewSz();
      if (paragraph.getCTP().getPPr().getRPr().getSzCsList().size() == 0) paragraph.getCTP().getPPr().getRPr().addNewSzCs();
      paragraph.getCTP().getPPr().getRPr().getRFontsArray(0).setAscii(fontFamily);
      paragraph.getCTP().getPPr().getRPr().getRFontsArray(0).setHAnsi(fontFamily);
      paragraph.getCTP().getPPr().getRPr().getSzArray(0).setVal(BigInteger.valueOf(fontSize*2)); //measurement unit is half pt
      paragraph.getCTP().getPPr().getRPr().getSzCsArray(0).setVal(BigInteger.valueOf(fontSize*2)); //measurement unit is half pt
     }