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;
}
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 List
s and/or Array
s 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
}