I want to create a bulleted/numbered list in a docx word document with Java. I am using the Apache POI 3.10 library. If I understand correctly, the steps would be like this:
I was trying to do something similar and hit my head up against it until it started working.
Here was my approach to adding AbstractNum to the document's numbering object. Calling 'addAbstractNum()' turns out to have a null bug in the version I am using (3.10-FINAL) for doing this. So to get around it, you will need to generate your AbstractNum in XML, parse it, manually assign it an id, and add it to the document's numbering object.
This is how I did it:
protected XWPFDocument doc;
private BigInteger addListStyle(String style)
{
try
{
XWPFNumbering numbering = doc.getNumbering();
// generate numbering style from XML
CTAbstractNum abstractNum = CTAbstractNum.Factory.parse(style);
XWPFAbstractNum abs = new XWPFAbstractNum(abstractNum, numbering);
// find available id in document
BigInteger id = BigInteger.valueOf(0);
boolean found = false;
while (!found)
{
Object o = numbering.getAbstractNum(id);
found = (o == null);
if (!found) id = id.add(BigInteger.ONE);
}
// assign id
abs.getAbstractNum().setAbstractNumId(id);
// add to numbering, should get back same id
id = numbering.addAbstractNum(abs);
// add to num list, result is numid
return doc.getNumbering().addNum(id);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
The format of the 'style' string, that is passed to the method, requires knowledge of XWPF documents -- of which, I have none. So I created a word document with a numbering style that I wanted. Saved it to a '.docx' file. Unzipped the '.docx' file, and copied the XML fragment from 'numbering.xml'. It will look something like this:
<xml-fragment xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 wp14"><w:nsid w:val="1656060D" /><w:multiLevelType w:val="hybridMultilevel" /><w:tmpl w:val="99FCFC1A" /><w:lvl w:ilvl="0" w:tplc="0409000F"><w:start w:val="1" /><w:numFmt w:val="decimal" /><w:lvlText w:val="%1." /><w:lvlJc w:val="left" /><w:pPr><w:ind w:left="720" w:hanging="360" /></w:pPr></w:lvl><w:lvl w:ilvl="1" w:tplc="04090019" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="lowerLetter" /><w:lvlText w:val="%2." /><w:lvlJc w:val="left" /><w:pPr><w:ind w:left="1440" w:hanging="360" /></w:pPr></w:lvl><w:lvl w:ilvl="2" w:tplc="0409001B" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="lowerRoman" /><w:lvlText w:val="%3." /><w:lvlJc w:val="right" /><w:pPr><w:ind w:left="2160" w:hanging="180" /></w:pPr></w:lvl><w:lvl w:ilvl="3" w:tplc="0409000F" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="decimal" /><w:lvlText w:val="%4." /><w:lvlJc w:val="left" /><w:pPr><w:ind w:left="2880" w:hanging="360" /></w:pPr></w:lvl><w:lvl w:ilvl="4" w:tplc="04090019" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="lowerLetter" /><w:lvlText w:val="%5." /><w:lvlJc w:val="left" /><w:pPr><w:ind w:left="3600" w:hanging="360" /></w:pPr></w:lvl><w:lvl w:ilvl="5" w:tplc="0409001B" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="lowerRoman" /><w:lvlText w:val="%6." /><w:lvlJc w:val="right" /><w:pPr><w:ind w:left="4320" w:hanging="180" /></w:pPr></w:lvl><w:lvl w:ilvl="6" w:tplc="0409000F" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="decimal" /><w:lvlText w:val="%7." /><w:lvlJc w:val="left" /><w:pPr><w:ind w:left="5040" w:hanging="360" /></w:pPr></w:lvl><w:lvl w:ilvl="7" w:tplc="04090019" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="lowerLetter" /><w:lvlText w:val="%8." /><w:lvlJc w:val="left" /><w:pPr><w:ind w:left="5760" w:hanging="360" /></w:pPr></w:lvl><w:lvl w:ilvl="8" w:tplc="0409001B" w:tentative="1"><w:start w:val="1" /><w:numFmt w:val="lowerRoman" /><w:lvlText w:val="%9." /><w:lvlJc w:val="right" /><w:pPr><w:ind w:left="6480" w:hanging="180" /></w:pPr></w:lvl></xml-fragment>
Take that string, pass it to the method above. Now you have a numID that you can make lists out of.
XWPFParagraph para = doc.createParagraph();
para.setStyle("ListParagraph");
para.setNumID(listType);
para.getCTP().getPPr().getNumPr().addNewIlvl().setVal(BigInteger.valueOf(level));
Good Luck.
I found the easiest way was to extract numbering.xml from a Word document that has the style you want, and then manually recreate the important tags from the XML tree using Java. It sounds a bit painful, but it's much more precise than trying to parse something the MS Word has created. And the CT functions are named after the XML nodes that they affect, so it's fairly intuitive once you get the idea.
For example in my case (this will return the correct CTAbstractNum
for either a bulleted or numbered single-level list):
private static CTAbstractNum getAbstractNumber(STNumberFormat.Enum numFmt) {
CTAbstractNum ctAbsNum = CTAbstractNum.Factory.newInstance();
CTLvl lvl = ctAbsNum.addNewLvl(); //Add a level
CTDecimalNumber start = lvl.addNewStart(); //Set the starting number
start.setVal(BigInteger.ONE);
CTNumFmt fmt = lvl.addNewNumFmt(); //Set the number format
fmt.setVal(numFmt);
//Add the text that's used for the bullet point
CTLevelText lt = lvl.addNewLvlText();
if (numFmt == STNumberFormat.BULLET) {
lt.setVal("");
lvl.addNewRPr(); //Set the Symbol font
CTFonts f = lvl.getRPr().addNewRFonts();
f.setAscii("Symbol");
f.setHAnsi("Symbol");
}
else { //Decimal
lt.setVal("%1.");
}
lvl.addNewPPr();
CTInd ind = lvl.getPPr().addNewInd(); //Set the indent
ind.setHanging(BigInteger.valueOf(360));
ind.setLeft(BigInteger.valueOf(720));
System.out.println(ctAbsNum);
return ctAbsNum;
}
Open the document Get the XWPFNumbering object from the document. Get the list of XWPFParagraphs from the document. Step through the list of paragraphs and as you get each one try to get the number ID from it. If this returns null then the paragraph is not in a list. If it does not return null, you can use the BigInteger to retrieve, from the XWPFNumbering object, details of the numbering/bulleting scheme applied to the paragraph.
I referred from this website
static void addCustomHeadingStyle(XWPFDocument docxDocument, XWPFStyles styles, String strStyleId, int headingLevel, int pointSize, String hexColor) {
CTStyle ctStyle = CTStyle.Factory.newInstance();
ctStyle.setStyleId(strStyleId);
CTString styleName = CTString.Factory.newInstance();
styleName.setVal(strStyleId);
ctStyle.setName(styleName);
CTDecimalNumber indentNumber = CTDecimalNumber.Factory.newInstance();
indentNumber.setVal(BigInteger.valueOf(headingLevel));
// lower number > style is more prominent in the formats bar
ctStyle.setUiPriority(indentNumber);
CTOnOff onoffnull = CTOnOff.Factory.newInstance();
ctStyle.setUnhideWhenUsed(onoffnull);
// style shows up in the formats bar
ctStyle.setQFormat(onoffnull);
// style defines a heading of the given level
CTPPr ppr = CTPPr.Factory.newInstance();
ppr.setOutlineLvl(indentNumber);
ppr.addNewNumPr().addNewNumId().setVal(BigInteger.ONE);
ctStyle.setPPr(ppr);
XWPFStyle style = new XWPFStyle(ctStyle);
CTHpsMeasure size = CTHpsMeasure.Factory.newInstance();
size.setVal(new BigInteger(String.valueOf(pointSize)));
CTHpsMeasure size2 = CTHpsMeasure.Factory.newInstance();
size2.setVal(new BigInteger("24"));
CTFonts fonts = CTFonts.Factory.newInstance();
fonts.setAscii("Calibri Light");
CTRPr rpr = CTRPr.Factory.newInstance();
rpr.setRFonts(fonts);
rpr.setSz(size);
rpr.setSzCs(size2);
style.setType(STStyleType.PARAGRAPH);
styles.addStyle(style);
}
}
it is possible to add custom styles, for creating titles and index, this answer is not mine just some changes to add the number
I too used the same steps you have mentioned and for the second step, I have used the following statement.
BigInteger abstractNumId = BigInteger.valueOf(0);
With this I was able to create Bulleted list. However, I have still not found a way to create Numbered lists.