I need to have bullet list in word document with indention and custom bullets, which is generated via Java with Apache POI API. I have searched and I cant find it for Word document. It is available for Powerpoint slide using text-box. But I do not want to use text-box. Please let me know the possibilities to achieve it. Any help much appreciated. Thanks! With Regards, Arun Ganesh. P
2 Answers
This is really only possible with the 2007 OOXML format and up (using POI's XWPF). Since it is based on XML you can always perform DOM manipulation to achieve what you want. The easiest way to make bullet points is to create a bullet list and add a bookmark as the text of that bullet. When processing the document, find your bookmark, then get the DOM node with
`org.w3c.dom.Node bkmk = bookmark.getctBookmark).getDomNode();`
Then, copy the bkmk node's parent, which is a paragraph tag. You now have a duplicate paragraph tag which has all the necessary child tags to be part of a bullet list. See, in OOXML there is no structure of a bullet list, it is simply a serial continuation of paragraphs that have similar numbering sub tags.
<w:p>
<w:pPr>
<w:pStyle w:val="style0" />
<w:numPr>
<w:ilvl w:val="1" />
<w:numId w:val="2" />
</w:numPr>
<w:tabs>
<w:tab w:leader="none" w:pos="1807" w:val="left" />
</w:tabs>
<w:spacing w:after="0" w:before="120" />
<w:ind w:end="907" w:hanging="360" w:start="907" />
<w:jc w:val="both" />
</w:pPr>
<w:bookmarkStart w:id="1" w:name="GIVES" />
<w:r>
<w:t>To be inserted Next Bullet</w:t>
</w:r>
<w:bookmarkEnd w:id="1" />
<w:r>
<w:rPr>
<w:rFonts w:eastAsia="Times New Roman" />
<w:color w:val="000000" />
<w:lang w:eastAsia="en-US" />
</w:rPr>
</w:r>
</w:p>
The key tags are and and I'm not super familiar with the XML meanings of the tags, but if you unzip any docx and look at the document.xml (after formatting it with tidy, of course) you can see the differences between paragraphs with and without numbering.
So, once you have a clone of your tag, you can traverse the node with DOM to get the and replace the node value with what you want, or you can use xpath to find the node (w:r/w:t). You have to setup a NamespaceContext and give it the proper code to understand the w: prefix
NodeList nl;
XPath xp = XPathFactory.newInstance().newXPath();
NamespaceContext nsContext = new NamespaceContext(){
@Override public String getNamespaceURI(String prefix) {
if (prefix.equals("w")) {
return "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
}
return null;
}
@Override public String getPrefix(String namespaceURI) {
return null;
}
@Override public Iterator<?> getPrefixes(String namespaceURI) {
return Collections.emptyList()
.iterator();
}
};
xp.setNamespaceContext(nsContext);
nl = (NodeList) xp.evaluate("w:r/w:t", copy, XPathConstants.NODESET);
Now, traverse the nodeList, setNodeValue("Hello World"). You can do this after cloning and doing:
paragraph.getParentNode().insertBefore(bkmk, paragraph);
to get as many bullet points as you want. If you do
`paragraph.getParentNode().append(bkmk)`
your new bullet point will be at the very end of the document!
So, you must do insertBefore (it's the only dom node manipulation available other than append). This leaves you with your original empty bullet point at the end, with the bookmark in it. You need to remove the bookmark with paragraph.getParentNode().removeChild(paragraph);
Then save your file with POI.
Basically, POI doesn't support bullet lists because OOXML doesn't really support bullet lists. Bullet lists are just a run of paragraphs with numbering tags as children. But, with POI you can always fall down to basic DOM manipulation and you can inspect the DOM with unzip and tidy.
POI's HWPF is an immature api - the lead developer was offered a job that entailed signing a non-disclosure agreement and he was forced to abandon working in it - and it may not be possible to use it to generate the files you wish.
See the continuation of the post