diff options
author | Andreas Beeker <kiwiwings@apache.org> | 2016-01-24 00:12:10 +0000 |
---|---|---|
committer | Andreas Beeker <kiwiwings@apache.org> | 2016-01-24 00:12:10 +0000 |
commit | b585430cabe8e5cfd2130b8667423e999525d4a3 (patch) | |
tree | 15caaa204e242dd1bf017537bed7692ac2f26497 | |
parent | 52140e65a9dff2d2dcc0f9775c309aea9a1d2961 (diff) | |
download | poi-b585430cabe8e5cfd2130b8667423e999525d4a3.tar.gz poi-b585430cabe8e5cfd2130b8667423e999525d4a3.zip |
#41047 - Support hyperlinks in HSLF shapes and textruns
#47291 - Cannot open link correctly which insert in ppt
HSLF hyperlink code was all over the place - moved most of it into HSLFHyperlink
extended common sl for hyperlinks
extended XSLF shape linking and added XSLFTextShape.appendText to go along with HSLF
adapted/fixed documentation
added convenience methods to the hyperlink classes to address the different targets
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1726458 13f79535-47bb-0310-9956-ffa450edef68
27 files changed, 946 insertions, 455 deletions
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 44e5d81c1c..fc25afae55 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 8cbbcdc852..812429295a 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<HSLFTextParagraph> txtParas : slide.getTextParagraphs()) { - List<HSLFHyperlink> links = HSLFHyperlink.find(txtParas); - String text = HSLFTextParagraph.getRawText(txtParas); - - for (HSLFHyperlink link : links) { - System.out.println(toStr(link, text)); + for (List<HSLFTextParagraph> 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 e3fdd85df2..73a59d0b81 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 9721660580..beadc1f988 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 cde288492c..50500d04bc 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<S,P>, + P extends TextParagraph<S,P,?> +> 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<S,P> 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 53ee6de529..aee69fb735 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 <code>null</code> if not found. + * + * @since POI 3.14-Beta1 + */ + Hyperlink<S,P> 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<S,P> createHyperlink(); } diff --git a/src/java/org/apache/poi/sl/usermodel/TextRun.java b/src/java/org/apache/poi/sl/usermodel/TextRun.java index 28db797958..014d3036b3 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(); + + + /** + * 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 getHyperlink(); + 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 df302ff62e..cc39a51819 100644 --- a/src/java/org/apache/poi/sl/usermodel/TextShape.java +++ b/src/java/org/apache/poi/sl/usermodel/TextShape.java @@ -119,6 +119,16 @@ public interface TextShape< }
/**
+ * 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.
* Text paragraphs are split by \\r or \\n.
@@ -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 0b093ecacb..6f4a5228d0 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<XSLFShape,XSLFTextParagraph> {
+ 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<XSLFShape,XSLFTextParagraph> 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 8b92ec2316..636647ef2e 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 5abb098375..298bcc7817 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 TextParagraph<XSLFShape,XSLFTextParagr }
}
}
+
+ /**
+ * Helper method for appending text and keeping paragraph and character properties.
+ * The character properties are moved to the end paragraph marker
+ */
+ /* package */ void clearButKeepProperties() {
+ CTTextParagraph thisP = getXmlObject();
+ for (int i=thisP.sizeOfBrArray(); i>0; 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 1b2272ba3e..049de50261 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 d78749c750..5aa11f1deb 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<XSLFTextRun> 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 8a2f3c6cc3..53c233199c 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<XSLFSlide> 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 c0e5948d77..32780264f2 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 2cfafa8d2e..0c74ae2b40 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 92245598f6..ca9d68d788 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 f896103b04..b7b697ac52 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; - - private int id=-1; - private int type; - private String address; - private String label; - private int startIndex, endIndex; +public final class HSLFHyperlink implements Hyperlink<HSLFShape,HSLFTextParagraph> { + private final ExHyperlink exHyper; + private final InteractiveInfo info; + private TxInteractiveInfoAtom txinfo; + + 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 setAddress(String str) { - address = str; + public void linkToSlide(Slide<HSLFShape,HSLFTextParagraph> 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); } - public int getId() { - return id; + @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); } - public void setId(int id) { - this.id = id; + @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) { + exHyper.setLinkURL(str); + } + + public int getId() { + 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<HSLFHyperlink> find(List<HSLFTextParagraph> paragraphs){ + protected static List<HSLFHyperlink> find(List<HSLFTextParagraph> paragraphs){ List<HSLFHyperlink> lst = new ArrayList<HSLFHyperlink>(); 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 <code>null</code> */ @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 e4ef3b1b77..0210a1d8a6 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). * </p> * <p> - * - * @author Yegor Kozlov */ public abstract class HSLFShape implements Shape<HSLFShape,HSLFTextParagraph> { @@ -89,7 +86,7 @@ public abstract class HSLFShape implements Shape<HSLFShape,HSLFTextParagraph> { * 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<HSLFShape,HSLFTextParagraph> { return getFill().getFillStyle(); } - /** - * Returns the hyperlink assigned to this shape - * - * @return the hyperlink assigned to this shape - * or <code>null</code> 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 ee61557734..72042769d1 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<HSLFShape,H if (trs == null) return; for (List<HSLFTextParagraph> ltp : trs) { HSLFTextParagraph.supplySheet(ltp, this); + HSLFTextParagraph.applyHyperlinks(ltp); } } @@ -171,6 +172,14 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheet<HSLFShape,H EscherContainerRecord sp = (EscherContainerRecord) it.next(); HSLFShape sh = HSLFShapeFactory.createShape(sp, null); sh.setSheet(this); + + if (sh instanceof HSLFSimpleShape) { + HSLFHyperlink link = HSLFHyperlink.find(sh); + if (link != null) { + ((HSLFSimpleShape)sh).setHyperlink(link); + } + } + shapeList.add(sh); } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java index 6c905b81f0..1326de19a0 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java @@ -32,8 +32,6 @@ import org.apache.poi.ddf.EscherSimpleProperty; import org.apache.poi.ddf.EscherSpRecord; import org.apache.poi.hslf.exceptions.HSLFException; import org.apache.poi.hslf.record.HSLFEscherClientDataRecord; -import org.apache.poi.hslf.record.InteractiveInfo; -import org.apache.poi.hslf.record.InteractiveInfoAtom; import org.apache.poi.hslf.record.OEPlaceholderAtom; import org.apache.poi.hslf.record.Record; import org.apache.poi.hslf.record.RoundTripHFPlaceholder12; @@ -70,6 +68,11 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H public final static double DEFAULT_LINE_WIDTH = 0.75; /** + * Hyperlink + */ + protected HSLFHyperlink _hyperlink; + + /** * Create a SimpleShape object and initialize it from the supplied Record container. * * @param escherRecord <code>EscherSpContainer</code> container which holds information about this shape @@ -270,56 +273,6 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H getFill().setForegroundColor(color); } - public void setHyperlink(HSLFHyperlink link){ - if(link.getId() == -1){ - throw new HSLFException("You must call SlideShow.addHyperlink(Hyperlink link) first"); - } - - InteractiveInfo info = new InteractiveInfo(); - InteractiveInfoAtom infoAtom = info.getInteractiveInfoAtom(); - - switch(link.getType()){ - case HSLFHyperlink.LINK_FIRSTSLIDE: - infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP); - infoAtom.setJump(InteractiveInfoAtom.JUMP_FIRSTSLIDE); - infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_FirstSlide); - break; - case HSLFHyperlink.LINK_LASTSLIDE: - infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP); - infoAtom.setJump(InteractiveInfoAtom.JUMP_LASTSLIDE); - infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_LastSlide); - break; - case HSLFHyperlink.LINK_NEXTSLIDE: - infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP); - infoAtom.setJump(InteractiveInfoAtom.JUMP_NEXTSLIDE); - infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_NextSlide); - break; - case HSLFHyperlink.LINK_PREVIOUSSLIDE: - infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP); - infoAtom.setJump(InteractiveInfoAtom.JUMP_PREVIOUSSLIDE); - infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_PreviousSlide); - break; - case HSLFHyperlink.LINK_URL: - infoAtom.setAction(InteractiveInfoAtom.ACTION_HYPERLINK); - infoAtom.setJump(InteractiveInfoAtom.JUMP_NONE); - infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_Url); - break; - case HSLFHyperlink.LINK_SLIDENUMBER: - infoAtom.setAction(InteractiveInfoAtom.ACTION_HYPERLINK); - infoAtom.setJump(InteractiveInfoAtom.JUMP_NONE); - infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_SlideNumber); - break; - default: - logger.log(POILogger.WARN, "Ignore unknown hyperlink type : "+link.getLabel()); - break; - } - - infoAtom.setHyperlinkID(link.getId()); - - HSLFEscherClientDataRecord cldata = getClientData(true); - cldata.addChild(info); - } - public Guide getAdjustValue(String name) { if (name == null || !name.matches("adj([1-9]|10)?")) { throw new IllegalArgumentException("Adjust value '"+name+"' not supported."); @@ -653,4 +606,26 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H } } } + + @Override + public HSLFHyperlink getHyperlink(){ + return _hyperlink; + } + + @Override + public HSLFHyperlink createHyperlink() { + if (_hyperlink == null) { + _hyperlink = HSLFHyperlink.createHyperlink(this); + } + return _hyperlink; + } + + /** + * Sets the hyperlink - used when the document is parsed + * + * @param link the hyperlink + */ + protected void setHyperlink(HSLFHyperlink link) { + _hyperlink = link; + } }
\ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java index fae9e20593..78abb0ab0f 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java @@ -1011,28 +1011,6 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap } /** - * Add a hyperlink to this presentation - * - * @return 0-based index of the hyperlink - */ - public int addHyperlink(HSLFHyperlink link) { - ExHyperlink ctrl = new ExHyperlink(); - ExHyperlinkAtom obj = ctrl.getExHyperlinkAtom(); - if(link.getType() == HSLFHyperlink.LINK_SLIDENUMBER) { - ctrl.setLinkURL(link.getAddress(), 0x30); - } else { - ctrl.setLinkURL(link.getAddress()); - } - ctrl.setLinkTitle(link.getLabel()); - - int objectId = addToObjListAtom(ctrl); - link.setId(objectId); - obj.setNumber(objectId); - - return objectId; - } - - /** * Add a embedded object to this presentation * * @return 0-based index of the embedded object @@ -1104,11 +1082,7 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap } protected int addToObjListAtom(RecordContainer exObj) { - ExObjList lst = (ExObjList) _documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID); - if (lst == null) { - lst = new ExObjList(); - _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom()); - } + ExObjList lst = getDocumentRecord().getExObjList(true); ExObjListAtom objAtom = lst.getExObjListAtom(); // increment the object ID seed int objectId = (int) objAtom.getObjectIDSeed() + 1; diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java index 5a050f5e13..ca76f84ee3 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java @@ -22,7 +22,6 @@ import static org.apache.poi.hslf.record.RecordTypes.OutlineTextRefAtom; import java.awt.Color;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@@ -40,6 +39,7 @@ import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType; import org.apache.poi.hslf.record.ColorSchemeAtom;
import org.apache.poi.hslf.record.EscherTextboxWrapper;
import org.apache.poi.hslf.record.FontCollection;
+import org.apache.poi.hslf.record.InteractiveInfo;
import org.apache.poi.hslf.record.MasterTextPropAtom;
import org.apache.poi.hslf.record.OutlineTextRefAtom;
import org.apache.poi.hslf.record.PPDrawing;
@@ -55,6 +55,7 @@ import org.apache.poi.hslf.record.TextCharsAtom; import org.apache.poi.hslf.record.TextHeaderAtom;
import org.apache.poi.hslf.record.TextRulerAtom;
import org.apache.poi.hslf.record.TextSpecInfoAtom;
+import org.apache.poi.hslf.record.TxInteractiveInfoAtom;
import org.apache.poi.sl.draw.DrawPaint;
import org.apache.poi.sl.usermodel.AutoNumberingScheme;
import org.apache.poi.sl.usermodel.PaintStyle;
@@ -102,7 +103,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText private StyleTextProp9Atom styleTextProp9Atom;
private boolean _dirty = false;
-
+
private final List<HSLFTextParagraph> parentList;
/**
@@ -162,14 +163,14 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText * Setting a master style reference
*
* @param paragraphStyle the master style reference
- *
+ *
* @since POI 3.14-Beta1
*/
@Internal
/* package */ void setMasterStyleReference(TextPropCollection paragraphStyle) {
_paragraphStyle = paragraphStyle;
}
-
+
/**
* Supply the Sheet we belong to, which might have an assigned SlideShow
* Also passes it on to our child RichTextRuns
@@ -183,9 +184,8 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText }
assert(sheet.getSlideShow() != null);
- applyHyperlinks(paragraphs);
}
-
+
/**
* Supply the Sheet we belong to, which might have an assigned SlideShow
* Also passes it on to our child RichTextRuns
@@ -519,7 +519,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText }
}
}
-
+
@Override
public HSLFTextShape getParentShape() {
return _parentShape;
@@ -603,12 +603,12 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText if (_runs.isEmpty()) {
return null;
}
-
+
SolidPaint sp = _runs.get(0).getFontColor();
if(sp == null) {
return null;
}
-
+
return DrawPaint.applyColorTransform(sp.getSolidColor());
}
@@ -709,7 +709,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText * Fetch the value of the given Paragraph related TextProp. Returns null if
* that TextProp isn't present. If the TextProp isn't present, the value
* from the appropriate Master Sheet will apply.
- *
+ *
* The propName can be a comma-separated list, in case multiple equivalent values
* are queried
*/
@@ -733,12 +733,12 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText }
boolean isChar = props.getTextPropType() == TextPropType.character;
-
+
for (String pn : propNames) {
TextProp prop = master.getStyleAttribute(txtype, paragraph.getIndentLevel(), pn, isChar);
if (prop != null) return prop;
}
-
+
return null;
}
@@ -821,8 +821,21 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText */
protected static void storeText(List<HSLFTextParagraph> 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<HSLFTextParagraph> 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<HSLFShape,HSLFText }
}
+ }
+
+ /**
+ * Update paragraph and character styles - merges them when subsequential styles match
+ */
+ private static void updateStyles(List<HSLFTextParagraph> 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<HSLFShape,HSLFText break;
}
}
+ }
+ private static void updateHyperlinks(List<HSLFTextParagraph> 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<HSLFTextParagraph> 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<HSLFShape,HSLFText throw new RuntimeException("failed dummy write", e);
}
}
-
- for (HSLFTextParagraph p : paragraphs) {
- p._dirty = false;
- }
}
/**
@@ -1043,7 +1109,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText }
return sb.toString();
}
-
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -1266,20 +1332,46 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText protected static void applyHyperlinks(List<HSLFTextParagraph> paragraphs) {
List<HSLFHyperlink> 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<HSLFTextRun> 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<HSLFTextParagraph> paragraphs, List<TextPropCollection> charStyles) {
int paraIdx = 0, runIdx = 0;
HSLFTextRun trun;
@@ -1444,7 +1536,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText public boolean isDirty() {
return _dirty;
}
-
+
/**
* Calculates the start index of the given text run
*
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java index 2178d1d85f..427e2d2c5e 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java @@ -27,10 +27,6 @@ import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp; import org.apache.poi.hslf.model.textproperties.TextProp; import org.apache.poi.hslf.model.textproperties.TextPropCollection; import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType; -import org.apache.poi.hslf.record.ExHyperlink; -import org.apache.poi.hslf.record.InteractiveInfo; -import org.apache.poi.hslf.record.InteractiveInfoAtom; -import org.apache.poi.hslf.record.Record; import org.apache.poi.sl.draw.DrawPaint; import org.apache.poi.sl.usermodel.PaintStyle; import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; @@ -51,7 +47,7 @@ public final class HSLFTextRun implements TextRun { private HSLFTextParagraph parentParagraph; private String _runText = ""; private String _fontFamily; - private int hyperlinkId = -1; + private HSLFHyperlink link; /** * Our paragraph and character style. @@ -395,64 +391,27 @@ public final class HSLFTextRun implements TextRun { public byte getPitchAndFamily() { return 0; } - + /** - * Sets the associated hyperlink id - currently this is only used while parsing and - * can't be used for update a ppt + * Sets the hyperlink - used when parsing the document * - * @param hyperlinkId the id or -1 to unset it + * @param link the hyperlink */ - public void setHyperlinkId(int hyperlinkId) { - this.hyperlinkId = hyperlinkId; + protected void setHyperlink(HSLFHyperlink link) { + this.link = link; } - /** - * Returns the associated hyperlink id - * - * @return the hyperlink id - */ - public int getHyperlinkId() { - return hyperlinkId; + @Override + public HSLFHyperlink getHyperlink() { + return link; } - @Override - public HSLFHyperlink getHyperlink() { - if (hyperlinkId == -1) { - return null; - } - - ExHyperlink linkRecord = parentParagraph.getSheet().getSlideShow().getDocumentRecord().getExObjList().get(hyperlinkId); - if (linkRecord == null) { - return null; - } - - InteractiveInfoAtom info = null; - for (Record r : parentParagraph.getRecords()) { - if (r instanceof InteractiveInfo) { - InteractiveInfo ii = (InteractiveInfo)r; - InteractiveInfoAtom iia = ii.getInteractiveInfoAtom(); - if (iia.getHyperlinkID() == hyperlinkId) { - info = iia; - break; - } - } - } - if (info == null) { - return null; + public HSLFHyperlink createHyperlink() { + if (link == null) { + link = HSLFHyperlink.createHyperlink(this); + parentParagraph.setDirty(); } - - // TODO: check previous/next sibling runs for same hyperlink id and return the whole string length - int startIdx = parentParagraph.getStartIdxOfTextRun(this); - - HSLFHyperlink link = new HSLFHyperlink(); - link.setId(hyperlinkId); - link.setType(info.getAction()); - link.setLabel(linkRecord.getLinkTitle()); - link.setAddress(linkRecord.getLinkURL()); - link.setStartIndex(startIdx); - link.setEndIndex(startIdx+getLength()); - return link; } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java index c90149854f..4f78bd6c8f 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java @@ -35,13 +35,10 @@ import org.apache.poi.ddf.EscherTextboxRecord; import org.apache.poi.hslf.exceptions.HSLFException; import org.apache.poi.hslf.model.HSLFMetroShape; import org.apache.poi.hslf.record.EscherTextboxWrapper; -import org.apache.poi.hslf.record.InteractiveInfo; -import org.apache.poi.hslf.record.InteractiveInfoAtom; import org.apache.poi.hslf.record.OEPlaceholderAtom; import org.apache.poi.hslf.record.PPDrawing; import org.apache.poi.hslf.record.RoundTripHFPlaceholder12; import org.apache.poi.hslf.record.TextHeaderAtom; -import org.apache.poi.hslf.record.TxInteractiveInfoAtom; import org.apache.poi.sl.draw.DrawFactory; import org.apache.poi.sl.draw.DrawTextShape; import org.apache.poi.sl.usermodel.Insets2D; @@ -622,33 +619,6 @@ implements TextShape<HSLFShape,HSLFTextParagraph> { 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<HSLFShape,HSLFTextParagraph> { 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<HSLFTextParagraph> paras = getTextParagraphs(); + HSLFTextRun htr = HSLFTextParagraph.appendText(paras, text, newParagraph); + setTextId(getRawText().hashCode()); + return htr; + } - // Update methods follow - - /** - * 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<HSLFTextParagraph> paras = getTextParagraphs(); - return HSLFTextParagraph.appendText(paras, text, newParagraph); - } - - @Override - public HSLFTextRun setText(String text) { - // init paragraphs - List<HSLFTextParagraph> 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<HSLFTextParagraph> paras = getTextParagraphs(); - HSLFTextParagraph.storeText(paras); - } - // Accesser methods follow + @Override + public HSLFTextRun setText(String text) { + // init paragraphs + List<HSLFTextParagraph> paras = getTextParagraphs(); + HSLFTextRun htr = HSLFTextParagraph.setText(paras, text); + setTextId(getRawText().hashCode()); + return htr; + } + + /** + * Saves the modified paragraphs/textrun to the records. + * Also updates the styles to the correct text length. + */ + protected void storeText() { + List<HSLFTextParagraph> 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 715640a1d5..7810929d78 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<HSLFTextParagraph> 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<HSLFHyperlink> links = HSLFHyperlink.find(para); - assertNotNull(links); + + List<HSLFHyperlink> 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<HSLFSlide> 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<HSLFHyperlink> findHyperlinks(List<HSLFTextParagraph> paras) { + List<HSLFHyperlink> links = new ArrayList<HSLFHyperlink>(); + 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 bf596faed8..88f6884df4 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()); |