poi-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kiwiwi...@apache.org
Subject svn commit: r1726458 [1/2] - in /poi: site/src/documentation/content/xdocs/ site/src/documentation/content/xdocs/slideshow/ trunk/src/examples/src/org/apache/poi/hslf/examples/ trunk/src/examples/src/org/apache/poi/xslf/usermodel/ trunk/src/java/org/ap...
Date Sun, 24 Jan 2016 00:12:11 GMT
Author: kiwiwings
Date: Sun Jan 24 00:12:10 2016
New Revision: 1726458

URL: http://svn.apache.org/viewvc?rev=1726458&view=rev
Log:
#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

Modified:
    poi/site/src/documentation/content/xdocs/slideshow/how-to-shapes.xml
    poi/site/src/documentation/content/xdocs/slideshow/xslf-cookbook.xml
    poi/site/src/documentation/content/xdocs/status.xml
    poi/trunk/src/examples/src/org/apache/poi/hslf/examples/CreateHyperlink.java
    poi/trunk/src/examples/src/org/apache/poi/hslf/examples/Hyperlinks.java
    poi/trunk/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial6.java
    poi/trunk/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
    poi/trunk/src/java/org/apache/poi/sl/usermodel/Hyperlink.java
    poi/trunk/src/java/org/apache/poi/sl/usermodel/SimpleShape.java
    poi/trunk/src/java/org/apache/poi/sl/usermodel/TextRun.java
    poi/trunk/src/java/org/apache/poi/sl/usermodel/TextShape.java
    poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFHyperlink.java
    poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java
    poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java
    poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
    poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFHyperlink.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/Document.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFHyperlink.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java
    poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/model/TestHyperlink.java
    poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExObjList.java

Modified: poi/site/src/documentation/content/xdocs/slideshow/how-to-shapes.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/slideshow/how-to-shapes.xml?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/slideshow/how-to-shapes.xml (original)
+++ poi/site/src/documentation/content/xdocs/slideshow/how-to-shapes.xml Sun Jan 24 00:12:10 2016
@@ -345,11 +345,15 @@
     for (HSLFSlide slide : ppt.getSlides()) {
         //read hyperlinks from the text runs
         for (List<HSLFTextParagraph> txt : slide.getTextParagraphs()) {
-            String text = HSLFTextParagraph.getText(txt);
-            for (HSLFHyperlink link : HSLFHyperlink.find(txt)) {
-                String title = link.getTitle();
-                String address = link.getAddress();
-                String substring = text.substring(link.getStartIndex(), link.getEndIndex()-1); //in ppt end index is inclusive
+            for (HSLFTextParagraph para : txt) {
+                for (HSLFTextRun run : para) {
+                    HSLFHyperlink link = run.getHyperlink();
+                    if (link != null) {
+                        String title = link.getLabel();
+                        String address = link.getAddress();
+                        String text = run.getRawText();
+                    }
+                }
             }
         }
 
@@ -357,10 +361,12 @@
         //for example to a Line object. The code below demonstrates how to
         //read such hyperlinks
         for (HSLFShape sh : slide.getShapes()) {
-            HSLFHyperlink link = sh.getHyperlink();
-            if(link != null)  {
-                String title = link.getTitle();
-                String address = link.getAddress();
+            if (sh instanceof HSLFSimpleShape) {
+                HSLFHyperlink link = ((HSLFSimpleShape)sh).getHyperlink();
+                if(link != null)  {
+                    String title = link.getLabel();
+                    String address = link.getAddress();
+                }
             }
         }
     }

Modified: poi/site/src/documentation/content/xdocs/slideshow/xslf-cookbook.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/slideshow/xslf-cookbook.xml?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/slideshow/xslf-cookbook.xml (original)
+++ poi/site/src/documentation/content/xdocs/slideshow/xslf-cookbook.xml Sun Jan 24 00:12:10 2016
@@ -240,7 +240,7 @@
             </source>
                 </section>
                 <anchor id="Hyperlinks"/>
-                <section><title>How to read hyperlinks from a slide show</title>
+                <section><title>How to create a hyperlink</title>
                     <source>
     XMLSlideShow ppt = new XMLSlideShow();
     XSLFSlide slide = ppt.createSlide();

Modified: poi/site/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/status.xml?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/status.xml (original)
+++ poi/site/src/documentation/content/xdocs/status.xml Sun Jan 24 00:12:10 2016
@@ -40,6 +40,8 @@
     </devs>
 
     <release version="3.14-beta2" date="2016-??-??">
+        <action dev="PD" type="add" fixes-bug="47291">Cannot open link correctly which insert in ppt</action>
+        <action dev="PD" type="add" fixes-bug="41047">Support hyperlinks in HSLF shapes and textruns</action>
         <action dev="PD" type="add" fixes-bug="58879">Return SpreadsheetVersion from Workbook</action>
         <action dev="PD" type="fix">Fix NPE when calling HSLFTextRun.setHorizontalCentered()</action>
         <action dev="PD" type="add" fixes-bug="58633">Set multiple cell style properties at once</action>

Modified: poi/trunk/src/examples/src/org/apache/poi/hslf/examples/CreateHyperlink.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/examples/src/org/apache/poi/hslf/examples/CreateHyperlink.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/examples/src/org/apache/poi/hslf/examples/CreateHyperlink.java (original)
+++ poi/trunk/src/examples/src/org/apache/poi/hslf/examples/CreateHyperlink.java Sun Jan 24 00:12:10 2016
@@ -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();
    }
 }

