diff --git a/src/examples/src/org/apache/poi/hslf/examples/CreateHyperlink.java b/src/examples/src/org/apache/poi/hslf/examples/CreateHyperlink.java
index 44e5d81c1..fc25afae5 100644
--- a/src/examples/src/org/apache/poi/hslf/examples/CreateHyperlink.java
+++ b/src/examples/src/org/apache/poi/hslf/examples/CreateHyperlink.java
@@ -39,38 +39,26 @@ public abstract class CreateHyperlink {
HSLFSlide slideC = ppt.createSlide();
// link to a URL
- HSLFTextBox textBox1 = new HSLFTextBox();
+ HSLFTextBox textBox1 = slideA.createTextBox();
textBox1.setText("Apache POI");
textBox1.setAnchor(new Rectangle(100, 100, 200, 50));
- String text = textBox1.getText();
- HSLFHyperlink link = new HSLFHyperlink();
- link.setAddress("http://www.apache.org");
- link.setLabel(textBox1.getText());
- int linkId = ppt.addHyperlink(link);
-
- // apply link to the text
- textBox1.setHyperlink(linkId, 0, text.length());
-
- slideA.addShape(textBox1);
+ HSLFHyperlink link1 = textBox1.getTextParagraphs().get(0).getTextRuns().get(0).createHyperlink();
+ link1.linkToUrl("http://www.apache.org");
+ link1.setLabel(textBox1.getText());
// link to another slide
- HSLFTextBox textBox2 = new HSLFTextBox();
+ HSLFTextBox textBox2 = slideA.createTextBox();
textBox2.setText("Go to slide #3");
textBox2.setAnchor(new Rectangle(100, 300, 200, 50));
- HSLFHyperlink link2 = new HSLFHyperlink();
- link2.setAddress(slideC);
- ppt.addHyperlink(link2);
-
- // apply link to the whole shape
- textBox2.setHyperlink(link2);
-
- slideA.addShape(textBox2);
+ HSLFHyperlink link2 = textBox2.getTextParagraphs().get(0).getTextRuns().get(0).createHyperlink();
+ link2.linkToSlide(slideC);
FileOutputStream out = new FileOutputStream("hyperlink.ppt");
ppt.write(out);
out.close();
+ ppt.close();
}
}
diff --git a/src/examples/src/org/apache/poi/hslf/examples/Hyperlinks.java b/src/examples/src/org/apache/poi/hslf/examples/Hyperlinks.java
index 8cbbcdc85..812429295 100644
--- a/src/examples/src/org/apache/poi/hslf/examples/Hyperlinks.java
+++ b/src/examples/src/org/apache/poi/hslf/examples/Hyperlinks.java
@@ -19,12 +19,15 @@ package org.apache.poi.hslf.examples;
import java.io.FileInputStream;
import java.util.List;
+import java.util.Locale;
import org.apache.poi.hslf.usermodel.HSLFHyperlink;
import org.apache.poi.hslf.usermodel.HSLFShape;
+import org.apache.poi.hslf.usermodel.HSLFSimpleShape;
import org.apache.poi.hslf.usermodel.HSLFSlide;
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
import org.apache.poi.hslf.usermodel.HSLFTextParagraph;
+import org.apache.poi.hslf.usermodel.HSLFTextRun;
/**
* Demonstrates how to read hyperlinks from a presentation
@@ -44,12 +47,14 @@ public final class Hyperlinks {
// read hyperlinks from the slide's text runs
System.out.println("- reading hyperlinks from the text runs");
- for (List txtParas : slide.getTextParagraphs()) {
- List links = HSLFHyperlink.find(txtParas);
- String text = HSLFTextParagraph.getRawText(txtParas);
-
- for (HSLFHyperlink link : links) {
- System.out.println(toStr(link, text));
+ for (List paras : slide.getTextParagraphs()) {
+ for (HSLFTextParagraph para : paras) {
+ for (HSLFTextRun run : para) {
+ HSLFHyperlink link = run.getHyperlink();
+ if (link != null) {
+ System.out.println(toStr(link, run.getRawText()));
+ }
+ }
}
}
@@ -58,18 +63,21 @@ public final class Hyperlinks {
// read such hyperlinks
System.out.println("- reading hyperlinks from the slide's shapes");
for (HSLFShape sh : slide.getShapes()) {
- HSLFHyperlink link = HSLFHyperlink.find(sh);
- if (link == null) continue;
- System.out.println(toStr(link, null));
+ if (sh instanceof HSLFSimpleShape) {
+ HSLFHyperlink link = ((HSLFSimpleShape)sh).getHyperlink();
+ if (link != null) {
+ System.out.println(toStr(link, null));
+ }
+ }
}
}
+ ppt.close();
}
}
static String toStr(HSLFHyperlink link, String rawText) {
//in ppt end index is inclusive
String formatStr = "title: %1$s, address: %2$s" + (rawText == null ? "" : ", start: %3$s, end: %4$s, substring: %5$s");
- String substring = (rawText == null) ? "" : rawText.substring(link.getStartIndex(), link.getEndIndex()-1);
- return String.format(formatStr, link.getLabel(), link.getAddress(), link.getStartIndex(), link.getEndIndex(), substring);
+ return String.format(Locale.ROOT, formatStr, link.getLabel(), link.getAddress(), link.getStartIndex(), link.getEndIndex(), rawText);
}
}
diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial6.java b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial6.java
index e3fdd85df..73a59d0b8 100644
--- a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial6.java
+++ b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial6.java
@@ -48,7 +48,7 @@ public class Tutorial6 {
XSLFTextRun r2 = shape2.addNewTextParagraph().addNewTextRun();
XSLFHyperlink link2 = r2.createHyperlink();
r2.setText("Go to the second slide"); // visible text
- link2.setAddress(slide2); // link address
+ link2.linkToSlide(slide2); // link address
diff --git a/src/java/org/apache/poi/sl/draw/DrawPictureShape.java b/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
index 972166058..beadc1f98 100644
--- a/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
@@ -61,7 +61,7 @@ public class DrawPictureShape extends DrawSimpleShape {
* Returns an ImageRenderer for the PictureData
*
* @param graphics
- * @return
+ * @return the image renderer
*/
public static ImageRenderer getImageRenderer(Graphics2D graphics, String contentType) {
ImageRenderer renderer = (ImageRenderer)graphics.getRenderingHint(Drawable.IMAGE_RENDERER);
diff --git a/src/java/org/apache/poi/sl/usermodel/Hyperlink.java b/src/java/org/apache/poi/sl/usermodel/Hyperlink.java
index cde288492..50500d04b 100644
--- a/src/java/org/apache/poi/sl/usermodel/Hyperlink.java
+++ b/src/java/org/apache/poi/sl/usermodel/Hyperlink.java
@@ -20,5 +20,59 @@ package org.apache.poi.sl.usermodel;
/**
* A PowerPoint hyperlink
*/
-public interface Hyperlink extends org.apache.poi.common.usermodel.Hyperlink {
+public interface Hyperlink<
+ S extends Shape,
+ P extends TextParagraph
+> extends org.apache.poi.common.usermodel.Hyperlink {
+ /**
+ * Link to an email
+ *
+ * @param emailAddress the email address
+ * @since POI 3.14-Beta2
+ */
+ void linkToEmail(String emailAddress);
+
+ /**
+ * Link to a web page / URL
+ *
+ * @param url the url
+ * @since POI 3.14-Beta2
+ */
+ void linkToUrl(String url);
+
+ /**
+ * Link to a slide in this slideshow
+ *
+ * @param slide the linked slide
+ * @since POI 3.14-Beta2
+ */
+ void linkToSlide(Slide slide);
+
+ /**
+ * Link to the next slide (relative from the current)
+ *
+ * @since POI 3.14-Beta2
+ */
+ void linkToNextSlide();
+
+ /**
+ * Link to the previous slide (relative from the current)
+ *
+ * @since POI 3.14-Beta2
+ */
+ void linkToPreviousSlide();
+
+ /**
+ * Link to the first slide in this slideshow
+ *
+ * @since POI 3.14-Beta2
+ */
+ void linkToFirstSlide();
+
+ /**
+ * Link to the last slide in this slideshow
+ *
+ * @since POI 3.14-Beta2
+ */
+ void linkToLastSlide();
}
diff --git a/src/java/org/apache/poi/sl/usermodel/SimpleShape.java b/src/java/org/apache/poi/sl/usermodel/SimpleShape.java
index 53ee6de52..aee69fb73 100644
--- a/src/java/org/apache/poi/sl/usermodel/SimpleShape.java
+++ b/src/java/org/apache/poi/sl/usermodel/SimpleShape.java
@@ -83,4 +83,24 @@ public interface SimpleShape<
* the solid fill attribute from the underlying implementation
*/
void setFillColor(Color color);
+
+ /**
+ * Returns the hyperlink assigned to this shape
+ *
+ * @return the hyperlink assigned to this shape
+ * or null
if not found.
+ *
+ * @since POI 3.14-Beta1
+ */
+ Hyperlink getHyperlink();
+
+ /**
+ * Creates a hyperlink and asigns it to this shape.
+ * If the shape has already a hyperlink assigned, return it instead
+ *
+ * @return the hyperlink assigned to this shape
+ *
+ * @since POI 3.14-Beta1
+ */
+ Hyperlink createHyperlink();
}
diff --git a/src/java/org/apache/poi/sl/usermodel/TextRun.java b/src/java/org/apache/poi/sl/usermodel/TextRun.java
index 28db79795..014d3036b 100644
--- a/src/java/org/apache/poi/sl/usermodel/TextRun.java
+++ b/src/java/org/apache/poi/sl/usermodel/TextRun.java
@@ -161,6 +161,19 @@ public interface TextRun {
* Return the associated hyperlink
*
* @return the associated hyperlink or null if no hyperlink was set
+ *
+ * @since POI 3.14-Beta2
*/
- Hyperlink getHyperlink();
+ Hyperlink,?> getHyperlink();
+
+
+ /**
+ * Creates a new hyperlink and assigns it to this text run.
+ * If the text run has already a hyperlink assigned, return it instead
+ *
+ * @return the associated hyperlink
+ *
+ * @since POI 3.14-Beta2
+ */
+ Hyperlink,?> createHyperlink();
}
diff --git a/src/java/org/apache/poi/sl/usermodel/TextShape.java b/src/java/org/apache/poi/sl/usermodel/TextShape.java
index df302ff62..cc39a5181 100644
--- a/src/java/org/apache/poi/sl/usermodel/TextShape.java
+++ b/src/java/org/apache/poi/sl/usermodel/TextShape.java
@@ -118,6 +118,16 @@ public interface TextShape<
OTHER
}
+ /**
+ * Returns the text contained in this text frame, which has been made safe
+ * for printing and other use.
+ *
+ * @return the text string for this textbox.
+ *
+ * @since POI 3.14-Beta2
+ */
+ String getText();
+
/**
* Sets (overwrites) the current text.
* Uses the properties of the first paragraph / textrun.
@@ -129,6 +139,18 @@ public interface TextShape<
* @return the last text run of the - potential split - text
*/
TextRun setText(String text);
+
+ /**
+ * Adds the supplied text onto the end of the TextParagraphs,
+ * creating a new RichTextRun for it to sit in.
+ *
+ * @param text the text string to be appended.
+ * @param newParagraph if true, a new paragraph will be added,
+ * which will contain the added text
+ *
+ * @since POI 3.14-Beta1
+ */
+ TextRun appendText(String text, boolean newParagraph);
/**
* @return the TextParagraphs for this text box
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFHyperlink.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFHyperlink.java
index 0b093ecac..6f4a5228d 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFHyperlink.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFHyperlink.java
@@ -16,20 +16,23 @@
==================================================================== */
package org.apache.poi.xslf.usermodel;
+import java.net.URI;
+
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.sl.usermodel.Hyperlink;
+import org.apache.poi.sl.usermodel.Slide;
import org.apache.poi.util.Internal;
import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink;
-import java.net.URI;
-
-public class XSLFHyperlink implements Hyperlink {
- final XSLFTextRun _r;
+public class XSLFHyperlink implements Hyperlink {
+ final XSLFSheet _sheet;
final CTHyperlink _link;
- XSLFHyperlink(CTHyperlink link, XSLFTextRun r){
- _r = r;
+ XSLFHyperlink(CTHyperlink link, XSLFSheet sheet){
+ _sheet = sheet;
_link = link;
}
@@ -39,24 +42,27 @@ public class XSLFHyperlink implements Hyperlink {
}
@Override
- public void setAddress(String address){
- XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet();
- PackageRelationship rel =
- sheet.getPackagePart().
- addExternalRelationship(address, XSLFRelation.HYPERLINK.getRelation());
- _link.setId(rel.getId());
+ public void setAddress(String address) {
+ linkToUrl(address);
}
-
+
@Override
public String getAddress() {
- return getTargetURI().toASCIIString();
+ if (!_link.isSetId()) {
+ return _link.getAction();
+ }
+
+ String id = _link.getId();
+ URI targetURI = _sheet.getPackagePart().getRelationship(id).getTargetURI();
+
+ return targetURI.toASCIIString();
}
@Override
public String getLabel() {
return _link.getTooltip();
}
-
+
@Override
public void setLabel(String label) {
_link.setTooltip(label);
@@ -64,28 +70,88 @@ public class XSLFHyperlink implements Hyperlink {
@Override
public int getType() {
- // TODO: currently this just returns nonsense
- if ("ppaction://hlinksldjump".equals(_link.getAction())) {
+ String action = _link.getAction();
+ if (action == null) {
+ action = "";
+ }
+ if (action.equals("ppaction://hlinksldjump") || action.startsWith("ppaction://hlinkshowjump")) {
return LINK_DOCUMENT;
}
- return LINK_URL;
+
+ String address = getAddress();
+ if (address == null) {
+ address = "";
+ }
+ if (address.startsWith("mailto:")) {
+ return LINK_EMAIL;
+ } else {
+ return LINK_URL;
+ }
}
-
- public void setAddress(XSLFSlide slide){
- XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet();
+
+ @Override
+ public void linkToEmail(String emailAddress) {
+ linkToExternal("mailto:"+emailAddress);
+ setLabel(emailAddress);
+ }
+
+ @Override
+ public void linkToUrl(String url) {
+ linkToExternal(url);
+ setLabel(url);
+ }
+
+ private void linkToExternal(String url) {
+ PackagePart thisPP = _sheet.getPackagePart();
+ if (_link.isSetId() && !_link.getId().isEmpty()) {
+ thisPP.removeRelationship(_link.getId());
+ }
+ PackageRelationship rel = thisPP.addExternalRelationship(url, XSLFRelation.HYPERLINK.getRelation());
+ _link.setId(rel.getId());
+ if (_link.isSetAction()) {
+ _link.unsetAction();
+ }
+ }
+
+ @Override
+ public void linkToSlide(Slide slide) {
+ PackagePart thisPP = _sheet.getPackagePart();
+ PackagePartName otherPPN = ((XSLFSheet)slide).getPackagePart().getPartName();
+ if (_link.isSetId() && !_link.getId().isEmpty()) {
+ thisPP.removeRelationship(_link.getId());
+ }
PackageRelationship rel =
- sheet.getPackagePart().
- addRelationship(slide.getPackagePart().getPartName(),
- TargetMode.INTERNAL,
- XSLFRelation.SLIDE.getRelation());
+ thisPP.addRelationship(otherPPN, TargetMode.INTERNAL, XSLFRelation.SLIDE.getRelation());
_link.setId(rel.getId());
_link.setAction("ppaction://hlinksldjump");
}
- @Internal
- public URI getTargetURI(){
- XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet();
- String id = _link.getId();
- return sheet.getPackagePart().getRelationship(id).getTargetURI();
+ @Override
+ public void linkToNextSlide() {
+ linkToRelativeSlide("nextslide");
}
-}
+
+ @Override
+ public void linkToPreviousSlide() {
+ linkToRelativeSlide("previousslide");
+ }
+
+ @Override
+ public void linkToFirstSlide() {
+ linkToRelativeSlide("firstslide");
+ }
+
+ @Override
+ public void linkToLastSlide() {
+ linkToRelativeSlide("lastslide");
+ }
+
+ private void linkToRelativeSlide(String jump) {
+ PackagePart thisPP = _sheet.getPackagePart();
+ if (_link.isSetId() && !_link.getId().isEmpty()) {
+ thisPP.removeRelationship(_link.getId());
+ }
+ _link.setId("");
+ _link.setAction("ppaction://hlinkshowjump?jump="+jump);
+ }
+}
\ No newline at end of file
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java
index 8b92ec231..636647ef2 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java
@@ -34,8 +34,8 @@ import org.apache.poi.sl.usermodel.LineDecoration;
import org.apache.poi.sl.usermodel.LineDecoration.DecorationShape;
import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize;
import org.apache.poi.sl.usermodel.PaintStyle;
-import org.apache.poi.sl.usermodel.Placeholder;
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
+import org.apache.poi.sl.usermodel.Placeholder;
import org.apache.poi.sl.usermodel.ShapeType;
import org.apache.poi.sl.usermodel.SimpleShape;
import org.apache.poi.sl.usermodel.StrokeStyle;
@@ -53,6 +53,7 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTGeomGuide;
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineEndProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineStyleList;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTOuterShadowEffect;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
@@ -923,4 +924,23 @@ public abstract class XSLFSimpleShape extends XSLFShape
public void setPlaceholder(Placeholder placeholder) {
super.setPlaceholder(placeholder);
}
+
+ @Override
+ public XSLFHyperlink getHyperlink() {
+ CTNonVisualDrawingProps cNvPr = getCNvPr();
+ if (!cNvPr.isSetHlinkClick()) {
+ return null;
+ }
+ return new XSLFHyperlink(cNvPr.getHlinkClick(), getSheet());
+ }
+
+ @Override
+ public XSLFHyperlink createHyperlink() {
+ XSLFHyperlink hl = getHyperlink();
+ if (hl == null) {
+ CTNonVisualDrawingProps cNvPr = getCNvPr();
+ hl = new XSLFHyperlink(cNvPr.addNewHlinkClick(), getSheet());
+ }
+ return hl;
+ }
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java
index 5abb09837..298bcc781 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java
@@ -994,4 +994,26 @@ public class XSLFTextParagraph implements TextParagraph0; i--) {
+ thisP.removeBr(i-1);
+ }
+ for (int i=thisP.sizeOfFldArray(); i>0; i--) {
+ thisP.removeFld(i-1);
+ }
+ if (!_runs.isEmpty()) {
+ int size = _runs.size();
+ thisP.setEndParaRPr(_runs.get(size-1).getRPr());
+ for (int i=size; i>0; i--) {
+ thisP.removeR(i-1);
+ }
+ _runs.clear();
+ }
+ }
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
index 1b2272ba3..049de5026 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
@@ -444,17 +444,19 @@ public class XSLFTextRun implements TextRun {
return "[" + getClass() + "]" + getRawText();
}
+ @Override
public XSLFHyperlink createHyperlink(){
- XSLFHyperlink link = new XSLFHyperlink(_r.getRPr().addNewHlinkClick(), this);
- return link;
+ XSLFHyperlink hl = getHyperlink();
+ if (hl == null) {
+ hl = new XSLFHyperlink(_r.getRPr().addNewHlinkClick(), _p.getParentShape().getSheet());
+ }
+ return hl;
}
@Override
public XSLFHyperlink getHyperlink(){
if(!_r.getRPr().isSetHlinkClick()) return null;
-
-
- return new XSLFHyperlink(_r.getRPr().getHlinkClick(), this);
+ return new XSLFHyperlink(_r.getRPr().getHlinkClick(), _p.getParentShape().getSheet());
}
private boolean fetchCharacterProperty(CharacterPropertyFetcher> fetcher){
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java
index d78749c75..5aa11f1de 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java
@@ -71,10 +71,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
return getTextParagraphs().iterator();
}
- /**
- *
- * @return text contained within this shape or empty string
- */
+ @Override
public String getText() {
StringBuilder out = new StringBuilder();
for (XSLFTextParagraph p : _paragraphs) {
@@ -95,50 +92,76 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
@Override
public XSLFTextRun setText(String text) {
- // copy properties from first paragraph / textrun
+ // calling clearText or setting to a new Array leads to a XmlValueDisconnectedException
+ if (!_paragraphs.isEmpty()) {
+ CTTextBody txBody = getTextBody(false);
+ int cntPs = txBody.sizeOfPArray();
+ for (int i = cntPs; i > 1; i--) {
+ txBody.removeP(i-1);
+ _paragraphs.remove(i-1);
+ }
+
+ _paragraphs.get(0).clearButKeepProperties();
+ }
+
+ return appendText(text, false);
+ }
+
+ @Override
+ public XSLFTextRun appendText(String text, boolean newParagraph) {
+ if (text == null) return null;
+
+ // copy properties from last paragraph / textrun or paragraph end marker
CTTextParagraphProperties pPr = null;
CTTextCharacterProperties rPr = null;
- if (!_paragraphs.isEmpty()) {
- XSLFTextParagraph p0 = _paragraphs.get(0);
- pPr = p0.getXmlObject().getPPr();
- if (!p0.getTextRuns().isEmpty()) {
- XSLFTextRun r0 = p0.getTextRuns().get(0);
+
+ boolean firstPara;
+ XSLFTextParagraph para;
+ if (_paragraphs.isEmpty()) {
+ firstPara = false;
+ para = null;
+ } else {
+ firstPara = !newParagraph;
+ para = _paragraphs.get(_paragraphs.size()-1);
+ CTTextParagraph ctp = para.getXmlObject();
+ pPr = ctp.getPPr();
+ List runs = para.getTextRuns();
+ if (!runs.isEmpty()) {
+ XSLFTextRun r0 = runs.get(runs.size()-1);
rPr = r0.getXmlObject().getRPr();
+ } else if (ctp.isSetEndParaRPr()) {
+ rPr = ctp.getEndParaRPr();
}
}
-
- // can't call clearText otherwise we receive a XmlValueDisconnectedException
- _paragraphs.clear();
- CTTextBody txBody = getTextBody(true);
- int cntPs = txBody.sizeOfPArray();
- // split text by paragraph and new line char
- XSLFTextRun r = null;
- for (String paraText : text.split("\\r\\n?|\\n")) {
- XSLFTextParagraph para = addNewTextParagraph();
- if (pPr != null) {
- para.getXmlObject().setPPr(pPr);
+ XSLFTextRun run = null;
+ for (String lineTxt : text.split("\\r\\n?|\\n")) {
+ if (!firstPara) {
+ if (para != null && para.getXmlObject().isSetEndParaRPr()) {
+ para.getXmlObject().unsetEndParaRPr();
+ }
+ para = addNewTextParagraph();
+ if (pPr != null) {
+ para.getXmlObject().setPPr(pPr);
+ }
}
- boolean first = true;
- for (String runText : paraText.split("[\u000b]")) {
- if (!first) {
+ boolean firstRun = true;
+ for (String runText : lineTxt.split("[\u000b]")) {
+ if (!firstRun) {
para.addLineBreak();
}
- r = para.addNewTextRun();
- r.setText(runText);
+ run = para.addNewTextRun();
+ run.setText(runText);
if (rPr != null) {
- r.getXmlObject().setRPr(rPr);
+ run.getXmlObject().setRPr(rPr);
}
- first = false;
+ firstRun = false;
}
+ firstPara = false;
}
- // simply setting a new pArray leads to XmlValueDisconnectedException
- for (int i = cntPs-1; i >= 0; i--) {
- txBody.removeP(i);
- }
-
- return r;
+ assert(run != null);
+ return run;
}
@Override
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFHyperlink.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFHyperlink.java
index 8a2f3c6cc..53c233199 100644
--- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFHyperlink.java
+++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFHyperlink.java
@@ -19,19 +19,17 @@ package org.apache.poi.xslf.usermodel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import java.awt.geom.Rectangle2D;
import java.io.IOException;
-import java.net.URI;
import java.util.List;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.TargetMode;
+import org.apache.poi.sl.usermodel.Hyperlink;
import org.apache.poi.xslf.XSLFTestDataSamples;
import org.junit.Test;
-/**
- * @author Yegor Kozlov
- */
public class TestXSLFHyperlink {
@Test
@@ -45,19 +43,19 @@ public class TestXSLFHyperlink {
assertEquals("Web Page", cell1.getText());
XSLFHyperlink link1 = cell1.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
assertNotNull(link1);
- assertEquals(URI.create("http://poi.apache.org/"), link1.getTargetURI());
+ assertEquals("http://poi.apache.org/", link1.getAddress());
XSLFTableCell cell2 = tbl.getRows().get(2).getCells().get(0);
assertEquals("Place in this document", cell2.getText());
XSLFHyperlink link2 = cell2.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
assertNotNull(link2);
- assertEquals(URI.create("/ppt/slides/slide2.xml"), link2.getTargetURI());
+ assertEquals("/ppt/slides/slide2.xml", link2.getAddress());
XSLFTableCell cell3 = tbl.getRows().get(3).getCells().get(0);
assertEquals("Email", cell3.getText());
XSLFHyperlink link3 = cell3.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
assertNotNull(link3);
- assertEquals(URI.create("mailto:dev@poi.apache.org?subject=Hi%20There"), link3.getTargetURI());
+ assertEquals("mailto:dev@poi.apache.org?subject=Hi%20There", link3.getAddress());
ppt.close();
}
@@ -75,7 +73,7 @@ public class TestXSLFHyperlink {
r1.setText("Web Page");
XSLFHyperlink link1 = r1.createHyperlink();
link1.setAddress("http://poi.apache.org/");
- assertEquals(URI.create("http://poi.apache.org/"), link1.getTargetURI());
+ assertEquals("http://poi.apache.org/", link1.getAddress());
assertEquals(numRel + 1, slide1.getPackagePart().getRelationships().size());
String id1 = link1.getXmlObject().getId();
@@ -90,8 +88,8 @@ public class TestXSLFHyperlink {
XSLFTextRun r2 = sh2.addNewTextParagraph().addNewTextRun();
r2.setText("Place in this document");
XSLFHyperlink link2 = r2.createHyperlink();
- link2.setAddress(slide2);
- assertEquals(URI.create("/ppt/slides/slide2.xml"), link2.getTargetURI());
+ link2.linkToSlide(slide2);
+ assertEquals("/ppt/slides/slide2.xml", link2.getAddress());
assertEquals(numRel + 2, slide1.getPackagePart().getRelationships().size());
String id2 = link2.getXmlObject().getId();
@@ -104,4 +102,76 @@ public class TestXSLFHyperlink {
ppt.close();
}
+
+
+ @Test
+ public void bug47291() throws IOException {
+ Rectangle2D anchor = new Rectangle2D.Double(100,100,100,100);
+ XMLSlideShow ppt1 = new XMLSlideShow();
+ XSLFSlide slide1 = ppt1.createSlide();
+ XSLFTextBox tb1 = slide1.createTextBox();
+ tb1.setAnchor(anchor);
+ XSLFTextRun r1 = tb1.setText("page1");
+ XSLFHyperlink hl1 = r1.createHyperlink();
+ hl1.linkToEmail("dev@poi.apache.org");
+ XSLFTextBox tb2 = ppt1.createSlide().createTextBox();
+ tb2.setAnchor(anchor);
+ XSLFTextRun r2 = tb2.setText("page2");
+ XSLFHyperlink hl2 = r2.createHyperlink();
+ hl2.linkToLastSlide();
+ XSLFSlide sl3 = ppt1.createSlide();
+ XSLFTextBox tb3 = sl3.createTextBox();
+ tb3.setAnchor(anchor);
+ tb3.setText("text1 ");
+ XSLFTextRun r3 = tb3.appendText("lin\u000bk", false);
+ tb3.appendText(" text2", false);
+ XSLFHyperlink hl3 = r3.createHyperlink();
+ hl3.linkToSlide(slide1);
+ XSLFTextBox tb4 = ppt1.createSlide().createTextBox();
+ tb4.setAnchor(anchor);
+ XSLFTextRun r4 = tb4.setText("page4");
+ XSLFHyperlink hl4 = r4.createHyperlink();
+ hl4.linkToUrl("http://poi.apache.org");
+ XSLFTextBox tb5 = ppt1.createSlide().createTextBox();
+ tb5.setAnchor(anchor);
+ tb5.setText("page5");
+ XSLFHyperlink hl5 = tb5.createHyperlink();
+ hl5.linkToFirstSlide();
+
+ XMLSlideShow ppt2 = XSLFTestDataSamples.writeOutAndReadBack(ppt1);
+ ppt1.close();
+
+ List slides = ppt2.getSlides();
+ tb1 = (XSLFTextBox)slides.get(0).getShapes().get(0);
+ hl1 = tb1.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
+ assertNotNull(hl1);
+ assertEquals("dev@poi.apache.org", hl1.getLabel());
+ assertEquals(Hyperlink.LINK_EMAIL, hl1.getType());
+
+ tb2 = (XSLFTextBox)slides.get(1).getShapes().get(0);
+ hl2 = tb2.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
+ assertNotNull(hl2);
+ assertEquals("lastslide", hl2.getXmlObject().getAction().split("=")[1]);
+ assertEquals(Hyperlink.LINK_DOCUMENT, hl2.getType());
+
+ tb3 = (XSLFTextBox)slides.get(2).getShapes().get(0);
+ hl3 = tb3.getTextParagraphs().get(0).getTextRuns().get(3).getHyperlink();
+ assertNotNull(hl3);
+ assertEquals("/ppt/slides/slide1.xml", hl3.getAddress());
+ assertEquals(Hyperlink.LINK_DOCUMENT, hl3.getType());
+
+ tb4 = (XSLFTextBox)slides.get(3).getShapes().get(0);
+ hl4 = tb4.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
+ assertNotNull(hl4);
+ assertEquals("http://poi.apache.org", hl4.getLabel());
+ assertEquals(Hyperlink.LINK_URL, hl4.getType());
+
+ tb5 = (XSLFTextBox)slides.get(4).getShapes().get(0);
+ hl5 = tb5.getHyperlink();
+ assertNotNull(hl5);
+ assertEquals("firstslide", hl5.getXmlObject().getAction().split("=")[1]);
+ assertEquals(Hyperlink.LINK_DOCUMENT, hl5.getType());
+
+ ppt2.close();
+ }
}
\ No newline at end of file
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java
index c0e5948d7..32780264f 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java
@@ -160,7 +160,7 @@ public final class OLEShape extends HSLFPictureShape {
if(_exEmbed == null){
HSLFSlideShow ppt = getSheet().getSlideShow();
- ExObjList lst = ppt.getDocumentRecord().getExObjList();
+ ExObjList lst = ppt.getDocumentRecord().getExObjList(false);
if(lst == null){
logger.log(POILogger.WARN, "ExObjList not found");
return null;
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/Document.java b/src/scratchpad/src/org/apache/poi/hslf/record/Document.java
index 2cfafa8d2..0c74ae2b4 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/record/Document.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/Document.java
@@ -46,22 +46,33 @@ public final class Document extends PositionDependentRecordContainer
* Returns the DocumentAtom of this Document
*/
public DocumentAtom getDocumentAtom() { return documentAtom; }
+
/**
* Returns the Environment of this Notes, which lots of
- * settings for the document in it
+ * settings for the document in it
*/
public Environment getEnvironment() { return environment; }
+
/**
* Returns the PPDrawingGroup, which holds an Escher Structure
- * that contains information on pictures in the slides.
+ * that contains information on pictures in the slides.
*/
public PPDrawingGroup getPPDrawingGroup() { return ppDrawing; }
+
/**
* Returns the ExObjList, which holds the references to
- * external objects used in the slides. This may be null, if
- * there are no external references.
+ * external objects used in the slides. This may be null, if
+ * there are no external references.
+ *
+ * @param create if true, create an ExObjList if it doesn't exist
*/
- public ExObjList getExObjList() { return exObjList; }
+ public ExObjList getExObjList(boolean create) {
+ if (exObjList == null && create) {
+ exObjList = new ExObjList();
+ addChildAfter(exObjList, getDocumentAtom());
+ }
+ return exObjList;
+ }
/**
* Returns all the SlideListWithTexts that are defined for
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java
index 92245598f..ca9d68d78 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java
@@ -67,9 +67,9 @@ public class ExHyperlink extends RecordContainer {
linkDetailsB.setText(url);
}
}
- public void setLinkURL(String url, int options) {
+
+ public void setLinkOptions(int options) {
if(linkDetailsB != null) {
- linkDetailsB.setText(url);
linkDetailsB.setOptions(options);
}
}
@@ -79,7 +79,7 @@ public class ExHyperlink extends RecordContainer {
linkDetailsA.setText(title);
}
}
-
+
/**
* Get the link details (field A)
*/
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFHyperlink.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFHyperlink.java
index f896103b0..b7b697ac5 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFHyperlink.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFHyperlink.java
@@ -23,6 +23,7 @@ import java.util.List;
import java.util.ListIterator;
import org.apache.poi.hslf.record.ExHyperlink;
+import org.apache.poi.hslf.record.ExHyperlinkAtom;
import org.apache.poi.hslf.record.ExObjList;
import org.apache.poi.hslf.record.HSLFEscherClientDataRecord;
import org.apache.poi.hslf.record.InteractiveInfo;
@@ -30,24 +31,95 @@ import org.apache.poi.hslf.record.InteractiveInfoAtom;
import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.TxInteractiveInfoAtom;
import org.apache.poi.sl.usermodel.Hyperlink;
+import org.apache.poi.sl.usermodel.Slide;
/**
* Represents a hyperlink in a PowerPoint document
*/
-public final class HSLFHyperlink implements Hyperlink {
- public static final byte LINK_NEXTSLIDE = InteractiveInfoAtom.LINK_NextSlide;
- public static final byte LINK_PREVIOUSSLIDE = InteractiveInfoAtom.LINK_PreviousSlide;
- public static final byte LINK_FIRSTSLIDE = InteractiveInfoAtom.LINK_FirstSlide;
- public static final byte LINK_LASTSLIDE = InteractiveInfoAtom.LINK_LastSlide;
- public static final byte LINK_SLIDENUMBER = InteractiveInfoAtom.LINK_SlideNumber;
- public static final byte LINK_URL = InteractiveInfoAtom.LINK_Url;
- public static final byte LINK_NULL = InteractiveInfoAtom.LINK_NULL;
+public final class HSLFHyperlink implements Hyperlink {
+ private final ExHyperlink exHyper;
+ private final InteractiveInfo info;
+ private TxInteractiveInfoAtom txinfo;
- private int id=-1;
- private int type;
- private String address;
- private String label;
- private int startIndex, endIndex;
+ protected HSLFHyperlink(ExHyperlink exHyper, InteractiveInfo info) {
+ this.info = info;
+ this.exHyper = exHyper;
+ }
+
+ public ExHyperlink getExHyperlink() {
+ return exHyper;
+ }
+
+ public InteractiveInfo getInfo() {
+ return info;
+ }
+
+ public TxInteractiveInfoAtom getTextRunInfo() {
+ return txinfo;
+ }
+
+ protected void setTextRunInfo(TxInteractiveInfoAtom txinfo) {
+ this.txinfo = txinfo;
+ }
+
+ /**
+ * Creates a new Hyperlink and assign it to a shape
+ * This is only a helper method - use {@link HSLFSimpleShape#createHyperlink()} instead!
+ *
+ * @param shape the shape which receives the hyperlink
+ * @return the new hyperlink
+ *
+ * @see HSLFShape#createHyperlink()
+ */
+ /* package */ static HSLFHyperlink createHyperlink(HSLFSimpleShape shape) {
+ // TODO: check if a hyperlink already exists
+ ExHyperlink exHyper = new ExHyperlink();
+ int linkId = shape.getSheet().getSlideShow().addToObjListAtom(exHyper);
+ ExHyperlinkAtom obj = exHyper.getExHyperlinkAtom();
+ obj.setNumber(linkId);
+ InteractiveInfo info = new InteractiveInfo();
+ info.getInteractiveInfoAtom().setHyperlinkID(linkId);
+ HSLFEscherClientDataRecord cldata = shape.getClientData(true);
+ cldata.addChild(info);
+ HSLFHyperlink hyper = new HSLFHyperlink(exHyper, info);
+ hyper.linkToNextSlide();
+ shape.setHyperlink(hyper);
+ return hyper;
+ }
+
+ /**
+ * Creates a new Hyperlink for a textrun.
+ * This is only a helper method - use {@link HSLFTextRun#createHyperlink()} instead!
+ *
+ * @param run the run which receives the hyperlink
+ * @return the new hyperlink
+ *
+ * @see HSLFTextRun#createHyperlink()
+ */
+ /* package */ static HSLFHyperlink createHyperlink(HSLFTextRun run) {
+ // TODO: check if a hyperlink already exists
+ ExHyperlink exHyper = new ExHyperlink();
+ int linkId = run.getTextParagraph().getSheet().getSlideShow().addToObjListAtom(exHyper);
+ ExHyperlinkAtom obj = exHyper.getExHyperlinkAtom();
+ obj.setNumber(linkId);
+ InteractiveInfo info = new InteractiveInfo();
+ info.getInteractiveInfoAtom().setHyperlinkID(linkId);
+ // don't add the hyperlink now to text paragraph records
+ // this will be done, when the paragraph is saved
+ HSLFHyperlink hyper = new HSLFHyperlink(exHyper, info);
+ hyper.linkToNextSlide();
+
+ TxInteractiveInfoAtom txinfo = new TxInteractiveInfoAtom();
+ int startIdx = run.getTextParagraph().getStartIdxOfTextRun(run);
+ int endIdx = startIdx + run.getLength();
+ txinfo.setStartIndex(startIdx);
+ txinfo.setEndIndex(endIdx);
+ hyper.setTextRunInfo(txinfo);
+
+ run.setHyperlink(hyper);
+ return hyper;
+ }
+
/**
* Gets the type of the hyperlink action.
@@ -58,70 +130,130 @@ public final class HSLFHyperlink implements Hyperlink {
*/
@Override
public int getType() {
- return type;
- }
-
- public void setType(int val) {
- type = val;
- switch(type){
- case LINK_NEXTSLIDE:
- label = "NEXT";
- address = "1,-1,NEXT";
- break;
- case LINK_PREVIOUSSLIDE:
- label = "PREV";
- address = "1,-1,PREV";
- break;
- case LINK_FIRSTSLIDE:
- label = "FIRST";
- address = "1,-1,FIRST";
- break;
- case LINK_LASTSLIDE:
- label = "LAST";
- address = "1,-1,LAST";
- break;
- case LINK_SLIDENUMBER:
- break;
- default:
- label = "";
- address = "";
- break;
+ switch (info.getInteractiveInfoAtom().getHyperlinkType()) {
+ case InteractiveInfoAtom.LINK_Url:
+ return (exHyper.getLinkURL().startsWith("mailto:")) ? LINK_EMAIL : LINK_URL;
+ case InteractiveInfoAtom.LINK_NextSlide:
+ case InteractiveInfoAtom.LINK_PreviousSlide:
+ case InteractiveInfoAtom.LINK_FirstSlide:
+ case InteractiveInfoAtom.LINK_LastSlide:
+ case InteractiveInfoAtom.LINK_SlideNumber:
+ return LINK_DOCUMENT;
+ case InteractiveInfoAtom.LINK_CustomShow:
+ case InteractiveInfoAtom.LINK_OtherPresentation:
+ case InteractiveInfoAtom.LINK_OtherFile:
+ return LINK_FILE;
+ default:
+ case InteractiveInfoAtom.LINK_NULL:
+ return -1;
}
}
@Override
- public String getAddress() {
- return address;
+ public void linkToEmail(String emailAddress) {
+ InteractiveInfoAtom iia = info.getInteractiveInfoAtom();
+ iia.setAction(InteractiveInfoAtom.ACTION_HYPERLINK);
+ iia.setJump(InteractiveInfoAtom.JUMP_NONE);
+ iia.setHyperlinkType(InteractiveInfoAtom.LINK_Url);
+ exHyper.setLinkURL("mailto:"+emailAddress);
+ exHyper.setLinkTitle(emailAddress);
+ exHyper.setLinkOptions(0x10);
}
- public void setAddress(HSLFSlide slide) {
- String href = slide._getSheetNumber() + ","+slide.getSlideNumber()+",Slide " + slide.getSlideNumber();
- setAddress(href);;
- setLabel("Slide " + slide.getSlideNumber());
- setType(HSLFHyperlink.LINK_SLIDENUMBER);
+ @Override
+ public void linkToUrl(String url) {
+ InteractiveInfoAtom iia = info.getInteractiveInfoAtom();
+ iia.setAction(InteractiveInfoAtom.ACTION_HYPERLINK);
+ iia.setJump(InteractiveInfoAtom.JUMP_NONE);
+ iia.setHyperlinkType(InteractiveInfoAtom.LINK_Url);
+ exHyper.setLinkURL(url);
+ exHyper.setLinkTitle(url);
+ exHyper.setLinkOptions(0x10);
+ }
+
+ @Override
+ public void linkToSlide(Slide slide) {
+ assert(slide instanceof HSLFSlide);
+ HSLFSlide sl = (HSLFSlide)slide;
+ int slideNum = slide.getSlideNumber();
+ String alias = "Slide "+slideNum;
+
+ InteractiveInfoAtom iia = info.getInteractiveInfoAtom();
+ iia.setAction(InteractiveInfoAtom.ACTION_HYPERLINK);
+ iia.setJump(InteractiveInfoAtom.JUMP_NONE);
+ iia.setHyperlinkType(InteractiveInfoAtom.LINK_SlideNumber);
+
+ linkToDocument(sl._getSheetNumber(),slideNum,alias,0x30);
+ }
+
+ @Override
+ public void linkToNextSlide() {
+ InteractiveInfoAtom iia = info.getInteractiveInfoAtom();
+ iia.setAction(InteractiveInfoAtom.ACTION_JUMP);
+ iia.setJump(InteractiveInfoAtom.JUMP_NEXTSLIDE);
+ iia.setHyperlinkType(InteractiveInfoAtom.LINK_NextSlide);
+
+ linkToDocument(1,-1,"NEXT",0x10);
+ }
+
+ @Override
+ public void linkToPreviousSlide() {
+ InteractiveInfoAtom iia = info.getInteractiveInfoAtom();
+ iia.setAction(InteractiveInfoAtom.ACTION_JUMP);
+ iia.setJump(InteractiveInfoAtom.JUMP_PREVIOUSSLIDE);
+ iia.setHyperlinkType(InteractiveInfoAtom.LINK_PreviousSlide);
+
+ linkToDocument(1,-1,"PREV",0x10);
+ }
+
+ @Override
+ public void linkToFirstSlide() {
+ InteractiveInfoAtom iia = info.getInteractiveInfoAtom();
+ iia.setAction(InteractiveInfoAtom.ACTION_JUMP);
+ iia.setJump(InteractiveInfoAtom.JUMP_FIRSTSLIDE);
+ iia.setHyperlinkType(InteractiveInfoAtom.LINK_FirstSlide);
+
+ linkToDocument(1,-1,"FIRST",0x10);
+ }
+
+ @Override
+ public void linkToLastSlide() {
+ InteractiveInfoAtom iia = info.getInteractiveInfoAtom();
+ iia.setAction(InteractiveInfoAtom.ACTION_JUMP);
+ iia.setJump(InteractiveInfoAtom.JUMP_LASTSLIDE);
+ iia.setHyperlinkType(InteractiveInfoAtom.LINK_LastSlide);
+
+ linkToDocument(1,-1,"LAST",0x10);
+ }
+
+ private void linkToDocument(int sheetNumber, int slideNumber, String alias, int options) {
+ exHyper.setLinkURL(sheetNumber+","+slideNumber+","+alias);
+ exHyper.setLinkTitle(alias);
+ exHyper.setLinkOptions(options);
+ }
+
+ @Override
+ public String getAddress() {
+ return exHyper.getLinkURL();
}
@Override
public void setAddress(String str) {
- address = str;
+ exHyper.setLinkURL(str);
}
public int getId() {
- return id;
- }
-
- public void setId(int id) {
- this.id = id;
+ return exHyper.getExHyperlinkAtom().getNumber();
}
@Override
public String getLabel() {
- return label;
+ return exHyper.getLinkTitle();
}
@Override
- public void setLabel(String str) {
- label = str;
+ public void setLabel(String label) {
+ exHyper.setLinkTitle(label);
}
/**
@@ -130,7 +262,7 @@ public final class HSLFHyperlink implements Hyperlink {
* @return the beginning character position
*/
public int getStartIndex() {
- return startIndex;
+ return (txinfo == null) ? -1 : txinfo.getStartIndex();
}
/**
@@ -139,16 +271,18 @@ public final class HSLFHyperlink implements Hyperlink {
* @param startIndex the beginning character position
*/
public void setStartIndex(int startIndex) {
- this.startIndex = startIndex;
+ if (txinfo != null) {
+ txinfo.setStartIndex(startIndex);
+ }
}
-
+
/**
* Gets the ending character position
*
* @return the ending character position
*/
public int getEndIndex() {
- return endIndex;
+ return (txinfo == null) ? -1 : txinfo.getEndIndex();
}
/**
@@ -157,9 +291,11 @@ public final class HSLFHyperlink implements Hyperlink {
* @param endIndex the ending character position
*/
public void setEndIndex(int endIndex) {
- this.endIndex = endIndex;
+ if (txinfo != null) {
+ txinfo.setEndIndex(endIndex);
+ }
}
-
+
/**
* Find hyperlinks in a text shape
*
@@ -177,15 +313,15 @@ public final class HSLFHyperlink implements Hyperlink {
* @return found hyperlinks
*/
@SuppressWarnings("resource")
- public static List find(List paragraphs){
+ protected static List find(List paragraphs){
List lst = new ArrayList();
if (paragraphs == null || paragraphs.isEmpty()) return lst;
HSLFTextParagraph firstPara = paragraphs.get(0);
-
+
HSLFSlideShow ppt = firstPara.getSheet().getSlideShow();
//document-level container which stores info about all links in a presentation
- ExObjList exobj = ppt.getDocumentRecord().getExObjList();
+ ExObjList exobj = ppt.getDocumentRecord().getExObjList(false);
if (exobj != null) {
Record[] records = firstPara.getRecords();
find(Arrays.asList(records), exobj, lst);
@@ -201,10 +337,10 @@ public final class HSLFHyperlink implements Hyperlink {
* @return found hyperlink or null
*/
@SuppressWarnings("resource")
- public static HSLFHyperlink find(HSLFShape shape){
+ protected static HSLFHyperlink find(HSLFShape shape){
HSLFSlideShow ppt = shape.getSheet().getSlideShow();
//document-level container which stores info about all links in a presentation
- ExObjList exobj = ppt.getDocumentRecord().getExObjList();
+ ExObjList exobj = ppt.getDocumentRecord().getExObjList(false);
HSLFEscherClientDataRecord cldata = shape.getClientData(false);
if (exobj != null && cldata != null) {
@@ -228,16 +364,12 @@ public final class HSLFHyperlink implements Hyperlink {
InteractiveInfo hldr = (InteractiveInfo)r;
InteractiveInfoAtom info = hldr.getInteractiveInfoAtom();
int id = info.getHyperlinkID();
- ExHyperlink linkRecord = exobj.get(id);
- if (linkRecord == null) {
+ ExHyperlink exHyper = exobj.get(id);
+ if (exHyper == null) {
continue;
}
-
- HSLFHyperlink link = new HSLFHyperlink();
- link.setId(id);
- link.setType(info.getAction());
- link.setLabel(linkRecord.getLinkTitle());
- link.setAddress(linkRecord.getLinkURL());
+
+ HSLFHyperlink link = new HSLFHyperlink(exHyper, hldr);
out.add(link);
if (iter.hasNext()) {
@@ -246,9 +378,7 @@ public final class HSLFHyperlink implements Hyperlink {
iter.previous();
continue;
}
- TxInteractiveInfoAtom txinfo = (TxInteractiveInfoAtom)r;
- link.setStartIndex(txinfo.getStartIndex());
- link.setEndIndex(txinfo.getEndIndex());
+ link.setTextRunInfo((TxInteractiveInfoAtom)r);
}
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java
index e4ef3b1b7..0210a1d8a 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java
@@ -28,7 +28,6 @@ import org.apache.poi.ddf.EscherChildAnchorRecord;
import org.apache.poi.ddf.EscherClientAnchorRecord;
import org.apache.poi.ddf.EscherColorRef;
import org.apache.poi.ddf.EscherContainerRecord;
-import org.apache.poi.ddf.EscherOptRecord;
import org.apache.poi.ddf.EscherProperties;
import org.apache.poi.ddf.EscherProperty;
import org.apache.poi.ddf.EscherRecord;
@@ -60,8 +59,6 @@ import org.apache.poi.util.Units;
* in points (72 points = 1 inch).
*
*
- *
- * @author Yegor Kozlov
*/
public abstract class HSLFShape implements Shape {
@@ -89,7 +86,7 @@ public abstract class HSLFShape implements Shape {
* Fill
*/
protected HSLFFill _fill;
-
+
/**
* Create a Shape object. This constructor is used when an existing Shape is read from from a PowerPoint document.
*
@@ -445,16 +442,6 @@ public abstract class HSLFShape implements Shape {
return getFill().getFillStyle();
}
- /**
- * Returns the hyperlink assigned to this shape
- *
- * @return the hyperlink assigned to this shape
- * or null
if not found.
- */
- public HSLFHyperlink getHyperlink(){
- return HSLFHyperlink.find(this);
- }
-
public void draw(Graphics2D graphics){
logger.log(POILogger.INFO, "Rendering " + getShapeName());
}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java
index ee6155773..72042769d 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java
@@ -134,6 +134,7 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheet ltp : trs) {
HSLFTextParagraph.supplySheet(ltp, this);
+ HSLFTextParagraph.applyHyperlinks(ltp);
}
}
@@ -171,6 +172,14 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheet parentList;
/**
@@ -162,14 +163,14 @@ public final class HSLFTextParagraph implements TextParagraph paragraphs) {
fixLineEndings(paragraphs);
+ updateTextAtom(paragraphs);
+ updateStyles(paragraphs);
+ updateHyperlinks(paragraphs);
+ refreshRecords(paragraphs);
- String rawText = toInternalString(getRawText(paragraphs));
+ for (HSLFTextParagraph p : paragraphs) {
+ p._dirty = false;
+ }
+ }
+
+ /**
+ * Set the correct text atom depending on the multibyte usage
+ */
+ private static void updateTextAtom(List paragraphs) {
+ final String rawText = toInternalString(getRawText(paragraphs));
// Will it fit in a 8 bit atom?
boolean isUnicode = StringUtil.hasMultibyte(rawText);
@@ -888,6 +901,16 @@ public final class HSLFTextParagraph implements TextParagraph paragraphs) {
+ final String rawText = toInternalString(getRawText(paragraphs));
+ TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;
+ StyleTextPropAtom styleAtom = findStyleAtomPresent(headerAtom, rawText.length());
+
// Update the text length for its Paragraph and Character stylings
// * reset the length, to the new string's length
// * add on +1 if the last block
@@ -933,7 +956,54 @@ public final class HSLFTextParagraph implements TextParagraph paragraphs) {
+ TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;
+ RecordContainer _txtbox = headerAtom.getParentRecord();
+ // remove existing hyperlink records
+ for (Record r : _txtbox.getChildRecords()) {
+ if (r instanceof InteractiveInfo || r instanceof TxInteractiveInfoAtom) {
+ _txtbox.removeChild(r);
+ }
+ }
+ // now go through all the textruns and check for hyperlinks
+ HSLFHyperlink lastLink = null;
+ for (HSLFTextParagraph para : paragraphs) {
+ for (HSLFTextRun run : para) {
+ HSLFHyperlink thisLink = run.getHyperlink();
+ if (thisLink != null && thisLink == lastLink) {
+ // the hyperlink extends over this text run, increase its length
+ // TODO: the text run might be longer than the hyperlink
+ thisLink.setEndIndex(thisLink.getEndIndex()+run.getLength());
+ } else {
+ if (lastLink != null) {
+ InteractiveInfo info = lastLink.getInfo();
+ TxInteractiveInfoAtom txinfo = lastLink.getTextRunInfo();
+ assert(info != null && txinfo != null);
+ _txtbox.appendChildRecord(info);
+ _txtbox.appendChildRecord(txinfo);
+ }
+ }
+ lastLink = thisLink;
+ }
+ }
+
+ if (lastLink != null) {
+ InteractiveInfo info = lastLink.getInfo();
+ TxInteractiveInfoAtom txinfo = lastLink.getTextRunInfo();
+ assert(info != null && txinfo != null);
+ _txtbox.appendChildRecord(info);
+ _txtbox.appendChildRecord(txinfo);
+ }
+ }
+
+ /**
+ * Writes the textbox records back to the document record
+ */
+ private static void refreshRecords(List paragraphs) {
+ TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;
+ RecordContainer _txtbox = headerAtom.getParentRecord();
if (_txtbox instanceof EscherTextboxWrapper) {
try {
((EscherTextboxWrapper) _txtbox).writeOut(null);
@@ -941,10 +1011,6 @@ public final class HSLFTextParagraph implements TextParagraph paragraphs) {
List links = HSLFHyperlink.find(paragraphs);
-
+
for (HSLFHyperlink h : links) {
int csIdx = 0;
for (HSLFTextParagraph p : paragraphs) {
- for (HSLFTextRun r : p) {
- if (h.getStartIndex() <= csIdx && csIdx < h.getEndIndex()) {
- r.setHyperlinkId(h.getId());
+ if (csIdx > h.getEndIndex()) break;
+ List runs = p.getTextRuns();
+ for (int rlen=0,rIdx=0; rIdx < runs.size(); csIdx+=rlen, rIdx++) {
+ HSLFTextRun run = runs.get(rIdx);
+ rlen = run.getLength();
+ if (csIdx < h.getEndIndex() && h.getStartIndex() < csIdx+rlen) {
+ String rawText = run.getRawText();
+ int startIdx = h.getStartIndex()-csIdx;
+ if (startIdx > 0) {
+ // hyperlink starts within current textrun
+ HSLFTextRun newRun = new HSLFTextRun(p);
+ newRun.setCharacterStyle(run.getCharacterStyle());
+ newRun.setText(rawText.substring(startIdx));
+ run.setText(rawText.substring(0, startIdx));
+ runs.add(rIdx+1, newRun);
+ rlen = startIdx;
+ continue;
+ }
+ int endIdx = Math.min(rlen, h.getEndIndex()-h.getStartIndex());
+ if (endIdx < rlen) {
+ // hyperlink ends before end of current textrun
+ HSLFTextRun newRun = new HSLFTextRun(p);
+ newRun.setCharacterStyle(run.getCharacterStyle());
+ newRun.setText(rawText.substring(0, endIdx));
+ run.setText(rawText.substring(endIdx));
+ runs.add(rIdx, newRun);
+ rlen = endIdx;
+ run = newRun;
+ }
+ run.setHyperlink(h);
}
- csIdx += r.getLength();
}
}
}
}
-
+
protected static void applyCharacterStyles(List paragraphs, List charStyles) {
int paraIdx = 0, runIdx = 0;
HSLFTextRun trun;
@@ -1444,7 +1536,7 @@ public final class HSLFTextParagraph implements TextParagraph {
return getClientDataRecord(RoundTripHFPlaceholder12.typeID);
}
- /**
- *
- * Assigns a hyperlink to this text shape
- *
- * @param linkId id of the hyperlink, @see org.apache.poi.hslf.usermodel.SlideShow#addHyperlink(Hyperlink)
- * @param beginIndex the beginning index, inclusive.
- * @param endIndex the ending index, exclusive.
- * @see org.apache.poi.hslf.usermodel.HSLFSlideShow#addHyperlink(HSLFHyperlink)
- */
- public void setHyperlink(int linkId, int beginIndex, int endIndex){
- //TODO validate beginIndex and endIndex and throw IllegalArgumentException
-
- InteractiveInfo info = new InteractiveInfo();
- InteractiveInfoAtom infoAtom = info.getInteractiveInfoAtom();
- infoAtom.setAction(org.apache.poi.hslf.record.InteractiveInfoAtom.ACTION_HYPERLINK);
- infoAtom.setHyperlinkType(org.apache.poi.hslf.record.InteractiveInfoAtom.LINK_Url);
- infoAtom.setHyperlinkID(linkId);
-
- _txtbox.appendChildRecord(info);
-
- TxInteractiveInfoAtom txiatom = new TxInteractiveInfoAtom();
- txiatom.setStartIndex(beginIndex);
- txiatom.setEndIndex(endIndex);
- _txtbox.appendChildRecord(txiatom);
-
- }
-
@Override
public boolean isPlaceholder() {
OEPlaceholderAtom oep = getPlaceholderAtom();
@@ -729,50 +699,38 @@ implements TextShape {
return HSLFTextParagraph.getRawText(getTextParagraphs());
}
- /**
- * Returns the text contained in this text frame, which has been made safe
- * for printing and other use.
- *
- * @return the text string for this textbox.
- */
+ @Override
public String getText() {
String rawText = getRawText();
return HSLFTextParagraph.toExternalString(rawText, getRunType());
}
+ @Override
+ public HSLFTextRun appendText(String text, boolean newParagraph) {
+ // init paragraphs
+ List paras = getTextParagraphs();
+ HSLFTextRun htr = HSLFTextParagraph.appendText(paras, text, newParagraph);
+ setTextId(getRawText().hashCode());
+ return htr;
+ }
- // Update methods follow
+ @Override
+ public HSLFTextRun setText(String text) {
+ // init paragraphs
+ List paras = getTextParagraphs();
+ HSLFTextRun htr = HSLFTextParagraph.setText(paras, text);
+ setTextId(getRawText().hashCode());
+ return htr;
+ }
- /**
- * Adds the supplied text onto the end of the TextParagraphs,
- * creating a new RichTextRun for it to sit in.
- *
- * @param text the text string used by this object.
- */
- public HSLFTextRun appendText(String text, boolean newParagraph) {
- // init paragraphs
- List paras = getTextParagraphs();
- return HSLFTextParagraph.appendText(paras, text, newParagraph);
- }
-
- @Override
- public HSLFTextRun setText(String text) {
- // init paragraphs
- List paras = getTextParagraphs();
- HSLFTextRun htr = HSLFTextParagraph.setText(paras, text);
- setTextId(text.hashCode());
- return htr;
- }
-
- /**
- * Saves the modified paragraphs/textrun to the records.
- * Also updates the styles to the correct text length.
- */
- protected void storeText() {
- List paras = getTextParagraphs();
- HSLFTextParagraph.storeText(paras);
- }
- // Accesser methods follow
+ /**
+ * Saves the modified paragraphs/textrun to the records.
+ * Also updates the styles to the correct text length.
+ */
+ protected void storeText() {
+ List paras = getTextParagraphs();
+ HSLFTextParagraph.storeText(paras);
+ }
/**
* Returns the array of all hyperlinks in this text run
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestHyperlink.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestHyperlink.java
index 715640a1d..7810929d7 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestHyperlink.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestHyperlink.java
@@ -22,16 +22,25 @@ import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.toExternalString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import org.apache.poi.POIDataSamples;
-import org.apache.poi.hslf.usermodel.*;
+import org.apache.poi.hslf.HSLFTestDataSamples;
+import org.apache.poi.hslf.record.InteractiveInfoAtom;
+import org.apache.poi.hslf.usermodel.HSLFHyperlink;
+import org.apache.poi.hslf.usermodel.HSLFSlide;
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.hslf.usermodel.HSLFTextBox;
+import org.apache.poi.hslf.usermodel.HSLFTextParagraph;
+import org.apache.poi.hslf.usermodel.HSLFTextRun;
+import org.apache.poi.sl.usermodel.Hyperlink;
import org.junit.Test;
/**
* Test Hyperlink.
- *
- * @author Yegor Kozlov
*/
public final class TestHyperlink {
private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance();
@@ -42,7 +51,7 @@ public final class TestHyperlink {
HSLFSlide slide = ppt.getSlides().get(0);
List para = slide.getTextParagraphs().get(1);
-
+
String rawText = toExternalString(getRawText(para), para.get(0).getRunType());
String expected =
"This page has two links:\n"+
@@ -52,9 +61,8 @@ public final class TestHyperlink {
"\n"+
"In addition, its notes has one link";
assertEquals(expected, rawText);
-
- List links = HSLFHyperlink.find(para);
- assertNotNull(links);
+
+ List links = findHyperlinks(para);
assertEquals(2, links.size());
assertEquals("http://jakarta.apache.org/poi/", links.get(0).getLabel());
@@ -68,17 +76,97 @@ public final class TestHyperlink {
slide = ppt.getSlides().get(1);
para = slide.getTextParagraphs().get(1);
rawText = toExternalString(getRawText(para), para.get(0).getRunType());
- expected =
+ expected =
"I have the one link:\n" +
"Jakarta HSSF";
assertEquals(expected, rawText);
- links = HSLFHyperlink.find(para);
+ links.clear();
+
+ links = findHyperlinks(para);
assertNotNull(links);
assertEquals(1, links.size());
assertEquals("Open Jakarta POI HSSF module test ", links.get(0).getLabel());
assertEquals("http://jakarta.apache.org/poi/hssf/", links.get(0).getAddress());
assertEquals("Jakarta HSSF", rawText.substring(links.get(0).getStartIndex(), links.get(0).getEndIndex()-1));
+
+ ppt.close();
+ }
+
+ @Test
+ public void bug47291() throws IOException {
+ HSLFSlideShow ppt1 = new HSLFSlideShow();
+ HSLFSlide slide1 = ppt1.createSlide();
+ HSLFTextRun r1 = slide1.createTextBox().setText("page1");
+ HSLFHyperlink hl1 = r1.createHyperlink();
+ hl1.linkToEmail("dev@poi.apache.org");
+ HSLFTextRun r2 = ppt1.createSlide().createTextBox().setText("page2");
+ HSLFHyperlink hl2 = r2.createHyperlink();
+ hl2.linkToLastSlide();
+ HSLFSlide sl1 = ppt1.createSlide();
+ HSLFTextBox tb1 = sl1.createTextBox();
+ tb1.setAnchor(new Rectangle2D.Double(100,100,100,100));
+ tb1.appendText("text1 ", false);
+ HSLFTextRun r3 = tb1.appendText("lin\u000bk", false);
+ tb1.appendText(" text2", false);
+ HSLFHyperlink hl3 = r3.createHyperlink();
+ hl3.linkToSlide(slide1);
+ HSLFTextRun r4 = ppt1.createSlide().createTextBox().setText("page4");
+ HSLFHyperlink hl4 = r4.createHyperlink();
+ hl4.linkToUrl("http://poi.apache.org");
+ HSLFTextBox tb5 = ppt1.createSlide().createTextBox();
+ tb5.setText("page5");
+ HSLFHyperlink hl5 = tb5.createHyperlink();
+ hl5.linkToFirstSlide();
+
+ HSLFSlideShow ppt2 = HSLFTestDataSamples.writeOutAndReadBack(ppt1);
+ ppt1.close();
+
+ List slides = ppt2.getSlides();
+ tb1 = (HSLFTextBox)slides.get(0).getShapes().get(0);
+ hl1 = tb1.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
+ assertNotNull(hl1);
+ assertEquals("dev@poi.apache.org", hl1.getLabel());
+ assertEquals(Hyperlink.LINK_EMAIL, hl1.getType());
+
+ HSLFTextBox tb2 = (HSLFTextBox)slides.get(1).getShapes().get(0);
+ hl2 = tb2.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
+ assertNotNull(hl2);
+ assertEquals(InteractiveInfoAtom.LINK_LastSlide, hl2.getInfo().getInteractiveInfoAtom().getHyperlinkType());
+ assertEquals(Hyperlink.LINK_DOCUMENT, hl2.getType());
+
+ HSLFTextBox tb3 = (HSLFTextBox)slides.get(2).getShapes().get(0);
+ hl3 = tb3.getTextParagraphs().get(0).getTextRuns().get(1).getHyperlink();
+ assertNotNull(hl3);
+ assertEquals(ppt2.getSlides().get(0)._getSheetNumber(), Integer.parseInt(hl3.getAddress().split(",")[0]));
+ assertEquals(Hyperlink.LINK_DOCUMENT, hl3.getType());
+
+ HSLFTextBox tb4 = (HSLFTextBox)slides.get(3).getShapes().get(0);
+ hl4 = tb4.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
+ assertNotNull(hl4);
+ assertEquals("http://poi.apache.org", hl4.getLabel());
+ assertEquals(Hyperlink.LINK_URL, hl4.getType());
+
+ tb5 = (HSLFTextBox)slides.get(4).getShapes().get(0);
+ hl5 = tb5.getHyperlink();
+ assertNotNull(hl5);
+ assertEquals(InteractiveInfoAtom.LINK_FirstSlide, hl5.getInfo().getInteractiveInfoAtom().getHyperlinkType());
+ assertEquals(Hyperlink.LINK_DOCUMENT, hl5.getType());
+
+ ppt2.close();
+ }
+
+ private static List findHyperlinks(List paras) {
+ List links = new ArrayList();
+ for (HSLFTextParagraph p : paras) {
+ for (HSLFTextRun r : p) {
+ HSLFHyperlink hl = r.getHyperlink();
+ if (hl != null) {
+ links.add(hl);
+ }
+ }
+ }
+ return links;
}
}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExObjList.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExObjList.java
index bf596faed..88f6884df 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExObjList.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExObjList.java
@@ -38,7 +38,7 @@ public class TestExObjList extends TestCase {
// Get the document
Document doc = ss.getDocumentRecord();
// Get the ExObjList
- ExObjList exObjList = doc.getExObjList();
+ ExObjList exObjList = doc.getExObjList(false);
assertNotNull(exObjList);
assertEquals(1033l, exObjList.getRecordType());