Samik
Samik

Reputation: 3455

How to create a bulleted list in Apache POI XWPF Document?

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:

  1. Create Numbering numbering = doc.createNumbering
  2. add AbstractNum to the Numbering and get the corresponding abstractNumId
  3. Add a Num with the AbstractNumId numId = numbering.addNum(abstractNumId)
  4. Now I can add numId to the paragraphs using para.setNumID(numId);

However I am stuck in the second step. How do I create an AbstractNum object that I can add to the numbering?

Upvotes: 4

Views: 16528

Answers (5)

Kanagasabapathy Rajkumar
Kanagasabapathy Rajkumar

Reputation: 1702

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

Upvotes: 0

Leonardo Silva
Leonardo Silva

Reputation: 79

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

Upvotes: 0

jamsandwich
jamsandwich

Reputation: 385

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;
}

Upvotes: 0

Blue
Blue

Reputation: 311

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.

Upvotes: 0

Ryan
Ryan

Reputation: 41

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.

Upvotes: 4

Related Questions