Modified: poi/trunk/src/examples/src/org/apache/poi/hslf/examples/Hyperlinks.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/examples/src/org/apache/poi/hslf/examples/Hyperlinks.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/examples/src/org/apache/poi/hslf/examples/Hyperlinks.java (original)
+++ poi/trunk/src/examples/src/org/apache/poi/hslf/examples/Hyperlinks.java Sun Jan 24 00:12:10 2016
@@ -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);
     }
 }

Modified: poi/trunk/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial6.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial6.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial6.java (original)
+++ poi/trunk/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial6.java Sun Jan 24 00:12:10 2016
@@ -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
 
 
 

Modified: poi/trunk/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/draw/DrawPictureShape.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/draw/DrawPictureShape.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/draw/DrawPictureShape.java Sun Jan 24 00:12:10 2016
@@ -61,7 +61,7 @@ public class DrawPictureShape extends Dr
      * 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);

Modified: poi/trunk/src/java/org/apache/poi/sl/usermodel/Hyperlink.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/usermodel/Hyperlink.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/usermodel/Hyperlink.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/usermodel/Hyperlink.java Sun Jan 24 00:12:10 2016
@@ -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();
 }

Modified: poi/trunk/src/java/org/apache/poi/sl/usermodel/SimpleShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/usermodel/SimpleShape.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/usermodel/SimpleShape.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/usermodel/SimpleShape.java Sun Jan 24 00:12:10 2016
@@ -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();
 }

Modified: poi/trunk/src/java/org/apache/poi/sl/usermodel/TextRun.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/usermodel/TextRun.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/usermodel/TextRun.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/usermodel/TextRun.java Sun Jan 24 00:12:10 2016
@@ -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();
 }

Modified: poi/trunk/src/java/org/apache/poi/sl/usermodel/TextShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/usermodel/TextShape.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/usermodel/TextShape.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/usermodel/TextShape.java Sun Jan 24 00:12:10 2016
@@ -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

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFHyperlink.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFHyperlink.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFHyperlink.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFHyperlink.java Sun Jan 24 00:12:10 2016
@@ -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 Hy
     }
 
     @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 Hy
 
     @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

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java Sun Jan 24 00:12:10 2016
@@ -34,8 +34,8 @@ import org.apache.poi.sl.usermodel.LineD
 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.drawin
 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 ex
     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;
+    }
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java Sun Jan 24 00:12:10 2016
@@ -994,4 +994,26 @@ public class XSLFTextParagraph implement
             }
         }
     }
+    
+    /**
+     * 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();
+        }
+    }
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java Sun Jan 24 00:12:10 2016
@@ -444,17 +444,19 @@ public class XSLFTextRun implements Text
         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){

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java Sun Jan 24 00:12:10 2016
@@ -71,10 +71,7 @@ public abstract class XSLFTextShape exte
         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 exte
 
     @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

Modified: poi/trunk/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFHyperlink.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFHyperlink.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFHyperlink.java (original)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFHyperlink.java Sun Jan 24 00:12:10 2016
@@ -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

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java Sun Jan 24 00:12:10 2016
@@ -160,7 +160,7 @@ public final class OLEShape extends HSLF
         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;

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/Document.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/Document.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/Document.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/Document.java Sun Jan 24 00:12:10 2016
@@ -46,22 +46,33 @@ public final class Document extends Posi
 	 * 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

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java Sun Jan 24 00:12:10 2016
@@ -67,9 +67,9 @@ public class ExHyperlink extends RecordC
 			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 RecordC
             linkDetailsA.setText(title);
         }
     }
-
+    
 	/**
 	 * Get the link details (field A)
 	 */

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFHyperlink.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFHyperlink.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFHyperlink.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFHyperlink.java Sun Jan 24 00:12:10 2016
@@ -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.Intera
 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 impleme
      */
     @Override
     public int getType() {
-        return type;
+        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;
+        }
     }
 
