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.UnderlinePatterns;
|
||||||
import org.apache.poi.xwpf.usermodel.VerticalAlign;
|
import org.apache.poi.xwpf.usermodel.VerticalAlign;
|
||||||
import org.apache.poi.xwpf.usermodel.XWPFDocument;
|
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.XWPFParagraph;
|
||||||
import org.apache.poi.xwpf.usermodel.XWPFRun;
|
import org.apache.poi.xwpf.usermodel.XWPFRun;
|
||||||
|
|
||||||
|
@ -78,6 +79,11 @@ public class SimpleDocument {
|
||||||
r3.setFontSize(20);
|
r3.setFontSize(20);
|
||||||
r3.setSubscript(VerticalAlign.SUPERSCRIPT);
|
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();
|
XWPFParagraph p3 = doc.createParagraph();
|
||||||
p3.setWordWrapped(true);
|
p3.setWordWrapped(true);
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.apache.poi.ooxml.POIXMLDocumentPart;
|
import org.apache.poi.ooxml.POIXMLDocumentPart;
|
||||||
import org.apache.poi.util.Internal;
|
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.
|
* @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.
|
* @return the inserted run or null if the given pos is out of bounds.
|
||||||
*/
|
*/
|
||||||
public XWPFRun insertNewRun(int pos) {
|
public XWPFRun insertNewRun(int pos) {
|
||||||
if (pos >= 0 && pos <= runs.size()) {
|
if (pos == runs.size()) {
|
||||||
// calculate the correct pos as our run/irun list contains
|
return createRun();
|
||||||
// hyperlinks
|
}
|
||||||
// and fields so it is different to the paragraph R array.
|
return insertNewProvidedRun(pos, newCursor -> {
|
||||||
int rPos = 0;
|
String uri = CTR.type.getName().getNamespaceURI();
|
||||||
for (int i = 0; i < pos; i++) {
|
String localPart = "r";
|
||||||
XWPFRun currRun = runs.get(i);
|
// creates a new run, cursor is positioned inside the new
|
||||||
if (!(currRun instanceof XWPFHyperlinkRun
|
// element
|
||||||
|| currRun instanceof XWPFFieldRun)) {
|
newCursor.beginElement(localPart, uri);
|
||||||
rPos++;
|
// 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
CTR ctRun = paragraph.insertNewR(rPos);
|
// To update the iruns, find where we're going
|
||||||
XWPFRun newRun = new XWPFRun(ctRun, (IRunBody) this);
|
// in the normal runs, and go in there
|
||||||
|
int iPos = iruns.size();
|
||||||
// To update the iruns, find where we're going
|
int oldAt = iruns.indexOf(run);
|
||||||
// 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);
|
|
||||||
if (oldAt != -1) {
|
if (oldAt != -1) {
|
||||||
iPos = oldAt;
|
iPos = oldAt;
|
||||||
}
|
}
|
||||||
|
iruns.add(iPos, newRun);
|
||||||
|
// Runs itself is easy to update
|
||||||
|
runs.add(pos, newRun);
|
||||||
|
return newRun;
|
||||||
}
|
}
|
||||||
iruns.add(iPos, newRun);
|
newCursor.dispose();
|
||||||
|
|
||||||
// Runs itself is easy to update
|
|
||||||
runs.add(pos, newRun);
|
|
||||||
|
|
||||||
return newRun;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
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.
|
* 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) {
|
public boolean removeRun(int pos) {
|
||||||
if (pos >= 0 && pos < runs.size()) {
|
if (pos >= 0 && pos < runs.size()) {
|
||||||
// Remove the run from our high level lists
|
|
||||||
XWPFRun run = runs.get(pos);
|
XWPFRun run = runs.get(pos);
|
||||||
if (run instanceof XWPFHyperlinkRun ||
|
// CTP -> CTHyperlink -> R array
|
||||||
run instanceof XWPFFieldRun) {
|
if (run instanceof XWPFHyperlinkRun
|
||||||
// TODO Add support for removing these kinds of nested runs,
|
&& isTheOnlyCTHyperlinkInRuns((XWPFHyperlinkRun) run)) {
|
||||||
// which aren't on the CTP -> R array, but CTP -> XXX -> R array
|
XmlCursor c = ((XWPFHyperlinkRun) run).getCTHyperlink()
|
||||||
throw new IllegalArgumentException("Removing Field or Hyperlink runs not yet supported");
|
.newCursor();
|
||||||
|
c.removeXml();
|
||||||
|
c.dispose();
|
||||||
|
runs.remove(pos);
|
||||||
|
iruns.remove(run);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
XmlCursor c = run.getCTR().newCursor();
|
||||||
|
c.removeXml();
|
||||||
|
c.dispose();
|
||||||
runs.remove(pos);
|
runs.remove(pos);
|
||||||
iruns.remove(run);
|
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getCTP().removeR(rPos);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
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
|
* 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
|
@Test
|
||||||
public void testPictures() throws IOException {
|
public void testPictures() throws IOException {
|
||||||
try (XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("VariousPictures.docx")) {
|
try (XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("VariousPictures.docx")) {
|
||||||
|
|
Loading…
Reference in New Issue