mirror of https://github.com/apache/poi.git
allow add and remove a HyperlinkRun or a FieldRun
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1875862 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
3cb1a38d8e
commit
ce544b8550
|
@ -27,6 +27,7 @@ import org.apache.poi.xwpf.usermodel.TextAlignment;
|
|||
import org.apache.poi.xwpf.usermodel.UnderlinePatterns;
|
||||
import org.apache.poi.xwpf.usermodel.VerticalAlign;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFDocument;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFHyperlinkRun;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFRun;
|
||||
|
||||
|
@ -78,6 +79,11 @@ public class SimpleDocument {
|
|||
r3.setFontSize(20);
|
||||
r3.setSubscript(VerticalAlign.SUPERSCRIPT);
|
||||
|
||||
// hyperlink
|
||||
XWPFHyperlinkRun hyperlink = p2.insertNewHyperlinkRun(0, "http://poi.apache.org/");
|
||||
hyperlink.setUnderline(UnderlinePatterns.SINGLE);
|
||||
hyperlink.setColor("0000ff");
|
||||
hyperlink.setText("Apache POI");
|
||||
|
||||
XWPFParagraph p3 = doc.createParagraph();
|
||||
p3.setWordWrapped(true);
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.math.BigInteger;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.poi.ooxml.POIXMLDocumentPart;
|
||||
import org.apache.poi.util.Internal;
|
||||
|
@ -1478,50 +1479,144 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
|
|||
}
|
||||
|
||||
/**
|
||||
* insert a new Run in RunArray
|
||||
* Appends a new field run to this paragraph
|
||||
*
|
||||
* @return a new field run
|
||||
*/
|
||||
public XWPFFieldRun createFieldRun() {
|
||||
CTSimpleField ctSimpleField = paragraph.addNewFldSimple();
|
||||
XWPFFieldRun newRun = new XWPFFieldRun(ctSimpleField, ctSimpleField.addNewR(), this);
|
||||
runs.add(newRun);
|
||||
iruns.add(newRun);
|
||||
return newRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert a new Run in all runs
|
||||
*
|
||||
* @param pos The position at which the new run should be added.
|
||||
*
|
||||
* @return the inserted run or null if the given pos is out of bounds.
|
||||
*/
|
||||
public XWPFRun insertNewRun(int pos) {
|
||||
if (pos >= 0 && pos <= runs.size()) {
|
||||
// calculate the correct pos as our run/irun list contains
|
||||
// hyperlinks
|
||||
// and fields so it is different to the paragraph R array.
|
||||
int rPos = 0;
|
||||
for (int i = 0; i < pos; i++) {
|
||||
XWPFRun currRun = runs.get(i);
|
||||
if (!(currRun instanceof XWPFHyperlinkRun
|
||||
|| currRun instanceof XWPFFieldRun)) {
|
||||
rPos++;
|
||||
if (pos == runs.size()) {
|
||||
return createRun();
|
||||
}
|
||||
return insertNewProvidedRun(pos, newCursor -> {
|
||||
String uri = CTR.type.getName().getNamespaceURI();
|
||||
String localPart = "r";
|
||||
// creates a new run, cursor is positioned inside the new
|
||||
// element
|
||||
newCursor.beginElement(localPart, uri);
|
||||
// move the cursor to the START token to the run just created
|
||||
newCursor.toParent();
|
||||
CTR r = (CTR) newCursor.getObject();
|
||||
return new XWPFRun(r, (IRunBody)this);
|
||||
});
|
||||
}
|
||||
|
||||
CTR ctRun = paragraph.insertNewR(rPos);
|
||||
XWPFRun newRun = new XWPFRun(ctRun, (IRunBody) this);
|
||||
/**
|
||||
* insert a new hyperlink Run in all runs
|
||||
*
|
||||
* @param pos The position at which the new run should be added.
|
||||
* @param uri hyperlink uri
|
||||
*
|
||||
* @return the inserted run or null if the given pos is out of bounds.
|
||||
*/
|
||||
public XWPFHyperlinkRun insertNewHyperlinkRun(int pos, String uri) {
|
||||
if (pos == runs.size()) {
|
||||
return createHyperlinkRun(uri);
|
||||
}
|
||||
XWPFHyperlinkRun newRun = insertNewProvidedRun(pos, newCursor -> {
|
||||
String namespaceURI = CTHyperlink.type.getName().getNamespaceURI();
|
||||
String localPart = "hyperlink";
|
||||
newCursor.beginElement(localPart, namespaceURI);
|
||||
// move the cursor to the START token to the hyperlink just created
|
||||
newCursor.toParent();
|
||||
CTHyperlink ctHyperLink = (CTHyperlink) newCursor.getObject();
|
||||
return new XWPFHyperlinkRun(ctHyperLink, ctHyperLink.addNewR(), this);
|
||||
});
|
||||
|
||||
String rId = getPart().getPackagePart().addExternalRelationship(
|
||||
uri, XWPFRelation.HYPERLINK.getRelation()
|
||||
).getId();
|
||||
newRun.getCTHyperlink().setId(rId);
|
||||
return newRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert a new field Run in all runs
|
||||
*
|
||||
* @param pos The position at which the new run should be added.
|
||||
*
|
||||
* @return the inserted run or null if the given pos is out of bounds.
|
||||
*/
|
||||
public XWPFFieldRun insertNewFieldRun(int pos) {
|
||||
if (pos == runs.size()) {
|
||||
return createFieldRun();
|
||||
}
|
||||
return insertNewProvidedRun(pos, newCursor -> {
|
||||
String uri = CTSimpleField.type.getName().getNamespaceURI();
|
||||
String localPart = "fldSimple";
|
||||
newCursor.beginElement(localPart, uri);
|
||||
// move the cursor to the START token to the field just created
|
||||
newCursor.toParent();
|
||||
CTSimpleField ctSimpleField = (CTSimpleField) newCursor.getObject();
|
||||
return new XWPFFieldRun(ctSimpleField, ctSimpleField.addNewR(), this);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* insert a new run provided by in all runs
|
||||
*
|
||||
* @param <T> XWPFRun or XWPFHyperlinkRun or XWPFFieldRun
|
||||
* @param pos The position at which the new run should be added.
|
||||
* @param provider provide a new run at position of the given cursor.
|
||||
* @return the inserted run or null if the given pos is out of bounds.
|
||||
*/
|
||||
private <T extends XWPFRun> T insertNewProvidedRun(int pos, Function<XmlCursor, T> provider) {
|
||||
if (pos >= 0 && pos < runs.size()) {
|
||||
XWPFRun run = runs.get(pos);
|
||||
CTR ctr = run.getCTR();
|
||||
XmlCursor newCursor = ctr.newCursor();
|
||||
if (!isCursorInParagraph(newCursor)) {
|
||||
// look up correct position for CTP -> XXX -> R array
|
||||
newCursor.toParent();
|
||||
}
|
||||
if (isCursorInParagraph(newCursor)) {
|
||||
// provide a new run
|
||||
T newRun = provider.apply(newCursor);
|
||||
|
||||
// To update the iruns, find where we're going
|
||||
// in the normal runs, and go in there
|
||||
int iPos = iruns.size();
|
||||
if (pos < runs.size()) {
|
||||
XWPFRun oldAtPos = runs.get(pos);
|
||||
int oldAt = iruns.indexOf(oldAtPos);
|
||||
int oldAt = iruns.indexOf(run);
|
||||
if (oldAt != -1) {
|
||||
iPos = oldAt;
|
||||
}
|
||||
}
|
||||
iruns.add(iPos, newRun);
|
||||
|
||||
// Runs itself is easy to update
|
||||
runs.add(pos, newRun);
|
||||
|
||||
return newRun;
|
||||
}
|
||||
|
||||
newCursor.dispose();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// TODO Add methods to allow adding a HyperlinkRun or a FieldRun
|
||||
|
||||
/**
|
||||
* verifies that cursor is on the right position
|
||||
*
|
||||
* @param cursor
|
||||
* @return
|
||||
*/
|
||||
private boolean isCursorInParagraph(XmlCursor cursor) {
|
||||
XmlCursor verify = cursor.newCursor();
|
||||
verify.toParent();
|
||||
boolean result = (verify.getObject() == this.paragraph);
|
||||
verify.dispose();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* this methods parse the paragraph and search for the string searched.
|
||||
|
@ -1643,31 +1738,67 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
|
|||
*/
|
||||
public boolean removeRun(int pos) {
|
||||
if (pos >= 0 && pos < runs.size()) {
|
||||
// Remove the run from our high level lists
|
||||
XWPFRun run = runs.get(pos);
|
||||
if (run instanceof XWPFHyperlinkRun ||
|
||||
run instanceof XWPFFieldRun) {
|
||||
// TODO Add support for removing these kinds of nested runs,
|
||||
// which aren't on the CTP -> R array, but CTP -> XXX -> R array
|
||||
throw new IllegalArgumentException("Removing Field or Hyperlink runs not yet supported");
|
||||
}
|
||||
// CTP -> CTHyperlink -> R array
|
||||
if (run instanceof XWPFHyperlinkRun
|
||||
&& isTheOnlyCTHyperlinkInRuns((XWPFHyperlinkRun) run)) {
|
||||
XmlCursor c = ((XWPFHyperlinkRun) run).getCTHyperlink()
|
||||
.newCursor();
|
||||
c.removeXml();
|
||||
c.dispose();
|
||||
runs.remove(pos);
|
||||
iruns.remove(run);
|
||||
// Remove the run from the low-level XML
|
||||
//calculate the correct pos as our run/irun list contains hyperlinks and fields so is different to the paragraph R array.
|
||||
int rPos = 0;
|
||||
for(int i=0;i<pos;i++) {
|
||||
XWPFRun currRun = runs.get(i);
|
||||
if(!(currRun instanceof XWPFHyperlinkRun || currRun instanceof XWPFFieldRun)) {
|
||||
rPos++;
|
||||
return true;
|
||||
}
|
||||
// CTP -> CTField -> R array
|
||||
if (run instanceof XWPFFieldRun
|
||||
&& isTheOnlyCTFieldInRuns((XWPFFieldRun) run)) {
|
||||
XmlCursor c = ((XWPFFieldRun) run).getCTField().newCursor();
|
||||
c.removeXml();
|
||||
c.dispose();
|
||||
runs.remove(pos);
|
||||
iruns.remove(run);
|
||||
return true;
|
||||
}
|
||||
getCTP().removeR(rPos);
|
||||
XmlCursor c = run.getCTR().newCursor();
|
||||
c.removeXml();
|
||||
c.dispose();
|
||||
runs.remove(pos);
|
||||
iruns.remove(run);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there only one ctHyperlink in all runs
|
||||
*
|
||||
* @param run
|
||||
* hyperlink run
|
||||
* @return
|
||||
*/
|
||||
private boolean isTheOnlyCTHyperlinkInRuns(XWPFHyperlinkRun run) {
|
||||
CTHyperlink ctHyperlink = run.getCTHyperlink();
|
||||
long count = runs.stream().filter(r -> (r instanceof XWPFHyperlinkRun
|
||||
&& ctHyperlink == ((XWPFHyperlinkRun) r).getCTHyperlink()))
|
||||
.count();
|
||||
return count <= 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there only one ctField in all runs
|
||||
*
|
||||
* @param run
|
||||
* field run
|
||||
* @return
|
||||
*/
|
||||
private boolean isTheOnlyCTFieldInRuns(XWPFFieldRun run) {
|
||||
CTSimpleField ctField = run.getCTField();
|
||||
long count = runs.stream().filter(r -> (r instanceof XWPFFieldRun
|
||||
&& ctField == ((XWPFFieldRun) r).getCTField())).count();
|
||||
return count <= 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the type of the BodyElement Paragraph
|
||||
*
|
||||
|
|
|
@ -327,6 +327,138 @@ public final class TestXWPFParagraph {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNewRuns() throws IOException {
|
||||
try (XWPFDocument doc = new XWPFDocument()) {
|
||||
|
||||
XWPFParagraph p = doc.createParagraph();
|
||||
XWPFHyperlinkRun h = p.createHyperlinkRun("http://poi.apache.org");
|
||||
XWPFFieldRun fieldRun = p.createFieldRun();
|
||||
XWPFRun r = p.createRun();
|
||||
|
||||
assertEquals(3, p.getRuns().size());
|
||||
assertEquals(0, p.getRuns().indexOf(h));
|
||||
assertEquals(1, p.getRuns().indexOf(fieldRun));
|
||||
assertEquals(2, p.getRuns().indexOf(r));
|
||||
|
||||
assertEquals(3, p.getIRuns().size());
|
||||
assertEquals(0, p.getIRuns().indexOf(h));
|
||||
assertEquals(1, p.getIRuns().indexOf(fieldRun));
|
||||
assertEquals(2, p.getIRuns().indexOf(r));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertNewRuns() throws IOException {
|
||||
try (XWPFDocument doc = new XWPFDocument()) {
|
||||
|
||||
XWPFParagraph p = doc.createParagraph();
|
||||
XWPFRun r = p.createRun();
|
||||
assertEquals(1, p.getRuns().size());
|
||||
assertEquals(0, p.getRuns().indexOf(r));
|
||||
|
||||
XWPFHyperlinkRun h = p.insertNewHyperlinkRun(0, "http://poi.apache.org");
|
||||
assertEquals(2, p.getRuns().size());
|
||||
assertEquals(0, p.getRuns().indexOf(h));
|
||||
assertEquals(1, p.getRuns().indexOf(r));
|
||||
|
||||
XWPFFieldRun fieldRun2 = p.insertNewFieldRun(2);
|
||||
assertEquals(3, p.getRuns().size());
|
||||
assertEquals(2, p.getRuns().indexOf(fieldRun2));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveRuns() throws IOException {
|
||||
try (XWPFDocument doc = new XWPFDocument()) {
|
||||
|
||||
XWPFParagraph p = doc.createParagraph();
|
||||
XWPFRun r = p.createRun();
|
||||
p.createRun();
|
||||
XWPFHyperlinkRun hyperlinkRun = p
|
||||
.createHyperlinkRun("http://poi.apache.org");
|
||||
XWPFFieldRun fieldRun = p.createFieldRun();
|
||||
|
||||
assertEquals(4, p.getRuns().size());
|
||||
assertEquals(2, p.getRuns().indexOf(hyperlinkRun));
|
||||
assertEquals(3, p.getRuns().indexOf(fieldRun));
|
||||
|
||||
p.removeRun(2);
|
||||
assertEquals(3, p.getRuns().size());
|
||||
assertEquals(-1, p.getRuns().indexOf(hyperlinkRun));
|
||||
assertEquals(2, p.getRuns().indexOf(fieldRun));
|
||||
|
||||
p.removeRun(0);
|
||||
assertEquals(2, p.getRuns().size());
|
||||
assertEquals(-1, p.getRuns().indexOf(r));
|
||||
assertEquals(1, p.getRuns().indexOf(fieldRun));
|
||||
|
||||
p.removeRun(1);
|
||||
assertEquals(1, p.getRuns().size());
|
||||
assertEquals(-1, p.getRuns().indexOf(fieldRun));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAndInsertRunsWithOtherIRunElement()
|
||||
throws IOException {
|
||||
XWPFDocument doc = new XWPFDocument();
|
||||
|
||||
XWPFParagraph p = doc.createParagraph();
|
||||
p.createRun();
|
||||
// add other run element
|
||||
p.getCTP().addNewSdt();
|
||||
// add two CTR in hyperlink
|
||||
XWPFHyperlinkRun hyperlinkRun = p
|
||||
.createHyperlinkRun("http://poi.apache.org");
|
||||
hyperlinkRun.getCTHyperlink().addNewR();
|
||||
p.createFieldRun();
|
||||
|
||||
XWPFDocument doc2 = XWPFTestDataSamples.writeOutAndReadBack(doc);
|
||||
XWPFParagraph paragraph = doc2.getParagraphArray(0);
|
||||
|
||||
assertEquals(4, paragraph.getRuns().size());
|
||||
assertEquals(5, paragraph.getIRuns().size());
|
||||
|
||||
assertTrue(paragraph.getRuns().get(1) instanceof XWPFHyperlinkRun);
|
||||
assertTrue(paragraph.getRuns().get(2) instanceof XWPFHyperlinkRun);
|
||||
assertTrue(paragraph.getRuns().get(3) instanceof XWPFFieldRun);
|
||||
|
||||
assertTrue(paragraph.getIRuns().get(1) instanceof XWPFSDT);
|
||||
assertTrue(paragraph.getIRuns().get(2) instanceof XWPFHyperlinkRun);
|
||||
|
||||
paragraph.removeRun(1);
|
||||
assertEquals(3, paragraph.getRuns().size());
|
||||
assertTrue(paragraph.getRuns().get(1) instanceof XWPFHyperlinkRun);
|
||||
assertTrue(paragraph.getRuns().get(2) instanceof XWPFFieldRun);
|
||||
|
||||
assertTrue(paragraph.getIRuns().get(1) instanceof XWPFSDT);
|
||||
assertTrue(paragraph.getIRuns().get(2) instanceof XWPFHyperlinkRun);
|
||||
|
||||
paragraph.removeRun(1);
|
||||
assertEquals(2, paragraph.getRuns().size());
|
||||
assertTrue(paragraph.getRuns().get(1) instanceof XWPFFieldRun);
|
||||
|
||||
assertTrue(paragraph.getIRuns().get(1) instanceof XWPFSDT);
|
||||
assertTrue(paragraph.getIRuns().get(2) instanceof XWPFFieldRun);
|
||||
|
||||
paragraph.removeRun(0);
|
||||
assertEquals(1, paragraph.getRuns().size());
|
||||
assertTrue(paragraph.getRuns().get(0) instanceof XWPFFieldRun);
|
||||
|
||||
assertTrue(paragraph.getIRuns().get(0) instanceof XWPFSDT);
|
||||
assertTrue(paragraph.getIRuns().get(1) instanceof XWPFFieldRun);
|
||||
|
||||
XWPFRun newRun = paragraph.insertNewRun(0);
|
||||
assertEquals(2, paragraph.getRuns().size());
|
||||
|
||||
assertEquals(3, paragraph.getIRuns().size());
|
||||
assertEquals(0, paragraph.getRuns().indexOf(newRun));
|
||||
|
||||
doc.close();
|
||||
doc2.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPictures() throws IOException {
|
||||
try (XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("VariousPictures.docx")) {
|
||||
|
|
Loading…
Reference in New Issue