-    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;
-        }
+    @Override
+    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);
     }
 
     @Override
-    public String getAddress() {
-        return address;
+    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);
     }
 
-    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 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);
     }
 
     @Override
-    public void setAddress(String str) {
-        address = str;
+    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 int getId() {
-        return 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 void setId(int id) {
-        this.id = id;
+    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 impleme
      * @return the beginning character position
      */
     public int getStartIndex() {
-        return startIndex;
+        return (txinfo == null) ? -1 : txinfo.getStartIndex();
     }
 
     /**
@@ -139,16 +271,18 @@ public final class HSLFHyperlink impleme
      * @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 impleme
      * @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 impleme
      * @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 impleme
      * @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 impleme
             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 impleme
                     iter.previous();
                     continue;
                 }
-                TxInteractiveInfoAtom txinfo = (TxInteractiveInfoAtom)r;
-                link.setStartIndex(txinfo.getStartIndex());
-                link.setEndIndex(txinfo.getEndIndex());
+                link.setTextRunInfo((TxInteractiveInfoAtom)r);
             }
         }
     }

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java Sun Jan 24 00:12:10 2016
@@ -28,7 +28,6 @@ import org.apache.poi.ddf.EscherChildAnc
 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 implemen
      * 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 implemen
         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());
     }

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java Sun Jan 24 00:12:10 2016
@@ -134,6 +134,7 @@ public abstract class HSLFSheet implemen
         if (trs == null) return;
         for (List<HSLFTextParagraph> ltp : trs) {
             HSLFTextParagraph.supplySheet(ltp, this);
+            HSLFTextParagraph.applyHyperlinks(ltp);
         }
     }
 
@@ -171,6 +172,14 @@ public abstract class HSLFSheet implemen
             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);
         }
 

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java Sun Jan 24 00:12:10 2016
@@ -32,8 +32,6 @@ import org.apache.poi.ddf.EscherSimplePr
 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 ex
     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 ex
         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 ex
             }
         }
     }
+
+    @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

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java Sun Jan 24 00:12:10 2016
@@ -1011,28 +1011,6 @@ public final class HSLFSlideShow impleme
 	}
 
 	/**
-	 * 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 impleme
 	}
 
 	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;

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java Sun Jan 24 00:12:10 2016
@@ -22,7 +22,6 @@ import static org.apache.poi.hslf.record
 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.textpro
 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.TextCh
 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 imp
     private StyleTextProp9Atom styleTextProp9Atom;
 
     private boolean _dirty = false;
-    
+
     private final List<HSLFTextParagraph> parentList;
 
     /**
@@ -162,14 +163,14 @@ public final class HSLFTextParagraph imp
      * 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 imp
         }
 
         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 imp
             }
         }
     }
-    
+
     @Override
     public HSLFTextShape getParentShape() {
         return _parentShape;
@@ -603,12 +603,12 @@ public final class HSLFTextParagraph imp
             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 imp
      * 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 imp
         }
 
         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 imp
      */
     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 imp
             }
         }
 
+    }
+
+    /**
+     * 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 imp
                 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 imp
                 throw new RuntimeException("failed dummy write", e);
             }
         }
-
-        for (HSLFTextParagraph p : paragraphs) {
-            p._dirty = false;
-        }
     }
 
     /**
@@ -1043,7 +1109,7 @@ public final class HSLFTextParagraph imp
         }
         return sb.toString();
     }
-    
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -1266,20 +1332,46 @@ public final class HSLFTextParagraph imp
 
     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 imp
     public boolean isDirty() {
         return _dirty;
     }
-    
+
     /**
      * Calculates the start index of the given text run
      *

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java?rev=1726458&r1=1726457&r2=1726458&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java Sun Jan 24 00:12:10 2016
@@ -27,10 +27,6 @@ import org.apache.poi.hslf.model.textpro
 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 implement
 	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 implement
     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;
     }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org


Mime
View raw message