Reputation: 1246
How can I add footnotes to a word document(.docx
) using Apache POI? I know how to retrieve footnotes already in the document, but I can't actually add more.
Upvotes: 0
Views: 1965
Reputation: 128
So that other won't have to do the same research as I did :)! Here is an updated version of the code for POI 4.1.0, using the class XWPFFootnote (which does not yet do all the job, unfortunately).
First, initialise the document to receive footnotes. doc
is your XWPFDocument (I always start with a template document, with premade styles, including the required footnote styles: FootnoteText, FootnoteReference). This basically creates two "dummy" footnotes, which I think are only used for display purposes -- but Word will consider the document as corrupt if they are not there.
doc.createFootnotes();
// Create the first two "dummy" footnotes.
{ // <w:footnote w:type="separator" w:id="-1"><w:p><w:r><w:separator/></w:r></w:p></w:footnote>
XWPFFootnote footnote = doc.createFootnote();
footnote.getCTFtnEdn().setId(BigInteger.ZERO.subtract(BigInteger.ONE)); // -1
footnote.getCTFtnEdn().setType(STFtnEdn.SEPARATOR);
footnote.getCTFtnEdn().addNewP();
footnote.getCTFtnEdn().getPArray(0).addNewR();
footnote.getCTFtnEdn().getPArray(0).getRArray(0).addNewSeparator();
}
{ // <w:footnote w:type="continuationSeparator" w:id="0"><w:p><w:r><w:continuationSeparator/></w:r></w:p></w:footnote>
XWPFFootnote footnote = doc.createFootnote();
footnote.getCTFtnEdn().setId(BigInteger.ZERO);
footnote.getCTFtnEdn().setType(STFtnEdn.CONTINUATION_SEPARATOR);
footnote.getCTFtnEdn().addNewP();
footnote.getCTFtnEdn().getPArray(0).addNewR();
footnote.getCTFtnEdn().getPArray(0).getRArray(0).addNewContinuationSeparator();
}
Then, you can proceed with adding your footnote, at some point in the text. Create paragraphs and runs as usual until the point where you want the reference to the footnote to appear (the number in exponent, usually).
footnote = doc.createFootnote();
paragraph.addFootnoteReference(footnote); // Creates a new run in the current paragraph to hold the reference.
XWPFParagraph footnoteParagraph;
{ // Create a low-level paragraph and add it to the footnote with the right style.
CTP ctp = footnote.getCTFtnEdn().addNewP();
footnoteParagraph = new XWPFParagraph(ctp, footnote);
footnoteParagraph.setStyle("FootnoteText");
}
{ // Create a run within this paragraph
XWPFRun run = paragraph.getLast().createRun();
run.setStyle("FootnoteReference");
run.getCTR().addNewFootnoteRef(); // Not addNewFootnoteReference, this is not recognised by Word!
}
{ // Add a space after the footnote number (like Word).
XWPFRun run = footnoteParagraph.createRun();
run.setText(" ");
}
{ // Add your own text in the footnote, as in any paragraph.
XWPFRun run = footnoteParagraph.createRun();
}
Upvotes: 1
Reputation: 1246
I'm not sure if this is the best way of going about adding footnotes to a word document, but it does work. After reviewing the Open XML of a document where the footnotes were added using word, I found that not only was a footnote needed, but also a reference inside the paragraph, and optionally but highly recommended three styles.
Adding a footnote to the document requires a CTFtnEdn
instance, which can be achieved by using CTFtnEdn.Factory.newInstance()
. Now to add the contents of the footnote I referenced the contents found inside a Footnote made using word:
<xml-fragment w:id="1" xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex" xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" 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:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape">
<w:p w:rsidR="00B87AAC" w:rsidRDefault="00B87AAC">
<w:pPr>
<w:pStyle w:val="FootnoteText"/>
</w:pPr>
<w:r>
<w:rPr>
<w:rStyle w:val="FootnoteReference"/>
</w:rPr>
<w:footnoteRef/>
</w:r>
<w:r>
<w:t xml:space="preserve">Here is a Footnote</w:t>
</w:r>
<w:bookmarkStart w:id="0" w:name="_GoBack"/>
<w:bookmarkEnd w:id="0"/>
</w:p>
</xml-fragment>
And here is an example of making that using java:
CTFtnEdn ctfInstance = CTFtnEdn.Factory.newInstance();
BigInteger id = new BigInteger("1");
ctfInstance.setId(id);
CTP ctp = ctfInstance.addNewP();
ctp.addNewPPr().addNewPStyle().setVal("FootnoteText");
CTR ctr = ctp.addNewR();
ctr.addNewRPr().addNewRStyle().setVal("FootnoteReference");
ctr.addNewFootnoteRef();
CTText cttext = ctp.addNewR().addNewT();
cttext.setStringValue("This is the text in the footnote");
cttext.setSpace(SpaceAttribute.Space.PRESERVE);
Once the tags have been added to the CTFtnEdn
instance it needs to be added to the WMPFDocument
using document.addFootnote()
.
Now to add a reference to the document's paragraphs - I don't believe that a footnote will even be visible at the bottom of the page without a reference. The Open XML for a reference looks like the following:
<w:r w:rsidR="00B87AAC">
<w:rPr>
<w:rStyle w:val="FootnoteReference"/>
</w:rPr>
<w:footnoteReference w:id="1"/>
</w:r>
This can easily be done in java using the code below:
ctr = document.getParagraphArray(0).getCTP().addNewR();
ctr.addNewRPr().addNewRStyle().setVal("FootnoteReference");
ctr.addNewFootnoteReference().setId(id);
In order to give the footnote it's own style with a superscript three styles need to be added. Below is the Open XML for all three of them:
<w:style w:styleId="FootnoteReference" w:type="character">
<w:name w:val="footnote reference"/>
<w:basedOn w:val="DefaultParagraphFont"/>
<w:uiPriority w:val="99"/>
<w:semiHidden/>
<w:unhideWhenUsed/>
<w:rPr>
<w:vertAlign w:val="superscript"/>
</w:rPr>
</w:style>
<w:style w:styleId="FootnoteText" w:type="paragraph">
<w:name w:val="footnote text"/>
<w:basedOn w:val="Normal"/>
<w:link w:val="FootnoteTextChar"/>
<w:uiPriority w:val="99"/>
<w:semiHidden/>
<w:unhideWhenUsed/>
<w:rPr>
<w:sz w:val="20"/>
<w:szCs w:val="20"/>
</w:rPr>
</w:style>
<w:style w:customStyle="1" w:styleId="FootnoteTextChar" w:type="character">
<w:name w:val="Footnote Text Char"/>
<w:basedOn w:val="DefaultParagraphFont"/>
<w:link w:val="FootnoteText"/>
<w:uiPriority w:val="99"/>
<w:semiHidden/>
<w:rPr>
<w:sz w:val="20"/>
<w:szCs w:val="20"/>
</w:rPr>
</w:style>
And adding this in java:
CTStyle style = CTStyle.Factory.newInstance();
style.setStyleId("FootnoteReference");
style.setType(STStyleType.CHARACTER);
style.addNewName().setVal("footnote reference");
style.addNewBasedOn().setVal("DefaultParagraphFont");
style.addNewUiPriority().setVal(new BigInteger("99"));
style.addNewSemiHidden();
style.addNewUnhideWhenUsed();
style.addNewRPr().addNewVertAlign().setVal(STVerticalAlignRun.SUPERSCRIPT);
document.getStyles().addStyle(new XWPFStyle(style));
style = CTStyle.Factory.newInstance();
style.setType(STStyleType.PARAGRAPH);
style.setStyleId("FootnoteText");
style.addNewName().setVal("footnote text");
style.addNewBasedOn().setVal("Normal");
style.addNewLink().setVal("FootnoteTextChar");
style.addNewUiPriority().setVal(new BigInteger("99"));
style.addNewSemiHidden();
style.addNewUnhideWhenUsed();
CTRPr rpr = style.addNewRPr();
rpr.addNewSz().setVal(new BigInteger("20"));
rpr.addNewSzCs().setVal(new BigInteger("20"));
document.getStyles().addStyle(new XWPFStyle(style));
style = CTStyle.Factory.newInstance();
style.setCustomStyle(STOnOff.X_1);
style.setStyleId("FootnoteTextChar");
style.setType(STStyleType.CHARACTER);
style.addNewName().setVal("Footnote Text Char");
style.addNewBasedOn().setVal("DefaultParagraphFont");
style.addNewLink().setVal("FootnoteText");
style.addNewUiPriority().setVal(new BigInteger("99"));
style.addNewSemiHidden();
rpr = style.addNewRPr();
rpr.addNewSz().setVal(new BigInteger("20"));
rpr.addNewSzCs().setVal(new BigInteger("20"));
document.getStyles().addStyle(new XWPFStyle(style));
Notice that document.getStyles().addStyle(new XWPFStyle(style))
was used to add new styles to the document.
Here is a SSCCE (make sure to change the output path at the end):
public static void main(String[] args) throws FileNotFoundException, IOException, XmlException{
// create doc
XWPFDocument document = new XWPFDocument();
// some sample text
document.createParagraph().createRun().setText("This is a Test.");
// check to add footnotes in case of empty
if (document.getFootnotes().isEmpty()){
document.createFootnotes();
}
// add footnote
CTFtnEdn ctfInstance = CTFtnEdn.Factory.newInstance();
BigInteger id = new BigInteger("1");
ctfInstance.setId(id);
CTP ctp = ctfInstance.addNewP();
ctp.addNewPPr().addNewPStyle().setVal("FootnoteText");
CTR ctr = ctp.addNewR();
ctr.addNewRPr().addNewRStyle().setVal("FootnoteReference");
ctr.addNewFootnoteRef();
CTText cttext = ctp.addNewR().addNewT();
cttext.setStringValue("This should be a footnote");
cttext.setSpace(SpaceAttribute.Space.PRESERVE);
// add footnote to document
document.addFootnote(ctfInstance);
// add reference to footnote at end of first paragraph
ctr = document.getParagraphArray(0).getCTP().addNewR();
ctr.addNewRPr().addNewRStyle().setVal("FootnoteReference");
ctr.addNewFootnoteReference().setId(id);
// styles
// if styles dont already exist then create them
if (document.getStyles()==null){
document.createStyles();
}
CTStyle style = CTStyle.Factory.newInstance();
style.setStyleId("FootnoteReference");
style.setType(STStyleType.CHARACTER);
style.addNewName().setVal("footnote reference");
style.addNewBasedOn().setVal("DefaultParagraphFont");
style.addNewUiPriority().setVal(new BigInteger("99"));
style.addNewSemiHidden();
style.addNewUnhideWhenUsed();
style.addNewRPr().addNewVertAlign().setVal(STVerticalAlignRun.SUPERSCRIPT);
// add style
document.getStyles().addStyle(new XWPFStyle(style));
style = CTStyle.Factory.newInstance();
style.setType(STStyleType.PARAGRAPH);
style.setStyleId("FootnoteText");
style.addNewName().setVal("footnote text");
style.addNewBasedOn().setVal("Normal");
style.addNewLink().setVal("FootnoteTextChar");
style.addNewUiPriority().setVal(new BigInteger("99"));
style.addNewSemiHidden();
style.addNewUnhideWhenUsed();
CTRPr rpr = style.addNewRPr();
rpr.addNewSz().setVal(new BigInteger("20"));
rpr.addNewSzCs().setVal(new BigInteger("20"));
// add style
document.getStyles().addStyle(new XWPFStyle(style));
style = CTStyle.Factory.newInstance();
style.setCustomStyle(STOnOff.X_1);
style.setStyleId("FootnoteTextChar");
style.setType(STStyleType.CHARACTER);
style.addNewName().setVal("Footnote Text Char");
style.addNewBasedOn().setVal("DefaultParagraphFont");
style.addNewLink().setVal("FootnoteText");
style.addNewUiPriority().setVal(new BigInteger("99"));
style.addNewSemiHidden();
rpr = style.addNewRPr();
rpr.addNewSz().setVal(new BigInteger("20"));
rpr.addNewSzCs().setVal(new BigInteger("20"));
// add style
document.getStyles().addStyle(new XWPFStyle(style));
// save document
FileOutputStream out = new FileOutputStream(new File("yourPathHere.docx"));
document.write(out);
out.close();
}
Upvotes: 4