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

后端 未结 5 452
鱼传尺愫
鱼传尺愫 2021-01-14 09:15

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:

相关标签:
5条回答
  • 2021-01-14 09:28

    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.

    0 讨论(0)
  • 2021-01-14 09:35

    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;
    }
    
    0 讨论(0)
  • 2021-01-14 09:36

    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

    0 讨论(0)
  • 2021-01-14 09:38
    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

    0 讨论(0)
  • 2021-01-14 09:45

    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.

    0 讨论(0)
提交回复
热议问题