[github-114] Extend docx footnote support. Thanks to Eliot Kimber. This closes #114
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1836415 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
fc044c7840
commit
c1fc95c517
@ -41,6 +41,8 @@ import org.apache.poi.ooxml.POIXMLDocumentPart;
|
|||||||
import org.apache.poi.ooxml.POIXMLException;
|
import org.apache.poi.ooxml.POIXMLException;
|
||||||
import org.apache.poi.ooxml.POIXMLProperties;
|
import org.apache.poi.ooxml.POIXMLProperties;
|
||||||
import org.apache.poi.ooxml.POIXMLRelation;
|
import org.apache.poi.ooxml.POIXMLRelation;
|
||||||
|
import org.apache.poi.ooxml.util.IdentifierManager;
|
||||||
|
import org.apache.poi.ooxml.util.PackageHelper;
|
||||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
||||||
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
|
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
|
||||||
import org.apache.poi.openxml4j.opc.OPCPackage;
|
import org.apache.poi.openxml4j.opc.OPCPackage;
|
||||||
@ -84,6 +86,7 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.STDocProtect;
|
|||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr;
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr;
|
||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff;
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff;
|
||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.StylesDocument;
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.StylesDocument;
|
||||||
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>High(ish) level class for working with .docx files.</p>
|
* <p>High(ish) level class for working with .docx files.</p>
|
||||||
@ -1653,4 +1656,33 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody {
|
|||||||
charts.add(xwpfChart);
|
charts.add(xwpfChart);
|
||||||
return xwpfChart;
|
return xwpfChart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new footnote and add it to the document.
|
||||||
|
* <p>The new note will have one paragraph with the style "FootnoteText"
|
||||||
|
* and one run containing the required footnote reference with the
|
||||||
|
* style "FootnoteReference".
|
||||||
|
*
|
||||||
|
* @return New XWPFFootnote.
|
||||||
|
*/
|
||||||
|
public XWPFFootnote createFootnote() {
|
||||||
|
XWPFFootnotes footnotes = this.createFootnotes();
|
||||||
|
|
||||||
|
XWPFFootnote footnote = footnotes.createFootnote();
|
||||||
|
return footnote;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified footnote if present.
|
||||||
|
*
|
||||||
|
* @param pos
|
||||||
|
* @return True if the footnote was removed.
|
||||||
|
*/
|
||||||
|
public boolean removeFootnote(int pos) {
|
||||||
|
if (null != footnotes) {
|
||||||
|
return footnotes.removeFootnote(pos);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* ====================================================================
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -17,6 +16,7 @@
|
|||||||
==================================================================== */
|
==================================================================== */
|
||||||
package org.apache.poi.xwpf.usermodel;
|
package org.apache.poi.xwpf.usermodel;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -25,12 +25,28 @@ import org.apache.poi.ooxml.POIXMLDocumentPart;
|
|||||||
import org.apache.xmlbeans.XmlCursor;
|
import org.apache.xmlbeans.XmlCursor;
|
||||||
import org.apache.xmlbeans.XmlObject;
|
import org.apache.xmlbeans.XmlObject;
|
||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn;
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn;
|
||||||
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdnRef;
|
||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
|
||||||
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
|
||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
|
||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock;
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock;
|
||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
|
||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a bottom-of-the-page footnote.
|
||||||
|
* <p>Create a new footnote using {@link XWPFDocument#createFootnote()} or
|
||||||
|
* {@link XWPFFootnotes#createFootnote()}.</p>
|
||||||
|
* <p>The first body element of a footnote should (or possibly must) be a paragraph
|
||||||
|
* with the first run containing a CTFtnEdnRef object. The {@link XWPFFootnote#createParagraph()}
|
||||||
|
* and {@link XWPFFootnote#createTable()} methods do this for you.</p>
|
||||||
|
* <p>Footnotes have IDs that are unique across all footnotes in the document. You use
|
||||||
|
* the footnote ID to create a reference to a footnote from within a paragraph.</p>
|
||||||
|
* <p>To create a reference to a footnote within a paragraph you create a run
|
||||||
|
* with a CTFtnEdnRef that specifies the ID of the target paragraph.
|
||||||
|
* The {@link XWPFParagraph#addFootnoteReference(XWPFFootnote)}
|
||||||
|
* method does this for you.</p>
|
||||||
|
*/
|
||||||
public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
||||||
private List<XWPFParagraph> paragraphs = new ArrayList<>();
|
private List<XWPFParagraph> paragraphs = new ArrayList<>();
|
||||||
private List<XWPFTable> tables = new ArrayList<>();
|
private List<XWPFTable> tables = new ArrayList<>();
|
||||||
@ -78,37 +94,67 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
|||||||
cursor.dispose();
|
cursor.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of {@link XWPFParagraph}s in the footnote.
|
||||||
|
* @return List of paragraphs
|
||||||
|
*/
|
||||||
public List<XWPFParagraph> getParagraphs() {
|
public List<XWPFParagraph> getParagraphs() {
|
||||||
return paragraphs;
|
return paragraphs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an iterator over the {@link XWPFParagraph}s in the footnote.
|
||||||
|
* @return Iterator over the paragraph list.
|
||||||
|
*/
|
||||||
public Iterator<XWPFParagraph> iterator() {
|
public Iterator<XWPFParagraph> iterator() {
|
||||||
return paragraphs.iterator();
|
return paragraphs.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of {@link XWPFTable}s in the footnote.
|
||||||
|
* @return List of tables
|
||||||
|
*/
|
||||||
public List<XWPFTable> getTables() {
|
public List<XWPFTable> getTables() {
|
||||||
return tables;
|
return tables;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of {@link XWPFPictureData}s in the footnote.
|
||||||
|
* @return List of pictures
|
||||||
|
*/
|
||||||
public List<XWPFPictureData> getPictures() {
|
public List<XWPFPictureData> getPictures() {
|
||||||
return pictures;
|
return pictures;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the body elements ({@link IBodyElement}) of the footnote.
|
||||||
|
* @return List of body elements.
|
||||||
|
*/
|
||||||
public List<IBodyElement> getBodyElements() {
|
public List<IBodyElement> getBodyElements() {
|
||||||
return bodyElements;
|
return bodyElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the underlying CTFtnEdn object for the footnote.
|
||||||
|
* @return CTFtnEdn object
|
||||||
|
*/
|
||||||
public CTFtnEdn getCTFtnEdn() {
|
public CTFtnEdn getCTFtnEdn() {
|
||||||
return ctFtnEdn;
|
return ctFtnEdn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the underlying CTFtnEdn for the footnote.
|
||||||
|
* <p>Use {@link XWPFDocument#createFootnote()} to create new footnotes.</p>
|
||||||
|
* @param footnote The CTFtnEdn object that will underly the footnote.
|
||||||
|
*/
|
||||||
public void setCTFtnEdn(CTFtnEdn footnote) {
|
public void setCTFtnEdn(CTFtnEdn footnote) {
|
||||||
ctFtnEdn = footnote;
|
ctFtnEdn = footnote;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Gets the {@link XWPFTable} at the specified position from the footnote's table array.
|
||||||
* @param pos in table array
|
* @param pos in table array
|
||||||
* @return The table at position pos
|
* @return The {@link XWPFTable} at position pos, or null if there is no table at position pos.
|
||||||
* @see org.apache.poi.xwpf.usermodel.IBody#getTableArray(int)
|
* @see org.apache.poi.xwpf.usermodel.IBody#getTableArray(int)
|
||||||
*/
|
*/
|
||||||
public XWPFTable getTableArray(int pos) {
|
public XWPFTable getTableArray(int pos) {
|
||||||
@ -119,16 +165,16 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* inserts an existing XWPFTable to the arrays bodyElements and tables
|
* Inserts an existing {@link XWPFTable) into the arrays bodyElements and tables.
|
||||||
*
|
*
|
||||||
* @param pos
|
* @param pos Position, in the bodyElements array, to insert the table
|
||||||
* @param table
|
* @param table {@link XWPFTable) to be inserted
|
||||||
* @see org.apache.poi.xwpf.usermodel.IBody#insertTable(int pos, XWPFTable table)
|
* @see org.apache.poi.xwpf.usermodel.IBody#insertTable(int pos, XWPFTable table)
|
||||||
*/
|
*/
|
||||||
public void insertTable(int pos, XWPFTable table) {
|
public void insertTable(int pos, XWPFTable table) {
|
||||||
bodyElements.add(pos, table);
|
bodyElements.add(pos, table);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (CTTbl tbl : ctFtnEdn.getTblArray()) {
|
for (CTTbl tbl : ctFtnEdn.getTblList()) {
|
||||||
if (tbl == table.getCTTbl()) {
|
if (tbl == table.getCTTbl()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -139,9 +185,10 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if there is a corresponding {@link XWPFTable} of the parameter ctTable in the tableList of this header
|
* if there is a corresponding {@link XWPFTable} of the parameter
|
||||||
* the method will return this table
|
* ctTable in the tableList of this header
|
||||||
* if there is no corresponding {@link XWPFTable} the method will return null
|
* the method will return this table, or null if there is no
|
||||||
|
* corresponding {@link XWPFTable}.
|
||||||
*
|
*
|
||||||
* @param ctTable
|
* @param ctTable
|
||||||
* @see org.apache.poi.xwpf.usermodel.IBody#getTable(CTTbl ctTable)
|
* @see org.apache.poi.xwpf.usermodel.IBody#getTable(CTTbl ctTable)
|
||||||
@ -157,13 +204,12 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if there is a corresponding {@link XWPFParagraph} of the parameter ctTable in the paragraphList of this header or footer
|
* if there is a corresponding {@link XWPFParagraph} of the parameter p in the paragraphList of this header or footer
|
||||||
* the method will return this paragraph
|
* the method will return that paragraph, otherwise the method will return null.
|
||||||
* if there is no corresponding {@link XWPFParagraph} the method will return null
|
|
||||||
*
|
*
|
||||||
* @param p is instance of CTP and is searching for an XWPFParagraph
|
* @param p The CTP paragraph to find the corresponding {@link XWPFParagraph} for.
|
||||||
* @return null if there is no XWPFParagraph with an corresponding CTPparagraph in the paragraphList of this header or footer
|
* @return The {@link XWPFParagraph} that corresponds to the CTP paragraph in the paragraph
|
||||||
* XWPFParagraph with the correspondig CTP p
|
* list of this footnote or null if no paragraph is found.
|
||||||
* @see org.apache.poi.xwpf.usermodel.IBody#getParagraph(CTP p)
|
* @see org.apache.poi.xwpf.usermodel.IBody#getParagraph(CTP p)
|
||||||
*/
|
*/
|
||||||
public XWPFParagraph getParagraph(CTP p) {
|
public XWPFParagraph getParagraph(CTP p) {
|
||||||
@ -175,8 +221,9 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the paragraph that holds
|
* Returns the {@link XWPFParagraph} at position pos in footnote's paragraph array.
|
||||||
* the text of the header or footer.
|
* @param pos Array position of the paragraph to get.
|
||||||
|
* @return the {@link XWPFParagraph} at position pos, or null if there is no paragraph at that position.
|
||||||
*
|
*
|
||||||
* @see org.apache.poi.xwpf.usermodel.IBody#getParagraphArray(int pos)
|
* @see org.apache.poi.xwpf.usermodel.IBody#getParagraphArray(int pos)
|
||||||
*/
|
*/
|
||||||
@ -188,9 +235,10 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the TableCell which belongs to the TableCell
|
* get the {@link XWPFTableCell} that belongs to the CTTc cell.
|
||||||
*
|
*
|
||||||
* @param cell
|
* @param cell
|
||||||
|
* @return {@link XWPFTableCell} that corresponds to the CTTc cell, if there is one, otherwise null.
|
||||||
* @see org.apache.poi.xwpf.usermodel.IBody#getTableCell(CTTc cell)
|
* @see org.apache.poi.xwpf.usermodel.IBody#getTableCell(CTTc cell)
|
||||||
*/
|
*/
|
||||||
public XWPFTableCell getTableCell(CTTc cell) {
|
public XWPFTableCell getTableCell(CTTc cell) {
|
||||||
@ -220,9 +268,10 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* verifies that cursor is on the right position
|
* Verifies that cursor is on the right position.
|
||||||
*
|
*
|
||||||
* @param cursor
|
* @param cursor
|
||||||
|
* @return true if the cursor is within a CTFtnEdn element.
|
||||||
*/
|
*/
|
||||||
private boolean isCursorInFtn(XmlCursor cursor) {
|
private boolean isCursorInFtn(XmlCursor cursor) {
|
||||||
XmlCursor verify = cursor.newCursor();
|
XmlCursor verify = cursor.newCursor();
|
||||||
@ -233,13 +282,19 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The owning object for this footnote
|
||||||
|
*
|
||||||
|
* @return The {@link XWPFFootnotes} object that contains this footnote.
|
||||||
|
*/
|
||||||
public POIXMLDocumentPart getOwner() {
|
public POIXMLDocumentPart getOwner() {
|
||||||
return footnotes;
|
return footnotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Insert a table constructed from OOXML table markup.
|
||||||
* @param cursor
|
* @param cursor
|
||||||
* @return the inserted table
|
* @return the inserted {@link XWPFTable}
|
||||||
* @see org.apache.poi.xwpf.usermodel.IBody#insertNewTbl(XmlCursor cursor)
|
* @see org.apache.poi.xwpf.usermodel.IBody#insertNewTbl(XmlCursor cursor)
|
||||||
*/
|
*/
|
||||||
public XWPFTable insertNewTbl(XmlCursor cursor) {
|
public XWPFTable insertNewTbl(XmlCursor cursor) {
|
||||||
@ -279,10 +334,10 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add a new paragraph at position of the cursor
|
* Add a new {@link XWPFParagraph} at position of the cursor.
|
||||||
*
|
*
|
||||||
* @param cursor
|
* @param cursor
|
||||||
* @return the inserted paragraph
|
* @return The inserted {@link XWPFParagraph}
|
||||||
* @see org.apache.poi.xwpf.usermodel.IBody#insertNewParagraph(XmlCursor cursor)
|
* @see org.apache.poi.xwpf.usermodel.IBody#insertNewParagraph(XmlCursor cursor)
|
||||||
*/
|
*/
|
||||||
public XWPFParagraph insertNewParagraph(final XmlCursor cursor) {
|
public XWPFParagraph insertNewParagraph(final XmlCursor cursor) {
|
||||||
@ -323,10 +378,10 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add a new table to the end of the footnote
|
* Add a new {@link XWPFTable} to the end of the footnote.
|
||||||
*
|
*
|
||||||
* @param table
|
* @param table CTTbl object from which to construct the {@link XWPFTable}
|
||||||
* @return the added XWPFTable
|
* @return The added {@link XWPFTable}
|
||||||
*/
|
*/
|
||||||
public XWPFTable addNewTbl(CTTbl table) {
|
public XWPFTable addNewTbl(CTTbl table) {
|
||||||
CTTbl newTable = ctFtnEdn.addNewTbl();
|
CTTbl newTable = ctFtnEdn.addNewTbl();
|
||||||
@ -337,10 +392,10 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add a new paragraph to the end of the footnote
|
* Add a new {@link XWPFParagraph} to the end of the footnote.
|
||||||
*
|
*
|
||||||
* @param paragraph
|
* @param paragraph CTP paragraph from which to construct the {@link XWPFParagraph}
|
||||||
* @return the added XWPFParagraph
|
* @return The added {@link XWPFParagraph}
|
||||||
*/
|
*/
|
||||||
public XWPFParagraph addNewParagraph(CTP paragraph) {
|
public XWPFParagraph addNewParagraph(CTP paragraph) {
|
||||||
CTP newPara = ctFtnEdn.addNewP();
|
CTP newPara = ctFtnEdn.addNewP();
|
||||||
@ -351,6 +406,7 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Get the {@link XWPFDocument} the footnote is part of.
|
||||||
* @see org.apache.poi.xwpf.usermodel.IBody#getXWPFDocument()
|
* @see org.apache.poi.xwpf.usermodel.IBody#getXWPFDocument()
|
||||||
*/
|
*/
|
||||||
public XWPFDocument getXWPFDocument() {
|
public XWPFDocument getXWPFDocument() {
|
||||||
@ -358,7 +414,8 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the Part, to which the body belongs, which you need for adding relationship to other parts
|
* Get the Part to which the footnote belongs, which you need for adding relationships to other parts
|
||||||
|
* @return {@link POIXMLDocumentPart} that contains the footnote.
|
||||||
*
|
*
|
||||||
* @see org.apache.poi.xwpf.usermodel.IBody#getPart()
|
* @see org.apache.poi.xwpf.usermodel.IBody#getPart()
|
||||||
*/
|
*/
|
||||||
@ -367,11 +424,106 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the PartType of the body
|
* Get the part type {@link BodyType} of the footnote.
|
||||||
|
* @return The {@link BodyType} value.
|
||||||
*
|
*
|
||||||
* @see org.apache.poi.xwpf.usermodel.IBody#getPartType()
|
* @see org.apache.poi.xwpf.usermodel.IBody#getPartType()
|
||||||
*/
|
*/
|
||||||
public BodyType getPartType() {
|
public BodyType getPartType() {
|
||||||
return BodyType.FOOTNOTE;
|
return BodyType.FOOTNOTE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ID of the footnote.
|
||||||
|
* <p>Footnote IDs are unique across all bottom-of-the-page and
|
||||||
|
* end note footnotes.</p>
|
||||||
|
*
|
||||||
|
* @return Footnote ID
|
||||||
|
*/
|
||||||
|
public BigInteger getId() {
|
||||||
|
return this.ctFtnEdn.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a new {@link XWPFParagraph} to this footnote.
|
||||||
|
*
|
||||||
|
* @return The new {@link XWPFParagraph}
|
||||||
|
*/
|
||||||
|
public XWPFParagraph createParagraph() {
|
||||||
|
XWPFParagraph p = new XWPFParagraph(this.ctFtnEdn.addNewP(), this);
|
||||||
|
paragraphs.add(p);
|
||||||
|
bodyElements.add(p);
|
||||||
|
|
||||||
|
// If the paragraph is the first paragraph in the footnote,
|
||||||
|
// ensure that it has a footnote reference run.
|
||||||
|
|
||||||
|
if (p.equals(getParagraphs().get(0))) {
|
||||||
|
ensureFootnoteRef(p);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that the specified paragraph has a reference marker for this
|
||||||
|
* footnote by adding a footnote reference if one is not found.
|
||||||
|
* <p>This method is for the first paragraph in the footnote, not
|
||||||
|
* paragraphs that will refer to the footnote. For references to
|
||||||
|
* the footnote, use {@link XWPFParagraph#addFootnoteReference(XWPFFootnote)}.
|
||||||
|
* </p>
|
||||||
|
* <p>The first run of the first paragraph in a footnote should
|
||||||
|
* contain a {@link CTFtnEdnRef} object.</p>
|
||||||
|
*
|
||||||
|
* @param p The {@link XWPFParagraph} to ensure
|
||||||
|
*/
|
||||||
|
public void ensureFootnoteRef(XWPFParagraph p) {
|
||||||
|
|
||||||
|
XWPFRun r = null;
|
||||||
|
if (p.getRuns().size() > 0) {
|
||||||
|
r = p.getRuns().get(0);
|
||||||
|
}
|
||||||
|
if (r == null) {
|
||||||
|
r = p.createRun();
|
||||||
|
}
|
||||||
|
CTR ctr = r.getCTR();
|
||||||
|
boolean foundRef = false;
|
||||||
|
for (CTFtnEdnRef ref : ctr.getFootnoteReferenceList()) {
|
||||||
|
if (getId().equals(ref.getId())) {
|
||||||
|
foundRef = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundRef) {
|
||||||
|
ctr.addNewRPr().addNewRStyle().setVal("FootnoteReference");
|
||||||
|
ctr.addNewFootnoteRef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a new {@link XWPFTable} to this footnote
|
||||||
|
*
|
||||||
|
* @return The new {@link XWPFTable}
|
||||||
|
*/
|
||||||
|
public XWPFTable createTable() {
|
||||||
|
XWPFTable table = new XWPFTable(ctFtnEdn.addNewTbl(), this);
|
||||||
|
if (bodyElements.size() == 0) {
|
||||||
|
XWPFParagraph p = createParagraph();
|
||||||
|
ensureFootnoteRef(p);
|
||||||
|
}
|
||||||
|
bodyElements.add(table);
|
||||||
|
tables.add(table);
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a new {@link XWPFTable} to this footnote
|
||||||
|
* @param rows Number of rows to initialize the table with
|
||||||
|
* @param cols Number of columns to initialize the table with
|
||||||
|
* @return the new {@link XWPFTable} with the specified number of rows and columns
|
||||||
|
*/
|
||||||
|
public XWPFTable createTable(int rows, int cols) {
|
||||||
|
XWPFTable table = new XWPFTable(ctFtnEdn.addNewTbl(), this, rows, cols);
|
||||||
|
bodyElements.add(table);
|
||||||
|
tables.add(table);
|
||||||
|
return table;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -35,10 +36,14 @@ import org.apache.xmlbeans.XmlException;
|
|||||||
import org.apache.xmlbeans.XmlOptions;
|
import org.apache.xmlbeans.XmlOptions;
|
||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFootnotes;
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFootnotes;
|
||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn;
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn;
|
||||||
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
|
||||||
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
|
||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.FootnotesDocument;
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.FootnotesDocument;
|
||||||
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STFtnEdn;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks after the collection of Footnotes for a document
|
* Looks after the collection of Footnotes for a document.
|
||||||
|
* Manages both bottom-of-the-page footnotes and end notes.
|
||||||
*/
|
*/
|
||||||
public class XWPFFootnotes extends POIXMLDocumentPart {
|
public class XWPFFootnotes extends POIXMLDocumentPart {
|
||||||
protected XWPFDocument document;
|
protected XWPFDocument document;
|
||||||
@ -157,4 +162,39 @@ public class XWPFFootnotes extends POIXMLDocumentPart {
|
|||||||
public void setXWPFDocument(XWPFDocument doc) {
|
public void setXWPFDocument(XWPFDocument doc) {
|
||||||
document = doc;
|
document = doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new footnote and add it to the document.
|
||||||
|
* <p>The new note will have one paragraph with the style "FootnoteText"
|
||||||
|
* and one run containing the required footnote reference with the
|
||||||
|
* style "FootnoteReference".
|
||||||
|
* </p>
|
||||||
|
* @return New XWPFFootnote
|
||||||
|
*/
|
||||||
|
public XWPFFootnote createFootnote() {
|
||||||
|
CTFtnEdn newNote = CTFtnEdn.Factory.newInstance();
|
||||||
|
newNote.setType(STFtnEdn.NORMAL);
|
||||||
|
|
||||||
|
XWPFFootnote footnote = addFootnote(newNote);
|
||||||
|
int id = ctFootnotes.sizeOfFootnoteArray();
|
||||||
|
footnote.getCTFtnEdn().setId(BigInteger.valueOf(id));
|
||||||
|
return footnote;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified footnote if present.
|
||||||
|
*
|
||||||
|
* @param pos
|
||||||
|
* @return True if the footnote was removed.
|
||||||
|
*/
|
||||||
|
public boolean removeFootnote(int pos) {
|
||||||
|
if (ctFootnotes.sizeOfFootnoteArray() >= pos - 1) {
|
||||||
|
ctFootnotes.removeFootnote(pos);
|
||||||
|
listFootnote.remove(pos);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.poi.ooxml.POIXMLDocumentPart;
|
import org.apache.poi.ooxml.POIXMLDocumentPart;
|
||||||
|
import org.apache.poi.ss.formula.eval.NotImplementedException;
|
||||||
import org.apache.poi.util.Internal;
|
import org.apache.poi.util.Internal;
|
||||||
import org.apache.poi.wp.usermodel.Paragraph;
|
import org.apache.poi.wp.usermodel.Paragraph;
|
||||||
import org.apache.xmlbeans.XmlCursor;
|
import org.apache.xmlbeans.XmlCursor;
|
||||||
@ -1667,4 +1668,17 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new run with a reference to the specified footnote.
|
||||||
|
* The footnote reference run will have the style name "FootnoteReference".
|
||||||
|
*
|
||||||
|
* @param footnote Footnote to which to add a reference.
|
||||||
|
*/
|
||||||
|
public void addFootnoteReference(XWPFFootnote footnote) {
|
||||||
|
XWPFRun run = createRun();
|
||||||
|
CTR ctRun = run.getCTR();
|
||||||
|
ctRun.addNewRPr().addNewRStyle().setVal("FootnoteReference");
|
||||||
|
ctRun.addNewFootnoteReference().setId(footnote.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,158 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
package org.apache.poi.xwpf.usermodel;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.poi.xwpf.XWPFTestDataSamples;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdnRef;
|
||||||
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
|
||||||
|
|
||||||
|
public class TestXWPFFootnote {
|
||||||
|
|
||||||
|
private XWPFDocument docOut;
|
||||||
|
private String p1Text;
|
||||||
|
private String p2Text;
|
||||||
|
private BigInteger footnoteId;
|
||||||
|
private XWPFFootnote footnote;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
docOut = new XWPFDocument();
|
||||||
|
p1Text = "First paragraph in footnote";
|
||||||
|
p2Text = "Second paragraph in footnote";
|
||||||
|
|
||||||
|
// NOTE: XWPFDocument.createFootnote() delegates directly
|
||||||
|
// to XWPFFootnotes.createFootnote() so this tests
|
||||||
|
// both creation of new XWPFFootnotes in document
|
||||||
|
// and XWPFFootnotes.createFootnote();
|
||||||
|
|
||||||
|
// NOTE: Creating the footnote does not automatically
|
||||||
|
// create a first paragraph.
|
||||||
|
footnote = docOut.createFootnote();
|
||||||
|
footnoteId = footnote.getId();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddParagraphsToFootnote() throws IOException {
|
||||||
|
|
||||||
|
// Add a run to the first paragraph:
|
||||||
|
|
||||||
|
XWPFParagraph p1 = footnote.createParagraph();
|
||||||
|
p1.createRun().setText(p1Text);
|
||||||
|
|
||||||
|
// Create a second paragraph:
|
||||||
|
|
||||||
|
XWPFParagraph p = footnote.createParagraph();
|
||||||
|
assertNotNull("Paragraph is null", p);
|
||||||
|
p.createRun().setText(p2Text);
|
||||||
|
|
||||||
|
XWPFDocument docIn = XWPFTestDataSamples.writeOutAndReadBack(docOut);
|
||||||
|
|
||||||
|
XWPFFootnote testFootnote = docIn.getFootnoteByID(footnoteId.intValue());
|
||||||
|
assertNotNull(testFootnote);
|
||||||
|
|
||||||
|
assertEquals(2, testFootnote.getParagraphs().size());
|
||||||
|
XWPFParagraph testP1 = testFootnote.getParagraphs().get(0);
|
||||||
|
assertEquals(p1Text, testP1.getText());
|
||||||
|
|
||||||
|
XWPFParagraph testP2 = testFootnote.getParagraphs().get(1);
|
||||||
|
assertEquals(p2Text, testP2.getText());
|
||||||
|
|
||||||
|
// The first paragraph added using createParagraph() should
|
||||||
|
// have the required footnote reference added to the first
|
||||||
|
// run.
|
||||||
|
|
||||||
|
// Verify that we have a footnote reference in the first paragraph and not
|
||||||
|
// in the second paragraph.
|
||||||
|
|
||||||
|
XWPFRun r1 = testP1.getRuns().get(0);
|
||||||
|
assertNotNull(r1);
|
||||||
|
assertTrue("No footnote reference in testP1", r1.getCTR().getFootnoteRefList().size() > 0);
|
||||||
|
assertNotNull("No footnote reference in testP1", r1.getCTR().getFootnoteRefArray(0));
|
||||||
|
|
||||||
|
XWPFRun r2 = testP2.getRuns().get(0);
|
||||||
|
assertNotNull("Expected a run in testP2", r2);
|
||||||
|
assertTrue("Found a footnote reference in testP2", r2.getCTR().getFootnoteRefList().size() == 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddTableToFootnote() throws IOException {
|
||||||
|
XWPFTable table = footnote.createTable();
|
||||||
|
assertNotNull(table);
|
||||||
|
|
||||||
|
XWPFDocument docIn = XWPFTestDataSamples.writeOutAndReadBack(docOut);
|
||||||
|
|
||||||
|
XWPFFootnote testFootnote = docIn.getFootnoteByID(footnoteId.intValue());
|
||||||
|
XWPFTable testTable = testFootnote.getTableArray(0);
|
||||||
|
assertNotNull(testTable);
|
||||||
|
|
||||||
|
table = footnote.createTable(2, 3);
|
||||||
|
assertEquals(2, table.getNumberOfRows());
|
||||||
|
assertEquals(3, table.getRow(0).getTableCells().size());
|
||||||
|
|
||||||
|
// If the table is the first body element of the footnote then
|
||||||
|
// a paragraph with the footnote reference should have been
|
||||||
|
// added automatically.
|
||||||
|
|
||||||
|
assertEquals("Expected 3 body elements", 3, footnote.getBodyElements().size());
|
||||||
|
IBodyElement testP1 = footnote.getBodyElements().get(0);
|
||||||
|
assertTrue("Expected a paragraph, got " + testP1.getClass().getSimpleName() , testP1 instanceof XWPFParagraph);
|
||||||
|
XWPFRun r1 = ((XWPFParagraph)testP1).getRuns().get(0);
|
||||||
|
assertNotNull(r1);
|
||||||
|
assertTrue("No footnote reference in testP1", r1.getCTR().getFootnoteRefList().size() > 0);
|
||||||
|
assertNotNull("No footnote reference in testP1", r1.getCTR().getFootnoteRefArray(0));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveFootnote() {
|
||||||
|
// NOTE: XWPFDocument.removeFootnote() delegates directly to
|
||||||
|
// XWPFFootnotes.
|
||||||
|
docOut.createFootnote();
|
||||||
|
assertEquals("Expected 2 footnotes", 2, docOut.getFootnotes().size());
|
||||||
|
assertNotNull("Didn't get second footnote", docOut.getFootnotes().get(1));
|
||||||
|
boolean result = docOut.removeFootnote(0);
|
||||||
|
assertTrue("Remove footnote did not return true", result);
|
||||||
|
assertEquals("Expected 1 footnote after removal", 1, docOut.getFootnotes().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddFootnoteRefToParagraph() {
|
||||||
|
XWPFParagraph p = docOut.createParagraph();
|
||||||
|
List<XWPFRun> runs = p.getRuns();
|
||||||
|
assertEquals("Expected no runs in new paragraph", 0, runs.size());
|
||||||
|
p.addFootnoteReference(footnote);
|
||||||
|
XWPFRun run = p.getRuns().get(0);
|
||||||
|
CTR ctr = run.getCTR();
|
||||||
|
assertNotNull("Expected a run", run);
|
||||||
|
CTFtnEdnRef ref = ctr.getFootnoteReferenceList().get(0);
|
||||||
|
assertNotNull(ref);
|
||||||
|
assertEquals("Footnote ID and reference ID did not match", footnote.getId(), ref.getId());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -21,28 +21,42 @@ import java.io.IOException;
|
|||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
import org.apache.poi.xwpf.XWPFTestDataSamples;
|
import org.apache.poi.xwpf.XWPFTestDataSamples;
|
||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn;
|
|
||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STFtnEdn;
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STFtnEdn;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
public class TestXWPFFootnotes extends TestCase {
|
public class TestXWPFFootnotes extends TestCase {
|
||||||
|
|
||||||
|
public void testCreateFootnotes() throws IOException{
|
||||||
|
XWPFDocument docOut = new XWPFDocument();
|
||||||
|
|
||||||
|
XWPFFootnotes footnotes = docOut.createFootnotes();
|
||||||
|
|
||||||
|
assertNotNull(footnotes);
|
||||||
|
|
||||||
|
XWPFFootnotes secondFootnotes = docOut.createFootnotes();
|
||||||
|
|
||||||
|
assertSame(footnotes, secondFootnotes);
|
||||||
|
|
||||||
|
docOut.close();
|
||||||
|
}
|
||||||
|
|
||||||
public void testAddFootnotesToDocument() throws IOException {
|
public void testAddFootnotesToDocument() throws IOException {
|
||||||
XWPFDocument docOut = new XWPFDocument();
|
XWPFDocument docOut = new XWPFDocument();
|
||||||
|
|
||||||
BigInteger noteId = BigInteger.valueOf(1);
|
// NOTE: XWPFDocument.createFootnote() delegates directly
|
||||||
|
// to XWPFFootnotes.createFootnote() so this tests
|
||||||
XWPFFootnotes footnotes = docOut.createFootnotes();
|
// both creation of new XWPFFootnotes in document
|
||||||
CTFtnEdn ctNote = CTFtnEdn.Factory.newInstance();
|
// and XWPFFootnotes.createFootnote();
|
||||||
ctNote.setId(noteId);
|
XWPFFootnote footnote = docOut.createFootnote();
|
||||||
ctNote.setType(STFtnEdn.NORMAL);
|
BigInteger noteId = footnote.getId();
|
||||||
footnotes.addFootnote(ctNote);
|
|
||||||
|
|
||||||
XWPFDocument docIn = XWPFTestDataSamples.writeOutAndReadBack(docOut);
|
XWPFDocument docIn = XWPFTestDataSamples.writeOutAndReadBack(docOut);
|
||||||
|
|
||||||
XWPFFootnote note = docIn.getFootnoteByID(noteId.intValue());
|
XWPFFootnote note = docIn.getFootnoteByID(noteId.intValue());
|
||||||
assertEquals(note.getCTFtnEdn().getType(), STFtnEdn.NORMAL);
|
assertNotNull(note);
|
||||||
|
assertEquals(STFtnEdn.NORMAL, note.getCTFtnEdn().getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user