Problem with sample code to set font properties for a whole XWPF Paragraph that includes Word fields

39 views Asked by At

I am using POI 5.2.3 and I am having problems with finding the getRPr methods isSetRFonts, isSetSz, isSetSzCs and the corresponding "gets" in setParagraphRunProperties in the answer to the question here: Apache-poi Java: How can I change the font name and size of a list numbering in a WORD document?

It seems that something changed in the POI since Axel Richter's reply above but I don't know where to look next after trawling the Apache POI change documents without success.

Any help will be much appreciated.

Axel Richter's original code is below.

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

 }
}

Tried complete sample code with correct POI 5.2.3 jar files but got compilation errors about unrecognised methods.

1

There are 1 answers

1
Axel Richter On

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
 }

Unfortunately neither Microsoft nor Apache POI documents reasons of those changes on XSDs and thus further on XMLBeans generated Java code. So programmers who wants using the XMLBeans-generated classes need deep knowledge about how all this works together.

There is no API documentation of the XMLBeans-generated classes online available. But we need this to check what methods are available. So we need downloading the sources of used poi-ooxml-full*.jar (since Apache POI 5) - ooxml-schemas-*.jar (up to Apache PPOI 4). Then we can create a API documentation from those sources using javadoc.

All Office Open XML files, so also *.docx, are simply ZIP archives. So one can unzip a *.docx file using a ZIP application (7-Zip can do that for example). Then we can have a look into the internals. Doing so one will see that for most changes of single child elements into list of child elements, the now possible lists of child elements are never used in pratice. Therefore, I suspect that these changes are simply due to inadequacies in Microsoft's XSDs.