[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:
PJ Fanning 2018-07-21 21:30:52 +00:00
parent fc044c7840
commit c1fc95c517
6 changed files with 451 additions and 41 deletions

View File

@ -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;
}
}
} }

View File

@ -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;
}
} }

View File

@ -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;
}
}
} }

View File

@ -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());
}
} }

View File

@ -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());
}
}

View File

@ -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());
} }
/** /**