MTilsted
MTilsted

Reputation: 5520

How do I create a bulleted list in docx with Apache Poi 5?

I know there are lots of answers to this, including some on Stackoverflow: Apache POI bullet spacing

Problem

I can't get them to work with Apache Poi 5.0.0:

Java Code

import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STLevelSuffix;
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.util.ArrayList;
import java.util.Arrays;

import java.math.BigInteger;
// https://www.titanwolf.org/Network/q/15e5b419-e50e-426c-895d-d4a47d18e714/y
public class CreateWordTableWithBulletList3 {
    public static void main(String[] args) throws Exception {
        ArrayList < String > documentList = new ArrayList < String > (
            Arrays.asList(
                new String[] {
                    "One",
                    "Two",
                    "Three, new test"
                }));

        CTAbstractNum cTAbstractNum = CTAbstractNum.Factory.newInstance();
        //Next we set the AbstractNumId. This requires care.
        //Since we are in a new document we can start numbering from 0.
        //But if we have an existing document, we must determine the next free number first.
        cTAbstractNum.setAbstractNumId(BigInteger.valueOf(0));

        //Bullet list
        CTLvl cTLvl = cTAbstractNum.addNewLvl();
        cTLvl.addNewNumFmt().setVal(STNumberFormat.BULLET);
        cTLvl.addNewSuff().setVal(STLevelSuffix.SPACE);
        cTLvl.addNewLvlText().setVal("•");

        XWPFAbstractNum abstractNum = new XWPFAbstractNum(cTAbstractNum);
        XWPFDocument document = new XWPFDocument();
        XWPFNumbering numbering = document.createNumbering();

        BigInteger abstractNumID = numbering.addAbstractNum(abstractNum);
        BigInteger numID = numbering.addNum(abstractNumID);

        XWPFParagraph paragraph = document.createParagraph();
        XWPFRun run = paragraph.createRun();
        run.setText("The list having space between bulltet point and text:");

        for (String string: documentList) {
            paragraph = document.createParagraph();
            paragraph.setNumID(numID);
            // font size for bullet point in half pt
            paragraph.getCTP().getPPr().addNewRPr().addNewSz().setVal(BigInteger.valueOf(48));
            run = paragraph.createRun();
            run.setText(string);
            run.setFontSize(24);
        }

        paragraph = document.createParagraph();

        FileOutputStream out = new FileOutputStream("/tmp/CreateWordSimplestBulletList4.docx");
        document.write(out);
        out.close();
        document.close();

    }
}

Reproduce

Above program which I tested generates a numbered list with Apache Poi 5.0.0.

Note: While my original output failed the test in real Word (Mac) I am currently testing in Libre Office (7.1.5.2) on Fedora (Linux) and on Slack's Preview Word function. Both show a numbered list.

Finally I just created a new project, which just contains all the .jar files from poi5 (including all the dependencies but nothing more). That still showed a numbered list in my Libre Office.

Resulting output

The numbering.xml extracted from .docx:

<?xml version="1.0" encoding="UTF-8"?>
<w:numbering xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
   <w:abstractNum w:abstractNumId="0">
      <w:lvl>
         <w:numFmt w:val="bullet" />
         <w:suff w:val="space" />
         <w:lvlText w:val="•" />
      </w:lvl>
   </w:abstractNum>
   <w:num w:numId="1">
      <w:abstractNumId w:val="0" />
   </w:num>
</w:numbering>

And here is the document.xml:

<?xml version="1.0" encoding="UTF-8"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
   <w:body>
      <w:p>
         <w:r>
            <w:t>The list having space between bulltet point and text:</w:t>
         </w:r>
      </w:p>
      <w:p>
         <w:pPr>
            <w:numPr>
               <w:numId w:val="1" />
            </w:numPr>
            <w:rPr>
               <w:sz w:val="48" />
            </w:rPr>
         </w:pPr>
         <w:r>
            <w:rPr>
               <w:sz w:val="48" />
            </w:rPr>
            <w:t>One</w:t>
         </w:r>
      </w:p>
      <w:p>
         <w:pPr>
            <w:numPr>
               <w:numId w:val="1" />
            </w:numPr>
            <w:rPr>
               <w:sz w:val="48" />
            </w:rPr>
         </w:pPr>
         <w:r>
            <w:rPr>
               <w:sz w:val="48" />
            </w:rPr>
            <w:t>Two</w:t>
         </w:r>
      </w:p>
      <w:p>
         <w:pPr>
            <w:numPr>
               <w:numId w:val="1" />
            </w:numPr>
            <w:rPr>
               <w:sz w:val="48" />
            </w:rPr>
         </w:pPr>
         <w:r>
            <w:rPr>
               <w:sz w:val="48" />
            </w:rPr>
            <w:t>Three, new test. Lite. Still testing.</w:t>
         </w:r>
      </w:p>
      <w:p />
   </w:body>
</w:document>

I think the error is in there somewhere.

Upvotes: 0

Views: 680

Answers (1)

Axel Richter
Axel Richter

Reputation: 61852

In my examples about creating numbering in Microsoft Word I assumed that the indent level will default to 0 if it is not set. I had tested that using all word processing applications I had and it was the case. But that seems not to be the case using all word processing applications which can open *.docx files. So explicitly setting the indent level seems to be the most compatible solution.

So in the code for creating the CTLvl do explicitly set the indent level using cTLvl.setIlvl(BigInteger.valueOf(0)); // set indent level 0:

...
  //Bullet list
  CTLvl cTLvl = cTAbstractNum.addNewLvl();
  cTLvl.setIlvl(BigInteger.valueOf(0)); // set indent level 0
  cTLvl.addNewNumFmt().setVal(STNumberFormat.BULLET);
  cTLvl.addNewSuff().setVal(STLevelSuffix.SPACE);
  cTLvl.addNewLvlText().setVal("•");
...

I have updated all my examples accordingly.

Upvotes: 1

Related Questions