]> source.dussan.org Git - poi.git/commitdiff
#57796 - Support hyperlink extraction when rendering slides
authorAndreas Beeker <kiwiwings@apache.org>
Tue, 12 Jan 2016 23:20:48 +0000 (23:20 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Tue, 12 Jan 2016 23:20:48 +0000 (23:20 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1724338 13f79535-47bb-0310-9956-ffa450edef68

20 files changed:
src/examples/src/org/apache/poi/hslf/examples/CreateHyperlink.java
src/examples/src/org/apache/poi/hslf/examples/Hyperlinks.java
src/java/org/apache/poi/sl/draw/DrawTextParagraph.java
src/java/org/apache/poi/sl/usermodel/TextRun.java
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFHyperlink.java
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFHyperlink.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFNotes.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlide.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java
src/scratchpad/testcases/org/apache/poi/hslf/model/TestHyperlink.java
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java
test-data/slideshow/WithLinks.ppt

index 1d8c95a19f167eec686440207ad13c320f412dbc..44e5d81c1c543c0c47b2e25111d01796b65c05e7 100644 (file)
@@ -46,7 +46,7 @@ public abstract class CreateHyperlink {
         String text = textBox1.getText();
         HSLFHyperlink link = new HSLFHyperlink();
         link.setAddress("http://www.apache.org");
-        link.setTitle(textBox1.getText());
+        link.setLabel(textBox1.getText());
         int linkId = ppt.addHyperlink(link);
 
         // apply link to the text
index c054d2e7ac8c23bfaee3c99d6256c7dd50587349..8cbbcdc852d5fcda10acc57e837a958ce8e04c5b 100644 (file)
@@ -70,6 +70,6 @@ public final class Hyperlinks {
         //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.getTitle(), link.getAddress(), link.getStartIndex(), link.getEndIndex(), substring);
+        return String.format(formatStr, link.getLabel(), link.getAddress(), link.getStartIndex(), link.getEndIndex(), substring);
     }
 }
index 65233fe0babc2cc5cdcae509e11a1facaba9bc8c..e8a7b0b216dcfc76ccf843658164042561b26151 100644 (file)
@@ -24,6 +24,7 @@ import java.awt.font.LineBreakMeasurer;
 import java.awt.font.TextAttribute;\r
 import java.awt.font.TextLayout;\r
 import java.awt.geom.Rectangle2D;\r
+import java.io.InvalidObjectException;\r
 import java.text.AttributedCharacterIterator;\r
 import java.text.AttributedCharacterIterator.Attribute;\r
 import java.text.AttributedString;\r
@@ -32,6 +33,7 @@ import java.util.List;
 import java.util.Map;\r
 \r
 import org.apache.poi.sl.usermodel.AutoNumberingScheme;\r
+import org.apache.poi.sl.usermodel.Hyperlink;\r
 import org.apache.poi.sl.usermodel.Insets2D;\r
 import org.apache.poi.sl.usermodel.PaintStyle;\r
 import org.apache.poi.sl.usermodel.PlaceableShape;\r
@@ -46,6 +48,10 @@ import org.apache.poi.util.StringUtil;
 import org.apache.poi.util.Units;\r
 \r
 public class DrawTextParagraph implements Drawable {\r
+    /** Keys for passing hyperlinks to the graphics context */\r
+    public static final XlinkAttribute HYPERLINK_HREF = new XlinkAttribute("href");\r
+    public static final XlinkAttribute HYPERLINK_LABEL = new XlinkAttribute("label");\r
+\r
     protected TextParagraph<?,?,?> paragraph;\r
     double x, y;\r
     protected List<DrawTextFragment> lines = new ArrayList<DrawTextFragment>();\r
@@ -58,6 +64,31 @@ public class DrawTextParagraph implements Drawable {
      */\r
     protected double maxLineHeight;\r
 \r
+    /**\r
+     * Defines an attribute used for storing the hyperlink associated with\r
+     * some renderable text.\r
+     */\r
+    private static class XlinkAttribute extends Attribute {\r
+\r
+        XlinkAttribute(String name) {\r
+            super(name);\r
+        }\r
+\r
+        /**\r
+         * Resolves instances being deserialized to the predefined constants.\r
+         */\r
+        protected Object readResolve() throws InvalidObjectException {\r
+            if (HYPERLINK_HREF.getName().equals(getName())) {\r
+                return HYPERLINK_HREF;\r
+            }\r
+            if (HYPERLINK_LABEL.getName().equals(getName())) {\r
+                return HYPERLINK_LABEL;\r
+            }\r
+            throw new InvalidObjectException("unknown attribute name");\r
+        }\r
+    }\r
+\r
+\r
     public DrawTextParagraph(TextParagraph<?,?,?> paragraph) {\r
         this.paragraph = paragraph;\r
     }\r
@@ -79,10 +110,10 @@ public class DrawTextParagraph implements Drawable {
     public void setAutoNumberingIdx(int index) {\r
         autoNbrIdx = index;\r
     }\r
-    \r
+\r
     public void draw(Graphics2D graphics){\r
         if (lines.isEmpty()) return;\r
-        \r
+\r
         double penY = y;\r
 \r
         boolean firstLine = true;\r
@@ -100,7 +131,7 @@ public class DrawTextParagraph implements Drawable {
             // special handling for HSLF\r
             indent -= leftMargin;\r
         }\r
-        \r
+\r
 //        Double rightMargin = paragraph.getRightMargin();\r
 //        if (rightMargin == null) {\r
 //            rightMargin = 0d;\r
@@ -109,7 +140,7 @@ public class DrawTextParagraph implements Drawable {
         //The vertical line spacing\r
         Double spacing = paragraph.getLineSpacing();\r
         if (spacing == null) spacing = 100d;\r
-        \r
+\r
         for(DrawTextFragment line : lines){\r
             double penX;\r
 \r
@@ -118,7 +149,7 @@ public class DrawTextParagraph implements Drawable {
                     // TODO: find out character style for empty, but bulleted/numbered lines\r
                     bullet = getBullet(graphics, line.getAttributedString().getIterator());\r
                 }\r
-                \r
+\r
                 if (bullet != null){\r
                     bullet.setPosition(x+leftMargin+indent, penY);\r
                     bullet.draw(graphics);\r
@@ -168,7 +199,7 @@ public class DrawTextParagraph implements Drawable {
 \r
         y = penY - y;\r
     }\r
-    \r
+\r
     public float getFirstLineHeight() {\r
         return (lines.isEmpty()) ? 0 : lines.get(0).getHeight();\r
     }\r
@@ -180,7 +211,7 @@ public class DrawTextParagraph implements Drawable {
     public boolean isEmptyParagraph() {\r
         return (lines.isEmpty() || rawText.trim().isEmpty());\r
     }\r
-    \r
+\r
     public void applyTransform(Graphics2D graphics) {\r
     }\r
 \r
@@ -265,7 +296,7 @@ public class DrawTextParagraph implements Drawable {
         String buFont = bulletStyle.getBulletFont();\r
         if (buFont == null) buFont = paragraph.getDefaultFontFamily();\r
         assert(buFont != null);\r
-        \r
+\r
         PlaceableShape<?,?> ps = getParagraphShape();\r
         PaintStyle fgPaintStyle = bulletStyle.getBulletFontColor();\r
         Paint fgPaint;\r
@@ -277,11 +308,11 @@ public class DrawTextParagraph implements Drawable {
 \r
         float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE);\r
         Double buSz = bulletStyle.getBulletFontSize();\r
-        if (buSz == null) buSz = 100d; \r
+        if (buSz == null) buSz = 100d;\r
         if (buSz > 0) fontSize *= buSz* 0.01;\r
         else fontSize = (float)-buSz;\r
 \r
-        \r
+\r
         AttributedString str = new AttributedString(mapFontCharset(buCharacter,buFont));\r
         str.addAttribute(TextAttribute.FOREGROUND, fgPaint);\r
         str.addAttribute(TextAttribute.FAMILY, buFont);\r
@@ -313,7 +344,7 @@ public class DrawTextParagraph implements Drawable {
                         case SMALL: c = Character.toLowerCase(c); break;\r
                         case NONE: break;\r
                     }\r
-    \r
+\r
                     buf.append(c);\r
                     break;\r
             }\r
@@ -321,7 +352,7 @@ public class DrawTextParagraph implements Drawable {
 \r
         return buf.toString();\r
     }\r
-    \r
+\r
     /**\r
      * Replace a tab with the effective number of white spaces.\r
      */\r
@@ -334,7 +365,7 @@ public class DrawTextParagraph implements Drawable {
         Double fs = tr.getFontSize();\r
         if (fs == null) fs = 12d;\r
         string.addAttribute(TextAttribute.SIZE, fs.floatValue());\r
-        \r
+\r
         TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true));\r
         double wspace = l.getAdvance();\r
 \r
@@ -348,7 +379,7 @@ public class DrawTextParagraph implements Drawable {
         }\r
         return buf.toString();\r
     }\r
-    \r
+\r
 \r
     /**\r
      * Returns wrapping width to break lines in this paragraph\r
@@ -418,7 +449,7 @@ public class DrawTextParagraph implements Drawable {
             this.endIndex = endIndex;\r
         }\r
     }\r
-    \r
+\r
     /**\r
      * Helper method for paint style relative to bounds, e.g. gradient paint\r
      */\r
@@ -443,7 +474,7 @@ public class DrawTextParagraph implements Drawable {
         if (text == null) text = new StringBuilder();\r
 \r
         PlaceableShape<?,?> ps = getParagraphShape();\r
-        \r
+\r
         DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER);\r
 \r
         for (TextRun run : paragraph){\r
@@ -464,7 +495,7 @@ public class DrawTextParagraph implements Drawable {
             if (fontFamily == null) {\r
                 fontFamily = paragraph.getDefaultFontFamily();\r
             }\r
-            \r
+\r
             int beginIndex = text.length();\r
             text.append(mapFontCharset(runText,fontFamily));\r
             int endIndex = text.length();\r
@@ -498,6 +529,12 @@ public class DrawTextParagraph implements Drawable {
             if(run.isSuperscript()) {\r
                 attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, beginIndex, endIndex));\r
             }\r
+            \r
+            Hyperlink hl = run.getHyperlink();\r
+            if (hl != null) {\r
+                attList.add(new AttributedStringData(HYPERLINK_HREF, hl.getAddress(), beginIndex, endIndex));\r
+                attList.add(new AttributedStringData(HYPERLINK_LABEL, hl.getLabel(), beginIndex, endIndex));\r
+            }\r
         }\r
 \r
         // ensure that the paragraph contains at least one character\r
@@ -517,9 +554,9 @@ public class DrawTextParagraph implements Drawable {
     }\r
 \r
     protected boolean isHSLF() {\r
-        return paragraph.getClass().getName().contains("HSLF");        \r
+        return paragraph.getClass().getName().contains("HSLF");\r
     }\r
-    \r
+\r
     /**\r
      * Map text charset depending on font family.\r
      * Currently this only maps for wingdings font (into unicode private use area)\r
@@ -527,7 +564,7 @@ public class DrawTextParagraph implements Drawable {
      * @param text the raw text\r
      * @param fontFamily the font family\r
      * @return AttributedString with mapped codepoints\r
-     * \r
+     *\r
      * @see <a href="http://stackoverflow.com/questions/8692095">Drawing exotic fonts in a java applet</a>\r
      * @see StringUtil#mapMsCodepointString(String)\r
      */\r
index 9eb27b616ed73d90f829155b8648ba87e91ff2e9..28db79795856001d2b0fe35dd9a5dd9ef32e8d33 100644 (file)
@@ -156,4 +156,11 @@ public interface TextRun {
      * @return the pitch and family id or -1 if not applicable
      */
     byte getPitchAndFamily();
+
+    /**
+     * Return the associated hyperlink
+     * 
+     * @return the associated hyperlink or null if no hyperlink was set
+     */
+    Hyperlink getHyperlink();
 }
index 5a25851b5c12e73dc6e0a4b25a07dedacaa325c2..0b093ecacb7749beeb1f94895611cdfc26e63b83 100644 (file)
@@ -18,15 +18,13 @@ package org.apache.poi.xslf.usermodel;
 \r
 import org.apache.poi.openxml4j.opc.PackageRelationship;\r
 import org.apache.poi.openxml4j.opc.TargetMode;\r
+import org.apache.poi.sl.usermodel.Hyperlink;\r
 import org.apache.poi.util.Internal;\r
 import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink;\r
 \r
 import java.net.URI;\r
 \r
-/**\r
- * @author Yegor Kozlov\r
- */\r
-public class XSLFHyperlink {\r
+public class XSLFHyperlink implements Hyperlink {\r
     final XSLFTextRun _r;\r
     final CTHyperlink _link;\r
 \r
@@ -40,15 +38,39 @@ public class XSLFHyperlink {
         return _link;\r
     }\r
 \r
+    @Override\r
     public void setAddress(String address){\r
         XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet();\r
         PackageRelationship rel =\r
                 sheet.getPackagePart().\r
                         addExternalRelationship(address, XSLFRelation.HYPERLINK.getRelation());\r
         _link.setId(rel.getId());\r
+    }\r
+    \r
+    @Override\r
+    public String getAddress() {\r
+        return getTargetURI().toASCIIString();\r
+    }\r
 \r
+    @Override\r
+    public String getLabel() {\r
+        return _link.getTooltip();\r
+    }\r
+    \r
+    @Override\r
+    public void setLabel(String label) {\r
+        _link.setTooltip(label);\r
     }\r
 \r
+    @Override\r
+    public int getType() {\r
+        // TODO: currently this just returns nonsense\r
+        if ("ppaction://hlinksldjump".equals(_link.getAction())) {\r
+            return LINK_DOCUMENT;\r
+        }\r
+        return LINK_URL;\r
+    }\r
+    \r
     public void setAddress(XSLFSlide slide){\r
         XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet();\r
         PackageRelationship rel =\r
index 8ce7c5241e9b0238020e3915d9e7372ae605e399..1b2272ba3e49c389c253dce1b8d89e81e0a0cd15 100644 (file)
@@ -449,6 +449,7 @@ public class XSLFTextRun implements TextRun {
         return link;\r
     }\r
 \r
+    @Override\r
     public XSLFHyperlink getHyperlink(){\r
         if(!_r.getRPr().isSetHlinkClick()) return null;\r
 \r
index 0022822b826281f464522bf1d45423972ce17995..4a3af15f8d8be154dabf59b0a284b010b9362f73 100644 (file)
@@ -282,7 +282,7 @@ public final class PPGraphics2D extends Graphics2D implements Cloneable {
      */
     public void drawString(String s, float x, float y) {
         HSLFTextBox txt = new HSLFTextBox(_group);
-        txt.getTextParagraphs().get(0).supplySheet(_group.getSheet());
+        txt.setSheet(_group.getSheet());
         txt.setText(s);
 
         HSLFTextRun rt = txt.getTextParagraphs().get(0).getTextRuns().get(0);
index d20f0efa9bcb12341bb53904e5d3c729b48c4c5e..f896103b04d95a23e280aa425ca4238ce7da8471 100644 (file)
@@ -29,13 +29,12 @@ 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.hslf.record.TxInteractiveInfoAtom;
+import org.apache.poi.sl.usermodel.Hyperlink;
 
 /**
  * Represents a hyperlink in a PowerPoint document
- *
- * @author Yegor Kozlov
  */
-public final class HSLFHyperlink {
+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;
@@ -47,7 +46,7 @@ public final class HSLFHyperlink {
     private int id=-1;
     private int type;
     private String address;
-    private String title;
+    private String label;
     private int startIndex, endIndex;
 
     /**
@@ -57,6 +56,7 @@ public final class HSLFHyperlink {
      * @return the hyperlink URL
      * @see InteractiveInfoAtom
      */
+    @Override
     public int getType() {
         return type;
     }
@@ -65,35 +65,31 @@ public final class HSLFHyperlink {
         type = val;
         switch(type){
             case LINK_NEXTSLIDE:
-                title = "NEXT";
+                label = "NEXT";
                 address = "1,-1,NEXT";
                 break;
             case LINK_PREVIOUSSLIDE:
-                title = "PREV";
+                label = "PREV";
                 address = "1,-1,PREV";
                 break;
             case LINK_FIRSTSLIDE:
-                title = "FIRST";
+                label = "FIRST";
                 address = "1,-1,FIRST";
                 break;
             case LINK_LASTSLIDE:
-                title = "LAST";
+                label = "LAST";
                 address = "1,-1,LAST";
                 break;
             case LINK_SLIDENUMBER:
                 break;
             default:
-                title = "";
+                label = "";
                 address = "";
                 break;
         }
     }
 
-    /**
-     * Gets the hyperlink URL
-     *
-     * @return the hyperlink URL
-     */
+    @Override
     public String getAddress() {
         return address;
     }
@@ -101,10 +97,11 @@ public final class HSLFHyperlink {
     public void setAddress(HSLFSlide slide) {
         String href = slide._getSheetNumber() + ","+slide.getSlideNumber()+",Slide " + slide.getSlideNumber();
         setAddress(href);;
-        setTitle("Slide " + slide.getSlideNumber());
+        setLabel("Slide " + slide.getSlideNumber());
         setType(HSLFHyperlink.LINK_SLIDENUMBER);
     }
 
+    @Override
     public void setAddress(String str) {
         address = str;
     }
@@ -117,17 +114,14 @@ public final class HSLFHyperlink {
         this.id = id;
     }
 
-    /**
-     * Gets the hyperlink user-friendly title (if different from URL)
-     *
-     * @return the  hyperlink user-friendly title
-     */
-    public String getTitle() {
-        return title;
+    @Override
+    public String getLabel() {
+        return label;
     }
 
-    public void setTitle(String str) {
-        title = str;
+    @Override
+    public void setLabel(String str) {
+        label = str;
     }
 
     /**
@@ -139,6 +133,15 @@ public final class HSLFHyperlink {
         return startIndex;
     }
 
+    /**
+     * Sets the beginning character position
+     *
+     * @param startIndex the beginning character position
+     */
+    public void setStartIndex(int startIndex) {
+        this.startIndex = startIndex;
+    }
+    
     /**
      * Gets the ending character position
      *
@@ -148,6 +151,15 @@ public final class HSLFHyperlink {
         return endIndex;
     }
 
+    /**
+     * Sets the ending character position
+     *
+     * @param endIndex the ending character position
+     */
+    public void setEndIndex(int endIndex) {
+        this.endIndex = endIndex;
+    }
+    
     /**
      * Find hyperlinks in a text shape
      *
@@ -222,9 +234,10 @@ public final class HSLFHyperlink {
             }
             
             HSLFHyperlink link = new HSLFHyperlink();
-            link.title = linkRecord.getLinkTitle();
-            link.address = linkRecord.getLinkURL();
-            link.type = info.getAction();
+            link.setId(id);
+            link.setType(info.getAction());
+            link.setLabel(linkRecord.getLinkTitle());
+            link.setAddress(linkRecord.getLinkURL());
             out.add(link);
 
             if (iter.hasNext()) {
@@ -234,8 +247,8 @@ public final class HSLFHyperlink {
                     continue;
                 }
                 TxInteractiveInfoAtom txinfo = (TxInteractiveInfoAtom)r;
-                link.startIndex = txinfo.getStartIndex();
-                link.endIndex = txinfo.getEndIndex();
+                link.setStartIndex(txinfo.getStartIndex());
+                link.setEndIndex(txinfo.getEndIndex());
             }
         }
     }
index 50125ea48e898a997612e7c78cab27bd967ddb9b..d64775e117189e892c11bf7fdd541f0bea7fec0d 100644 (file)
@@ -56,13 +56,6 @@ public final class HSLFNotes extends HSLFSheet implements Notes<HSLFShape,HSLFTe
         if (_paragraphs.isEmpty()) {
             logger.log(POILogger.WARN, "No text records found for notes sheet");
         }
-
-        // Set the sheet on each TextRun
-        for (List<HSLFTextParagraph> ltp : _paragraphs) {
-            for (HSLFTextParagraph tp : ltp) {
-                tp.supplySheet(this);
-            }
-        }
     }
 
     /**
index 3fe79aeb16417a793f00ce211611a6dac5431996..ee61557734084661e997088c2a86eac91692f56e 100644 (file)
@@ -133,9 +133,7 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheet<HSLFShape,H
         List<List<HSLFTextParagraph>> trs = getTextParagraphs();
         if (trs == null) return;
         for (List<HSLFTextParagraph> ltp : trs) {
-            for (HSLFTextParagraph tp : ltp) {
-                tp.supplySheet(this);
-            }
+            HSLFTextParagraph.supplySheet(ltp, this);
         }
     }
 
index 4da85a946eec65673db47ed9c6e30e5f52ccb931..b78a5c8fad811e71ad24c93ab796359767f081ed 100644 (file)
@@ -310,7 +310,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
                 infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_SlideNumber);
                 break;
             default:
-                logger.log(POILogger.WARN, "Ignore unknown hyperlink type : "+link.getTitle());
+                logger.log(POILogger.WARN, "Ignore unknown hyperlink type : "+link.getLabel());
                 break;
         }
 
index 73223646c794dfec91c51e72174bc74f02533e1c..453a790fddd891753f4eaa169a12db48fa6e6e3a 100644 (file)
@@ -94,12 +94,6 @@ public final class HSLFSlide extends HSLFSheet implements Slide<HSLFShape,HSLFTe
                for (List<HSLFTextParagraph> l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) {
                    if (!_paragraphs.contains(l)) _paragraphs.add(l);
                }
-
-        for(List<HSLFTextParagraph> ltp : _paragraphs) {
-            for (HSLFTextParagraph tp : ltp) {
-                tp.supplySheet(this);
-            }
-        }
        }
 
        /**
index 1e89b69a9e37c2297a62d4dbb80b76a7f2e052c9..9b5668ffee18eb1f8b46628ab7119f2e9e4ba328 100644 (file)
@@ -51,12 +51,6 @@ public final class HSLFSlideMaster extends HSLFMasterSheet {
         for (List<HSLFTextParagraph> l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) {
             if (!_paragraphs.contains(l)) _paragraphs.add(l);
         }
-
-        for (List<HSLFTextParagraph> p : _paragraphs) {
-            for (HSLFTextParagraph htp : p) {
-                htp.supplySheet(this);
-            }
-        }
     }
 
     /**
index f08c398074d160bf9ca77d7d1273417800d39f90..fae9e205939ee3e7d7d776917daba9cd20d6e7bc 100644 (file)
@@ -33,6 +33,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.poi.hslf.record.MainMaster;
 import org.apache.poi.ddf.EscherBSERecord;
 import org.apache.poi.ddf.EscherContainerRecord;
 import org.apache.poi.ddf.EscherOptRecord;
@@ -59,12 +60,14 @@ import org.apache.poi.hslf.record.ExVideoContainer;
 import org.apache.poi.hslf.record.FontCollection;
 import org.apache.poi.hslf.record.FontEntityAtom;
 import org.apache.poi.hslf.record.HeadersFootersContainer;
+import org.apache.poi.hslf.record.Notes;
 import org.apache.poi.hslf.record.PersistPtrHolder;
 import org.apache.poi.hslf.record.PositionDependentRecord;
 import org.apache.poi.hslf.record.PositionDependentRecordContainer;
 import org.apache.poi.hslf.record.Record;
 import org.apache.poi.hslf.record.RecordContainer;
 import org.apache.poi.hslf.record.RecordTypes;
+import org.apache.poi.hslf.record.Slide;
 import org.apache.poi.hslf.record.SlideListWithText;
 import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
 import org.apache.poi.hslf.record.SlidePersistAtom;
@@ -120,12 +123,6 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
        private POILogger logger = POILogFactory.getLogger(this.getClass());
 
 
-       /* ===============================================================
-        *                       Setup Code
-        * ===============================================================
-        */
-
-
        /**
         * Constructs a Powerpoint document from the underlying
         * HSLFSlideShow object. Finds the model stuff from this
@@ -338,138 +335,133 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
                // We always use the latest versions of these records, and use the
                // SlideAtom/NotesAtom to match them with the StyleAtomSet
 
-               SlideListWithText masterSLWT = _documentRecord.getMasterSlideListWithText();
-               SlideListWithText slidesSLWT = _documentRecord.getSlideSlideListWithText();
-               SlideListWithText notesSLWT = _documentRecord.getNotesSlideListWithText();
-
-               // Find master slides
-               // These can be MainMaster records, but oddly they can also be
-               // Slides or Notes, and possibly even other odd stuff....
-               // About the only thing you can say is that the master details are in
-               // the first SLWT.
-               if (masterSLWT != null) {
-                       for (SlideAtomsSet sas : masterSLWT.getSlideAtomsSets()) {
-                               Record r = getCoreRecordForSAS(sas);
-                               int sheetNo = sas.getSlidePersistAtom().getSlideIdentifier();
-                               if (r instanceof org.apache.poi.hslf.record.Slide) {
-                                       HSLFTitleMaster master = new HSLFTitleMaster((org.apache.poi.hslf.record.Slide) r,
-                                                       sheetNo);
-                                       master.setSlideShow(this);
-                                       _titleMasters.add(master);
-                               } else if (r instanceof org.apache.poi.hslf.record.MainMaster) {
-                                       HSLFSlideMaster master = new HSLFSlideMaster((org.apache.poi.hslf.record.MainMaster) r,
-                                                       sheetNo);
-                                       master.setSlideShow(this);
-                                       _masters.add(master);
-                               }
-                       }
-               }
-
+               findMasterSlides();
+               
                // Having sorted out the masters, that leaves the notes and slides
+        Map<Integer,Integer> slideIdToNotes = new HashMap<Integer,Integer>();
 
-               // Start by finding the notes records to go with the entries in
-               // notesSLWT
-               org.apache.poi.hslf.record.Notes[] notesRecords;
-               Map<Integer,Integer> slideIdToNotes = new HashMap<Integer,Integer>();
-               if (notesSLWT == null) {
-                       // None
-                       notesRecords = new org.apache.poi.hslf.record.Notes[0];
-               } else {
-                       // Match up the records and the SlideAtomSets
-                       List<org.apache.poi.hslf.record.Notes> notesRecordsL = 
-                          new ArrayList<org.apache.poi.hslf.record.Notes>();
-                       int idx = -1;
-                       for (SlideAtomsSet notesSet : notesSLWT.getSlideAtomsSets()) {
-                           idx++;
-                               // Get the right core record
-                               Record r = getCoreRecordForSAS(notesSet);
-                SlidePersistAtom spa = notesSet.getSlidePersistAtom();
-
-                               String loggerLoc = "A Notes SlideAtomSet at "+idx+" said its record was at refID "+spa.getRefID();
-                                       
-                               // Ensure it really is a notes record
-                               if (r == null || r instanceof org.apache.poi.hslf.record.Notes) {
-                                   if (r == null) {
-                           logger.log(POILogger.WARN, loggerLoc+", but that record didn't exist - record ignored.");
-                                   }
-                                   // we need to add also null-records, otherwise the index references to other existing
-                                   // don't work anymore
-                                       org.apache.poi.hslf.record.Notes notesRecord = (org.apache.poi.hslf.record.Notes) r;
-                                       notesRecordsL.add(notesRecord);
-
-                                       // Record the match between slide id and these notes
-                                       int slideId = spa.getSlideIdentifier();
-                                       slideIdToNotes.put(slideId, idx);
-                               } else {
-                                       logger.log(POILogger.ERROR, loggerLoc+", but that was actually a " + r);
-                               }
-                       }
-                       notesRecords = new org.apache.poi.hslf.record.Notes[notesRecordsL.size()];
-                       notesRecords = notesRecordsL.toArray(notesRecords);
-               }
+        // Start by finding the notes records
+        findNotesSlides(slideIdToNotes);
 
                // Now, do the same thing for our slides
-               org.apache.poi.hslf.record.Slide[] slidesRecords;
-               SlideAtomsSet[] slidesSets = new SlideAtomsSet[0];
-               if (slidesSLWT == null) {
-                       // None
-                       slidesRecords = new org.apache.poi.hslf.record.Slide[0];
-               } else {
-                       // Match up the records and the SlideAtomSets
-                       slidesSets = slidesSLWT.getSlideAtomsSets();
-                       slidesRecords = new org.apache.poi.hslf.record.Slide[slidesSets.length];
-                       for (int i = 0; i < slidesSets.length; i++) {
-                               // Get the right core record
-                               Record r = getCoreRecordForSAS(slidesSets[i]);
-
-                               // Ensure it really is a slide record
-                               if (r instanceof org.apache.poi.hslf.record.Slide) {
-                                       slidesRecords[i] = (org.apache.poi.hslf.record.Slide) r;
-                               } else {
-                                       logger.log(POILogger.ERROR, "A Slide SlideAtomSet at " + i
-                                                       + " said its record was at refID "
-                                                       + slidesSets[i].getSlidePersistAtom().getRefID()
-                                                       + ", but that was actually a " + r);
-                               }
-                       }
-               }
+               findSlides(slideIdToNotes);
+       }
 
-               // Finally, generate model objects for everything
-               // Notes first
-               for (org.apache.poi.hslf.record.Notes n : notesRecords) {
-                   HSLFNotes hn = null;
-                   if (n != null) {
-                   hn = new HSLFNotes(n);
-                   hn.setSlideShow(this);
-                   }
-                   _notes.add(hn);
-               }
-               // Then slides
-               for (int i = 0; i < slidesRecords.length; i++) {
-                       SlideAtomsSet sas = slidesSets[i];
-                       int slideIdentifier = sas.getSlidePersistAtom().getSlideIdentifier();
-
-                       // Do we have a notes for this?
-                       HSLFNotes notes = null;
-                       // Slide.SlideAtom.notesId references the corresponding notes slide.
-                       // 0 if slide has no notes.
-                       int noteId = slidesRecords[i].getSlideAtom().getNotesID();
-                       if (noteId != 0) {
-                               Integer notesPos = slideIdToNotes.get(noteId);
-                               if (notesPos != null) {
-                                       notes = _notes.get(notesPos);
-                               } else {
-                                       logger.log(POILogger.ERROR, "Notes not found for noteId=" + noteId);
-                               }
-                       }
+       /**
+     * Find master slides
+     * These can be MainMaster records, but oddly they can also be
+     * Slides or Notes, and possibly even other odd stuff....
+     * About the only thing you can say is that the master details are in the first SLWT.
+        */
+       private void findMasterSlides() {
+        SlideListWithText masterSLWT = _documentRecord.getMasterSlideListWithText();
+        if (masterSLWT == null) {
+            return;
+        }
+        
+        for (SlideAtomsSet sas : masterSLWT.getSlideAtomsSets()) {
+            Record r = getCoreRecordForSAS(sas);
+            int sheetNo = sas.getSlidePersistAtom().getSlideIdentifier();
+            if (r instanceof Slide) {
+                HSLFTitleMaster master = new HSLFTitleMaster((Slide)r, sheetNo);
+                master.setSlideShow(this);
+                _titleMasters.add(master);
+            } else if (r instanceof MainMaster) {
+                HSLFSlideMaster master = new HSLFSlideMaster((MainMaster)r, sheetNo);
+                master.setSlideShow(this);
+                _masters.add(master);
+            }
+        }
+       }
+       
+       private void findNotesSlides(Map<Integer,Integer> slideIdToNotes) {
+        SlideListWithText notesSLWT = _documentRecord.getNotesSlideListWithText();
 
-                       // Now, build our slide
-                       HSLFSlide hs = new HSLFSlide(slidesRecords[i], notes, sas, slideIdentifier, (i + 1));
-                       hs.setSlideShow(this);
-                       _slides.add(hs);
-               }
+        if (notesSLWT == null) {
+            return;
+        }
+        
+        // Match up the records and the SlideAtomSets
+        int idx = -1;
+        for (SlideAtomsSet notesSet : notesSLWT.getSlideAtomsSets()) {
+            idx++;
+            // Get the right core record
+            Record r = getCoreRecordForSAS(notesSet);
+            SlidePersistAtom spa = notesSet.getSlidePersistAtom();
+
+            String loggerLoc = "A Notes SlideAtomSet at "+idx+" said its record was at refID "+spa.getRefID();
+
+            // we need to add null-records, otherwise the index references to other existing don't work anymore
+            if (r == null) {
+                logger.log(POILogger.WARN, loggerLoc+", but that record didn't exist - record ignored.");
+                continue;
+            }
+
+            // Ensure it really is a notes record
+            if (!(r instanceof Notes)) {
+                logger.log(POILogger.ERROR, loggerLoc+", but that was actually a " + r);
+                continue;
+            }
+            
+            Notes notesRecord = (Notes) r;
+
+            // Record the match between slide id and these notes
+            int slideId = spa.getSlideIdentifier();
+            slideIdToNotes.put(slideId, idx);
+            
+            HSLFNotes hn = new HSLFNotes(notesRecord);
+            hn.setSlideShow(this);
+            _notes.add(hn);
+        }
        }
 
+       private void findSlides(Map<Integer,Integer> slideIdToNotes) {
+        SlideListWithText slidesSLWT = _documentRecord.getSlideSlideListWithText();
+        if (slidesSLWT == null) {
+            return;
+        }
+
+        // Match up the records and the SlideAtomSets
+        int idx = -1;
+        for (SlideAtomsSet sas : slidesSLWT.getSlideAtomsSets()) {
+            idx++;
+            // Get the right core record
+            SlidePersistAtom spa = sas.getSlidePersistAtom();
+            Record r = getCoreRecordForSAS(sas);
+
+            // Ensure it really is a slide record
+            if (!(r instanceof Slide)) {
+                logger.log(POILogger.ERROR, "A Slide SlideAtomSet at " + idx
+                        + " said its record was at refID "
+                        + spa.getRefID()
+                        + ", but that was actually a " + r);
+                continue;
+            }
+            
+            Slide slide = (Slide)r;
+
+            // Do we have a notes for this?
+            HSLFNotes notes = null;
+            // Slide.SlideAtom.notesId references the corresponding notes slide.
+            // 0 if slide has no notes.
+            int noteId = slide.getSlideAtom().getNotesID();
+            if (noteId != 0) {
+                Integer notesPos = slideIdToNotes.get(noteId);
+                if (notesPos != null && 0 <= notesPos && notesPos < _notes.size()) {
+                    notes = _notes.get(notesPos);
+                } else {
+                    logger.log(POILogger.ERROR, "Notes not found for noteId=" + noteId);
+                }
+            }
+
+            // Now, build our slide
+            int slideIdentifier = spa.getSlideIdentifier();
+            HSLFSlide hs = new HSLFSlide(slide, notes, sas, slideIdentifier, (idx + 1));
+            hs.setSlideShow(this);
+            _slides.add(hs);
+        }
+       }
+       
        @Override
        public void write(OutputStream out) throws IOException {
            // check for text paragraph modifications
@@ -512,12 +504,6 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
         }
        }
 
-       /*
-        * ===============================================================
-        *                         Accessor Code
-        * ===============================================================
-        */
-
        /**
         * Returns an array of the most recent version of all the interesting
         * records
@@ -604,12 +590,6 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
                return _documentRecord;
        }
 
-       /*
-        * ===============================================================
-        * Re-ordering Code
-        * ===============================================================
-        */
-
        /**
         * Re-orders a slide, to a new position.
         *
@@ -720,12 +700,6 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
                return removedSlide;
        }
 
-       /*
-        * ===============================================================
-        *  Addition Code
-        * ===============================================================
-        */
-
        /**
         * Create a blank <code>Slide</code>.
         *
@@ -786,7 +760,7 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
                                + " and identifier " + sp.getSlideIdentifier());
 
                // Add the core records for this new Slide to the record tree
-               org.apache.poi.hslf.record.Slide slideRecord = slide.getSlideRecord();
+               Slide slideRecord = slide.getSlideRecord();
                int psrId = addPersistentObject(slideRecord);
                sp.setRefID(psrId);
                slideRecord.setSheetId(psrId);
@@ -1049,7 +1023,7 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
         } else {
             ctrl.setLinkURL(link.getAddress());
         }
-               ctrl.setLinkTitle(link.getTitle());
+               ctrl.setLinkTitle(link.getLabel());
 
                int objectId = addToObjListAtom(ctrl);
                link.setId(objectId);
index f1443ccfe01cfd41ea72b19ae89ec73af6fe1209..ceb4853e30899a9a3c25a5d74f6b2223ef2fc05b 100644 (file)
@@ -102,6 +102,8 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
     private StyleTextProp9Atom styleTextProp9Atom;\r
 \r
     private boolean _dirty = false;\r
+    \r
+    private final List<HSLFTextParagraph> parentList;\r
 \r
     /**\r
     * Constructs a Text Run from a Unicode text block.\r
@@ -110,11 +112,13 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
     * @param tha the TextHeaderAtom that defines what's what\r
     * @param tba the TextBytesAtom containing the text or null if {@link TextCharsAtom} is provided\r
     * @param tca the TextCharsAtom containing the text or null if {@link TextBytesAtom} is provided\r
+    * @param parentList the list which contains this paragraph\r
      */\r
     /* package */ HSLFTextParagraph(\r
         TextHeaderAtom tha,\r
         TextBytesAtom tba,\r
-        TextCharsAtom tca\r
+        TextCharsAtom tca,\r
+        List<HSLFTextParagraph> parentList\r
     ) {\r
         if (tha == null) {\r
             throw new IllegalArgumentException("TextHeaderAtom must be set.");\r
@@ -122,6 +126,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
         _headerAtom = tha;\r
         _byteAtom = tba;\r
         _charAtom = tca;\r
+        this.parentList = parentList;\r
     }\r
 \r
     /* package */HSLFTextParagraph(HSLFTextParagraph other) {\r
@@ -133,6 +138,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
         _ruler = other._ruler;\r
         shapeId = other.shapeId;\r
         _paragraphStyle.copy(other._paragraphStyle);\r
+        parentList = other.parentList;\r
     }\r
 \r
     public void addTextRun(HSLFTextRun run) {\r
@@ -168,7 +174,23 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
      * Supply the Sheet we belong to, which might have an assigned SlideShow\r
      * Also passes it on to our child RichTextRuns\r
      */\r
-    public void supplySheet(HSLFSheet sheet) {\r
+    public static void supplySheet(List<HSLFTextParagraph> paragraphs, HSLFSheet sheet) {\r
+        if (paragraphs == null) {\r
+            return;\r
+        }\r
+        for (HSLFTextParagraph p : paragraphs) {\r
+            p.supplySheet(sheet);\r
+        }\r
+\r
+        assert(sheet.getSlideShow() != null);\r
+        applyHyperlinks(paragraphs);\r
+    }\r
+    \r
+    /**\r
+     * Supply the Sheet we belong to, which might have an assigned SlideShow\r
+     * Also passes it on to our child RichTextRuns\r
+     */\r
+    private void supplySheet(HSLFSheet sheet) {\r
         this._sheet = sheet;\r
 \r
         if (_runs == null) return;\r
@@ -942,7 +964,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
             if (addParagraph && !lastParaEmpty) {\r
                 TextPropCollection tpc = htp.getParagraphStyle();\r
                 HSLFTextParagraph prevHtp = htp;\r
-                htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom);\r
+                htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom, paragraphs);\r
                 htp.getParagraphStyle().copy(tpc);\r
                 htp.setParentShape(prevHtp.getParentShape());\r
                 htp.setShapeId(prevHtp.getShapeId());\r
@@ -1211,7 +1233,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
 \r
             // split, but keep delimiter\r
             for (String para : rawText.split("(?<=\r)")) {\r
-                HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars);\r
+                HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars, paragraphs);\r
                 paragraphs.add(tpara);\r
                 tpara._ruler = ruler;\r
                 tpara.getParagraphStyle().updateTextSize(para.length());\r
@@ -1235,6 +1257,22 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
         return paragraphCollection;\r
     }\r
 \r
+    protected static void applyHyperlinks(List<HSLFTextParagraph> paragraphs) {\r
+        List<HSLFHyperlink> links = HSLFHyperlink.find(paragraphs);\r
+        \r
+        for (HSLFHyperlink h : links) {\r
+            int csIdx = 0;\r
+            for (HSLFTextParagraph p : paragraphs) {\r
+                for (HSLFTextRun r : p) {\r
+                    if (h.getStartIndex() <= csIdx && csIdx < h.getEndIndex()) {\r
+                        r.setHyperlinkId(h.getId());\r
+                    }\r
+                    csIdx += r.getLength();\r
+                }\r
+            }\r
+        }\r
+    }\r
+    \r
     protected static void applyCharacterStyles(List<HSLFTextParagraph> paragraphs, List<TextPropCollection> charStyles) {\r
         int paraIdx = 0, runIdx = 0;\r
         HSLFTextRun trun;\r
@@ -1340,15 +1378,17 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
         TextPropCollection charStyle = sta.addCharacterTextPropCollection(1);\r
         wrapper.appendChildRecord(sta);\r
 \r
-        HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null);\r
+        List<HSLFTextParagraph> paragraphs = new ArrayList<HSLFTextParagraph>(1);\r
+        HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null, paragraphs);\r
         htp.setParagraphStyle(paraStyle);\r
+        paragraphs.add(htp);\r
 \r
         HSLFTextRun htr = new HSLFTextRun(htp);\r
         htr.setCharacterStyle(charStyle);\r
         htr.setText("");\r
         htp.addTextRun(htr);\r
 \r
-        return Arrays.asList(htp);\r
+        return paragraphs;\r
     }\r
 \r
     public EscherTextboxWrapper getTextboxWrapper() {\r
@@ -1397,4 +1437,23 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
     public boolean isDirty() {\r
         return _dirty;\r
     }\r
+    \r
+    /**\r
+     * Calculates the start index of the given text run\r
+     *\r
+     * @param textrun the text run to search for\r
+     * @return the start index with the paragraph collection or -1 if not found\r
+     */\r
+    /* package */ int getStartIdxOfTextRun(HSLFTextRun textrun) {\r
+        int idx = 0;\r
+        for (HSLFTextParagraph p : parentList) {\r
+            for (HSLFTextRun r : p) {\r
+                if (r == textrun) {\r
+                    return idx;\r
+                }\r
+                idx += r.getLength();\r
+            }\r
+        }\r
+        return -1;\r
+    }\r
 }
\ No newline at end of file
index 437fb745b8ad9396417f9168dcf41aafeef13df7..62cd072f214cbcbd29f5e1071649b645ff89f80f 100644 (file)
@@ -27,6 +27,10 @@ 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;
@@ -47,6 +51,7 @@ public final class HSLFTextRun implements TextRun {
        private HSLFTextParagraph parentParagraph;
        private String _runText = "";
        private String _fontFamily;
+       private int hyperlinkId = -1;
        
        /**
         * Our paragraph and character style.
@@ -390,4 +395,64 @@ 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
+     *
+     * @param hyperlink the id or -1 to unset it
+     */
+    public void setHyperlinkId(int hyperlinkId) {
+        this.hyperlinkId = hyperlinkId;
+    }
+    
+    /**
+     * Returns the associated hyperlink id
+     *
+     * @return the hyperlink id
+     */
+    public int getHyperlinkId() {
+        return hyperlinkId;
+    }
+    
+
+    @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;
+        }
+        
+        // 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;
+    }
 }
index 88e5d80016946442cd8eaecc4e2a18d1eae23a9e..fbe91040e8c5117f6bb95fb5c1cc29d0aedc5ddf 100644 (file)
@@ -594,13 +594,8 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
         // (We can't do it in the constructor because the sheet
         //  is not assigned then, it's only built once we have
         //  all the records)
-        List<HSLFTextParagraph> paras = getTextParagraphs();
-        if (paras != null) {
-            for (HSLFTextParagraph htp : paras) {
-                // Supply the sheet to our child RichTextRuns
-                htp.supplySheet(_sheet);
-            }
-        }
+        List<HSLFTextParagraph> ltp = getTextParagraphs();
+        HSLFTextParagraph.supplySheet(ltp, sheet);
     }
 
     /**
index 5429b96fbc718660e3acafd2ea32a8454d72e8a9..715640a1d59419ffda68d857dc90e6bd00e49dac 100644 (file)
@@ -57,11 +57,11 @@ public final class TestHyperlink {
         assertNotNull(links);
         assertEquals(2, links.size());
 
-        assertEquals("http://jakarta.apache.org/poi/", links.get(0).getTitle());
+        assertEquals("http://jakarta.apache.org/poi/", links.get(0).getLabel());
         assertEquals("http://jakarta.apache.org/poi/", links.get(0).getAddress());
         assertEquals("http://jakarta.apache.org/poi/", rawText.substring(links.get(0).getStartIndex(), links.get(0).getEndIndex()-1));
 
-        assertEquals("http://slashdot.org/", links.get(1).getTitle());
+        assertEquals("http://slashdot.org/", links.get(1).getLabel());
         assertEquals("http://slashdot.org/", links.get(1).getAddress());
         assertEquals("http://slashdot.org/", rawText.substring(links.get(1).getStartIndex(), links.get(1).getEndIndex()-1));
 
@@ -77,7 +77,7 @@ public final class TestHyperlink {
         assertNotNull(links);
         assertEquals(1, links.size());
 
-        assertEquals("http://jakarta.apache.org/poi/hssf/", links.get(0).getTitle());
+        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));
     }
index 2c166ebf1f8c5806620e38311efe79c8062b21d5..e3b2683e531a054b923b27db053f0db9b28afd63 100644 (file)
@@ -26,6 +26,10 @@ import static org.junit.Assert.assertTrue;
 import java.awt.Color;
 import java.io.File;
 import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.text.AttributedCharacterIterator;
+import java.text.AttributedCharacterIterator.Attribute;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
@@ -47,7 +51,9 @@ import org.apache.poi.hslf.record.Record;
 import org.apache.poi.hslf.record.SlideListWithText;
 import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
 import org.apache.poi.hslf.record.TextHeaderAtom;
+import org.apache.poi.hssf.usermodel.DummyGraphics2d;
 import org.apache.poi.sl.draw.DrawPaint;
+import org.apache.poi.sl.draw.DrawTextParagraph;
 import org.apache.poi.sl.usermodel.PaintStyle;
 import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
 import org.apache.poi.sl.usermodel.PictureData.PictureType;
@@ -67,8 +73,6 @@ import org.junit.Test;
 /**
  * Testcases for bugs entered in bugzilla
  * the Test name contains the bugzilla bug id
- *
- * @author Yegor Kozlov
  */
 public final class TestBugs {
     /**
@@ -822,6 +826,59 @@ public final class TestBugs {
         
         ppt.close();
     }
+
+    @Test
+    public void bug57796() throws IOException {
+        HSLFSlideShow ppt = open("WithLinks.ppt");
+        HSLFSlide slide = ppt.getSlides().get(0);
+        HSLFTextShape shape = (HSLFTextShape)slide.getShapes().get(1);
+        List<HSLFHyperlink> hlList =  HSLFHyperlink.find(shape);
+        HSLFHyperlink hlShape = hlList.get(0);
+        HSLFTextRun r = shape.getTextParagraphs().get(1).getTextRuns().get(0);
+        HSLFHyperlink hlRun = r.getHyperlink();
+        assertEquals(hlRun.getId(), hlShape.getId());
+        assertEquals(hlRun.getAddress(), hlShape.getAddress());
+        assertEquals(hlRun.getLabel(), hlShape.getLabel());
+        assertEquals(hlRun.getType(), hlShape.getType());
+        assertEquals(hlRun.getStartIndex(), hlShape.getStartIndex());
+        assertEquals(hlRun.getEndIndex(), hlShape.getEndIndex());
+
+        OutputStream nullOutput = new OutputStream(){
+            public void write(int b) throws IOException {}
+        };
+        
+        final boolean found[] = { false }; 
+        DummyGraphics2d dgfx = new DummyGraphics2d(new PrintStream(nullOutput)){
+            public void drawString(AttributedCharacterIterator iterator, float x, float y) {
+                // For the test file, common sl draws textruns one by one and not mixed
+                // so we evaluate the whole iterator
+                Map<Attribute, Object> attributes = null;
+                StringBuffer sb = new StringBuffer();
+                
+                for (char c = iterator.first();
+                        c != AttributedCharacterIterator.DONE;
+                        c = iterator.next()) {
+                    sb.append(c);
+                    attributes = iterator.getAttributes();
+                }
+
+                if ("Jakarta HSSF".equals(sb.toString())) {
+                    // this is a test for a manually modified ppt, for real hyperlink label
+                    // one would need to access the screen tip record
+                    String href = (String)attributes.get(DrawTextParagraph.HYPERLINK_HREF);
+                    String label = (String)attributes.get(DrawTextParagraph.HYPERLINK_LABEL);
+                    assertEquals("http://jakarta.apache.org/poi/hssf/", href);
+                    assertEquals("Open Jakarta POI HSSF module test  ", label);
+                    found[0] = true;
+                }
+            }
+        };
+        
+        ppt.getSlides().get(1).draw(dgfx);
+        assertTrue(found[0]);
+        
+        ppt.close();
+    }
     
     private static HSLFSlideShow open(String fileName) throws IOException {
         File sample = HSLFTestDataSamples.getSampleFile(fileName);
index f08a81a09b78855b92cfceb0fe16f173a3c3820d..80d2e6dbb4ae08193e10f9c506c9860fbabdbb44 100644 (file)
Binary files a/test-data/slideshow/WithLinks.ppt and b/test-data/slideshow/WithLinks.ppt differ