Arthur
Arthur

Reputation: 1246

Adding footnotes to a word document

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

Answers (2)

dourouc05
dourouc05

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

Arthur
Arthur

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

Related Questions