#57796 - Support hyperlink extraction when rendering slides

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1724338 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2016-01-12 23:20:48 +00:00
parent ef61211207
commit b0c16b8261
20 changed files with 459 additions and 250 deletions

View File

@ -46,7 +46,7 @@ public abstract class CreateHyperlink {
String text = textBox1.getText(); String text = textBox1.getText();
HSLFHyperlink link = new HSLFHyperlink(); HSLFHyperlink link = new HSLFHyperlink();
link.setAddress("http://www.apache.org"); link.setAddress("http://www.apache.org");
link.setTitle(textBox1.getText()); link.setLabel(textBox1.getText());
int linkId = ppt.addHyperlink(link); int linkId = ppt.addHyperlink(link);
// apply link to the text // apply link to the text

View File

@ -70,6 +70,6 @@ public final class Hyperlinks {
//in ppt end index is inclusive //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 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); 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);
} }
} }

View File

@ -24,6 +24,7 @@ import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute; import java.awt.font.TextAttribute;
import java.awt.font.TextLayout; import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.io.InvalidObjectException;
import java.text.AttributedCharacterIterator; import java.text.AttributedCharacterIterator;
import java.text.AttributedCharacterIterator.Attribute; import java.text.AttributedCharacterIterator.Attribute;
import java.text.AttributedString; import java.text.AttributedString;
@ -32,6 +33,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.poi.sl.usermodel.AutoNumberingScheme; import org.apache.poi.sl.usermodel.AutoNumberingScheme;
import org.apache.poi.sl.usermodel.Hyperlink;
import org.apache.poi.sl.usermodel.Insets2D; import org.apache.poi.sl.usermodel.Insets2D;
import org.apache.poi.sl.usermodel.PaintStyle; import org.apache.poi.sl.usermodel.PaintStyle;
import org.apache.poi.sl.usermodel.PlaceableShape; import org.apache.poi.sl.usermodel.PlaceableShape;
@ -46,6 +48,10 @@ import org.apache.poi.util.StringUtil;
import org.apache.poi.util.Units; import org.apache.poi.util.Units;
public class DrawTextParagraph implements Drawable { public class DrawTextParagraph implements Drawable {
/** Keys for passing hyperlinks to the graphics context */
public static final XlinkAttribute HYPERLINK_HREF = new XlinkAttribute("href");
public static final XlinkAttribute HYPERLINK_LABEL = new XlinkAttribute("label");
protected TextParagraph<?,?,?> paragraph; protected TextParagraph<?,?,?> paragraph;
double x, y; double x, y;
protected List<DrawTextFragment> lines = new ArrayList<DrawTextFragment>(); protected List<DrawTextFragment> lines = new ArrayList<DrawTextFragment>();
@ -58,6 +64,31 @@ public class DrawTextParagraph implements Drawable {
*/ */
protected double maxLineHeight; protected double maxLineHeight;
/**
* Defines an attribute used for storing the hyperlink associated with
* some renderable text.
*/
private static class XlinkAttribute extends Attribute {
XlinkAttribute(String name) {
super(name);
}
/**
* Resolves instances being deserialized to the predefined constants.
*/
protected Object readResolve() throws InvalidObjectException {
if (HYPERLINK_HREF.getName().equals(getName())) {
return HYPERLINK_HREF;
}
if (HYPERLINK_LABEL.getName().equals(getName())) {
return HYPERLINK_LABEL;
}
throw new InvalidObjectException("unknown attribute name");
}
}
public DrawTextParagraph(TextParagraph<?,?,?> paragraph) { public DrawTextParagraph(TextParagraph<?,?,?> paragraph) {
this.paragraph = paragraph; this.paragraph = paragraph;
} }
@ -79,10 +110,10 @@ public class DrawTextParagraph implements Drawable {
public void setAutoNumberingIdx(int index) { public void setAutoNumberingIdx(int index) {
autoNbrIdx = index; autoNbrIdx = index;
} }
public void draw(Graphics2D graphics){ public void draw(Graphics2D graphics){
if (lines.isEmpty()) return; if (lines.isEmpty()) return;
double penY = y; double penY = y;
boolean firstLine = true; boolean firstLine = true;
@ -100,7 +131,7 @@ public class DrawTextParagraph implements Drawable {
// special handling for HSLF // special handling for HSLF
indent -= leftMargin; indent -= leftMargin;
} }
// Double rightMargin = paragraph.getRightMargin(); // Double rightMargin = paragraph.getRightMargin();
// if (rightMargin == null) { // if (rightMargin == null) {
// rightMargin = 0d; // rightMargin = 0d;
@ -109,7 +140,7 @@ public class DrawTextParagraph implements Drawable {
//The vertical line spacing //The vertical line spacing
Double spacing = paragraph.getLineSpacing(); Double spacing = paragraph.getLineSpacing();
if (spacing == null) spacing = 100d; if (spacing == null) spacing = 100d;
for(DrawTextFragment line : lines){ for(DrawTextFragment line : lines){
double penX; double penX;
@ -118,7 +149,7 @@ public class DrawTextParagraph implements Drawable {
// TODO: find out character style for empty, but bulleted/numbered lines // TODO: find out character style for empty, but bulleted/numbered lines
bullet = getBullet(graphics, line.getAttributedString().getIterator()); bullet = getBullet(graphics, line.getAttributedString().getIterator());
} }
if (bullet != null){ if (bullet != null){
bullet.setPosition(x+leftMargin+indent, penY); bullet.setPosition(x+leftMargin+indent, penY);
bullet.draw(graphics); bullet.draw(graphics);
@ -168,7 +199,7 @@ public class DrawTextParagraph implements Drawable {
y = penY - y; y = penY - y;
} }
public float getFirstLineHeight() { public float getFirstLineHeight() {
return (lines.isEmpty()) ? 0 : lines.get(0).getHeight(); return (lines.isEmpty()) ? 0 : lines.get(0).getHeight();
} }
@ -180,7 +211,7 @@ public class DrawTextParagraph implements Drawable {
public boolean isEmptyParagraph() { public boolean isEmptyParagraph() {
return (lines.isEmpty() || rawText.trim().isEmpty()); return (lines.isEmpty() || rawText.trim().isEmpty());
} }
public void applyTransform(Graphics2D graphics) { public void applyTransform(Graphics2D graphics) {
} }
@ -265,7 +296,7 @@ public class DrawTextParagraph implements Drawable {
String buFont = bulletStyle.getBulletFont(); String buFont = bulletStyle.getBulletFont();
if (buFont == null) buFont = paragraph.getDefaultFontFamily(); if (buFont == null) buFont = paragraph.getDefaultFontFamily();
assert(buFont != null); assert(buFont != null);
PlaceableShape<?,?> ps = getParagraphShape(); PlaceableShape<?,?> ps = getParagraphShape();
PaintStyle fgPaintStyle = bulletStyle.getBulletFontColor(); PaintStyle fgPaintStyle = bulletStyle.getBulletFontColor();
Paint fgPaint; Paint fgPaint;
@ -277,11 +308,11 @@ public class DrawTextParagraph implements Drawable {
float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE); float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE);
Double buSz = bulletStyle.getBulletFontSize(); Double buSz = bulletStyle.getBulletFontSize();
if (buSz == null) buSz = 100d; if (buSz == null) buSz = 100d;
if (buSz > 0) fontSize *= buSz* 0.01; if (buSz > 0) fontSize *= buSz* 0.01;
else fontSize = (float)-buSz; else fontSize = (float)-buSz;
AttributedString str = new AttributedString(mapFontCharset(buCharacter,buFont)); AttributedString str = new AttributedString(mapFontCharset(buCharacter,buFont));
str.addAttribute(TextAttribute.FOREGROUND, fgPaint); str.addAttribute(TextAttribute.FOREGROUND, fgPaint);
str.addAttribute(TextAttribute.FAMILY, buFont); str.addAttribute(TextAttribute.FAMILY, buFont);
@ -313,7 +344,7 @@ public class DrawTextParagraph implements Drawable {
case SMALL: c = Character.toLowerCase(c); break; case SMALL: c = Character.toLowerCase(c); break;
case NONE: break; case NONE: break;
} }
buf.append(c); buf.append(c);
break; break;
} }
@ -321,7 +352,7 @@ public class DrawTextParagraph implements Drawable {
return buf.toString(); return buf.toString();
} }
/** /**
* Replace a tab with the effective number of white spaces. * Replace a tab with the effective number of white spaces.
*/ */
@ -334,7 +365,7 @@ public class DrawTextParagraph implements Drawable {
Double fs = tr.getFontSize(); Double fs = tr.getFontSize();
if (fs == null) fs = 12d; if (fs == null) fs = 12d;
string.addAttribute(TextAttribute.SIZE, fs.floatValue()); string.addAttribute(TextAttribute.SIZE, fs.floatValue());
TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true)); TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true));
double wspace = l.getAdvance(); double wspace = l.getAdvance();
@ -348,7 +379,7 @@ public class DrawTextParagraph implements Drawable {
} }
return buf.toString(); return buf.toString();
} }
/** /**
* Returns wrapping width to break lines in this paragraph * Returns wrapping width to break lines in this paragraph
@ -418,7 +449,7 @@ public class DrawTextParagraph implements Drawable {
this.endIndex = endIndex; this.endIndex = endIndex;
} }
} }
/** /**
* Helper method for paint style relative to bounds, e.g. gradient paint * Helper method for paint style relative to bounds, e.g. gradient paint
*/ */
@ -443,7 +474,7 @@ public class DrawTextParagraph implements Drawable {
if (text == null) text = new StringBuilder(); if (text == null) text = new StringBuilder();
PlaceableShape<?,?> ps = getParagraphShape(); PlaceableShape<?,?> ps = getParagraphShape();
DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER); DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER);
for (TextRun run : paragraph){ for (TextRun run : paragraph){
@ -464,7 +495,7 @@ public class DrawTextParagraph implements Drawable {
if (fontFamily == null) { if (fontFamily == null) {
fontFamily = paragraph.getDefaultFontFamily(); fontFamily = paragraph.getDefaultFontFamily();
} }
int beginIndex = text.length(); int beginIndex = text.length();
text.append(mapFontCharset(runText,fontFamily)); text.append(mapFontCharset(runText,fontFamily));
int endIndex = text.length(); int endIndex = text.length();
@ -498,6 +529,12 @@ public class DrawTextParagraph implements Drawable {
if(run.isSuperscript()) { if(run.isSuperscript()) {
attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, beginIndex, endIndex)); attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, beginIndex, endIndex));
} }
Hyperlink hl = run.getHyperlink();
if (hl != null) {
attList.add(new AttributedStringData(HYPERLINK_HREF, hl.getAddress(), beginIndex, endIndex));
attList.add(new AttributedStringData(HYPERLINK_LABEL, hl.getLabel(), beginIndex, endIndex));
}
} }
// ensure that the paragraph contains at least one character // ensure that the paragraph contains at least one character
@ -517,9 +554,9 @@ public class DrawTextParagraph implements Drawable {
} }
protected boolean isHSLF() { protected boolean isHSLF() {
return paragraph.getClass().getName().contains("HSLF"); return paragraph.getClass().getName().contains("HSLF");
} }
/** /**
* Map text charset depending on font family. * Map text charset depending on font family.
* Currently this only maps for wingdings font (into unicode private use area) * Currently this only maps for wingdings font (into unicode private use area)
@ -527,7 +564,7 @@ public class DrawTextParagraph implements Drawable {
* @param text the raw text * @param text the raw text
* @param fontFamily the font family * @param fontFamily the font family
* @return AttributedString with mapped codepoints * @return AttributedString with mapped codepoints
* *
* @see <a href="http://stackoverflow.com/questions/8692095">Drawing exotic fonts in a java applet</a> * @see <a href="http://stackoverflow.com/questions/8692095">Drawing exotic fonts in a java applet</a>
* @see StringUtil#mapMsCodepointString(String) * @see StringUtil#mapMsCodepointString(String)
*/ */

View File

@ -156,4 +156,11 @@ public interface TextRun {
* @return the pitch and family id or -1 if not applicable * @return the pitch and family id or -1 if not applicable
*/ */
byte getPitchAndFamily(); byte getPitchAndFamily();
/**
* Return the associated hyperlink
*
* @return the associated hyperlink or null if no hyperlink was set
*/
Hyperlink getHyperlink();
} }

View File

@ -18,15 +18,13 @@ package org.apache.poi.xslf.usermodel;
import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.TargetMode; import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.sl.usermodel.Hyperlink;
import org.apache.poi.util.Internal; import org.apache.poi.util.Internal;
import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink; import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink;
import java.net.URI; import java.net.URI;
/** public class XSLFHyperlink implements Hyperlink {
* @author Yegor Kozlov
*/
public class XSLFHyperlink {
final XSLFTextRun _r; final XSLFTextRun _r;
final CTHyperlink _link; final CTHyperlink _link;
@ -40,15 +38,39 @@ public class XSLFHyperlink {
return _link; return _link;
} }
@Override
public void setAddress(String address){ public void setAddress(String address){
XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet(); XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet();
PackageRelationship rel = PackageRelationship rel =
sheet.getPackagePart(). sheet.getPackagePart().
addExternalRelationship(address, XSLFRelation.HYPERLINK.getRelation()); addExternalRelationship(address, XSLFRelation.HYPERLINK.getRelation());
_link.setId(rel.getId()); _link.setId(rel.getId());
}
@Override
public String getAddress() {
return getTargetURI().toASCIIString();
} }
@Override
public String getLabel() {
return _link.getTooltip();
}
@Override
public void setLabel(String label) {
_link.setTooltip(label);
}
@Override
public int getType() {
// TODO: currently this just returns nonsense
if ("ppaction://hlinksldjump".equals(_link.getAction())) {
return LINK_DOCUMENT;
}
return LINK_URL;
}
public void setAddress(XSLFSlide slide){ public void setAddress(XSLFSlide slide){
XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet(); XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet();
PackageRelationship rel = PackageRelationship rel =

View File

@ -449,6 +449,7 @@ public class XSLFTextRun implements TextRun {
return link; return link;
} }
@Override
public XSLFHyperlink getHyperlink(){ public XSLFHyperlink getHyperlink(){
if(!_r.getRPr().isSetHlinkClick()) return null; if(!_r.getRPr().isSetHlinkClick()) return null;

View File

@ -282,7 +282,7 @@ public final class PPGraphics2D extends Graphics2D implements Cloneable {
*/ */
public void drawString(String s, float x, float y) { public void drawString(String s, float x, float y) {
HSLFTextBox txt = new HSLFTextBox(_group); HSLFTextBox txt = new HSLFTextBox(_group);
txt.getTextParagraphs().get(0).supplySheet(_group.getSheet()); txt.setSheet(_group.getSheet());
txt.setText(s); txt.setText(s);
HSLFTextRun rt = txt.getTextParagraphs().get(0).getTextRuns().get(0); HSLFTextRun rt = txt.getTextParagraphs().get(0).getTextRuns().get(0);

View 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.InteractiveInfoAtom;
import org.apache.poi.hslf.record.Record; import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.TxInteractiveInfoAtom; import org.apache.poi.hslf.record.TxInteractiveInfoAtom;
import org.apache.poi.sl.usermodel.Hyperlink;
/** /**
* Represents a hyperlink in a PowerPoint document * 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_NEXTSLIDE = InteractiveInfoAtom.LINK_NextSlide;
public static final byte LINK_PREVIOUSSLIDE = InteractiveInfoAtom.LINK_PreviousSlide; public static final byte LINK_PREVIOUSSLIDE = InteractiveInfoAtom.LINK_PreviousSlide;
public static final byte LINK_FIRSTSLIDE = InteractiveInfoAtom.LINK_FirstSlide; public static final byte LINK_FIRSTSLIDE = InteractiveInfoAtom.LINK_FirstSlide;
@ -47,7 +46,7 @@ public final class HSLFHyperlink {
private int id=-1; private int id=-1;
private int type; private int type;
private String address; private String address;
private String title; private String label;
private int startIndex, endIndex; private int startIndex, endIndex;
/** /**
@ -57,6 +56,7 @@ public final class HSLFHyperlink {
* @return the hyperlink URL * @return the hyperlink URL
* @see InteractiveInfoAtom * @see InteractiveInfoAtom
*/ */
@Override
public int getType() { public int getType() {
return type; return type;
} }
@ -65,35 +65,31 @@ public final class HSLFHyperlink {
type = val; type = val;
switch(type){ switch(type){
case LINK_NEXTSLIDE: case LINK_NEXTSLIDE:
title = "NEXT"; label = "NEXT";
address = "1,-1,NEXT"; address = "1,-1,NEXT";
break; break;
case LINK_PREVIOUSSLIDE: case LINK_PREVIOUSSLIDE:
title = "PREV"; label = "PREV";
address = "1,-1,PREV"; address = "1,-1,PREV";
break; break;
case LINK_FIRSTSLIDE: case LINK_FIRSTSLIDE:
title = "FIRST"; label = "FIRST";
address = "1,-1,FIRST"; address = "1,-1,FIRST";
break; break;
case LINK_LASTSLIDE: case LINK_LASTSLIDE:
title = "LAST"; label = "LAST";
address = "1,-1,LAST"; address = "1,-1,LAST";
break; break;
case LINK_SLIDENUMBER: case LINK_SLIDENUMBER:
break; break;
default: default:
title = ""; label = "";
address = ""; address = "";
break; break;
} }
} }
/** @Override
* Gets the hyperlink URL
*
* @return the hyperlink URL
*/
public String getAddress() { public String getAddress() {
return address; return address;
} }
@ -101,10 +97,11 @@ public final class HSLFHyperlink {
public void setAddress(HSLFSlide slide) { public void setAddress(HSLFSlide slide) {
String href = slide._getSheetNumber() + ","+slide.getSlideNumber()+",Slide " + slide.getSlideNumber(); String href = slide._getSheetNumber() + ","+slide.getSlideNumber()+",Slide " + slide.getSlideNumber();
setAddress(href);; setAddress(href);;
setTitle("Slide " + slide.getSlideNumber()); setLabel("Slide " + slide.getSlideNumber());
setType(HSLFHyperlink.LINK_SLIDENUMBER); setType(HSLFHyperlink.LINK_SLIDENUMBER);
} }
@Override
public void setAddress(String str) { public void setAddress(String str) {
address = str; address = str;
} }
@ -117,17 +114,14 @@ public final class HSLFHyperlink {
this.id = id; this.id = id;
} }
/** @Override
* Gets the hyperlink user-friendly title (if different from URL) public String getLabel() {
* return label;
* @return the hyperlink user-friendly title
*/
public String getTitle() {
return title;
} }
public void setTitle(String str) { @Override
title = str; public void setLabel(String str) {
label = str;
} }
/** /**
@ -139,6 +133,15 @@ public final class HSLFHyperlink {
return startIndex; 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 * Gets the ending character position
* *
@ -148,6 +151,15 @@ public final class HSLFHyperlink {
return endIndex; 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 * Find hyperlinks in a text shape
* *
@ -222,9 +234,10 @@ public final class HSLFHyperlink {
} }
HSLFHyperlink link = new HSLFHyperlink(); HSLFHyperlink link = new HSLFHyperlink();
link.title = linkRecord.getLinkTitle(); link.setId(id);
link.address = linkRecord.getLinkURL(); link.setType(info.getAction());
link.type = info.getAction(); link.setLabel(linkRecord.getLinkTitle());
link.setAddress(linkRecord.getLinkURL());
out.add(link); out.add(link);
if (iter.hasNext()) { if (iter.hasNext()) {
@ -234,8 +247,8 @@ public final class HSLFHyperlink {
continue; continue;
} }
TxInteractiveInfoAtom txinfo = (TxInteractiveInfoAtom)r; TxInteractiveInfoAtom txinfo = (TxInteractiveInfoAtom)r;
link.startIndex = txinfo.getStartIndex(); link.setStartIndex(txinfo.getStartIndex());
link.endIndex = txinfo.getEndIndex(); link.setEndIndex(txinfo.getEndIndex());
} }
} }
} }

View File

@ -56,13 +56,6 @@ public final class HSLFNotes extends HSLFSheet implements Notes<HSLFShape,HSLFTe
if (_paragraphs.isEmpty()) { if (_paragraphs.isEmpty()) {
logger.log(POILogger.WARN, "No text records found for notes sheet"); 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);
}
}
} }
/** /**

View File

@ -133,9 +133,7 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheet<HSLFShape,H
List<List<HSLFTextParagraph>> trs = getTextParagraphs(); List<List<HSLFTextParagraph>> trs = getTextParagraphs();
if (trs == null) return; if (trs == null) return;
for (List<HSLFTextParagraph> ltp : trs) { for (List<HSLFTextParagraph> ltp : trs) {
for (HSLFTextParagraph tp : ltp) { HSLFTextParagraph.supplySheet(ltp, this);
tp.supplySheet(this);
}
} }
} }

View File

@ -310,7 +310,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_SlideNumber); infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_SlideNumber);
break; break;
default: default:
logger.log(POILogger.WARN, "Ignore unknown hyperlink type : "+link.getTitle()); logger.log(POILogger.WARN, "Ignore unknown hyperlink type : "+link.getLabel());
break; break;
} }

View File

@ -94,12 +94,6 @@ public final class HSLFSlide extends HSLFSheet implements Slide<HSLFShape,HSLFTe
for (List<HSLFTextParagraph> l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) { for (List<HSLFTextParagraph> l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) {
if (!_paragraphs.contains(l)) _paragraphs.add(l); if (!_paragraphs.contains(l)) _paragraphs.add(l);
} }
for(List<HSLFTextParagraph> ltp : _paragraphs) {
for (HSLFTextParagraph tp : ltp) {
tp.supplySheet(this);
}
}
} }
/** /**

View File

@ -51,12 +51,6 @@ public final class HSLFSlideMaster extends HSLFMasterSheet {
for (List<HSLFTextParagraph> l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) { for (List<HSLFTextParagraph> l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) {
if (!_paragraphs.contains(l)) _paragraphs.add(l); if (!_paragraphs.contains(l)) _paragraphs.add(l);
} }
for (List<HSLFTextParagraph> p : _paragraphs) {
for (HSLFTextParagraph htp : p) {
htp.supplySheet(this);
}
}
} }
/** /**

View File

@ -33,6 +33,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.poi.hslf.record.MainMaster;
import org.apache.poi.ddf.EscherBSERecord; import org.apache.poi.ddf.EscherBSERecord;
import org.apache.poi.ddf.EscherContainerRecord; import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.ddf.EscherOptRecord; 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.FontCollection;
import org.apache.poi.hslf.record.FontEntityAtom; import org.apache.poi.hslf.record.FontEntityAtom;
import org.apache.poi.hslf.record.HeadersFootersContainer; 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.PersistPtrHolder;
import org.apache.poi.hslf.record.PositionDependentRecord; import org.apache.poi.hslf.record.PositionDependentRecord;
import org.apache.poi.hslf.record.PositionDependentRecordContainer; import org.apache.poi.hslf.record.PositionDependentRecordContainer;
import org.apache.poi.hslf.record.Record; import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.RecordContainer; import org.apache.poi.hslf.record.RecordContainer;
import org.apache.poi.hslf.record.RecordTypes; 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;
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
import org.apache.poi.hslf.record.SlidePersistAtom; 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()); private POILogger logger = POILogFactory.getLogger(this.getClass());
/* ===============================================================
* Setup Code
* ===============================================================
*/
/** /**
* Constructs a Powerpoint document from the underlying * Constructs a Powerpoint document from the underlying
* HSLFSlideShow object. Finds the model stuff from this * 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 // We always use the latest versions of these records, and use the
// SlideAtom/NotesAtom to match them with the StyleAtomSet // SlideAtom/NotesAtom to match them with the StyleAtomSet
SlideListWithText masterSLWT = _documentRecord.getMasterSlideListWithText(); findMasterSlides();
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);
}
}
}
// Having sorted out the masters, that leaves the notes and slides // 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 // Start by finding the notes records
// notesSLWT findNotesSlides(slideIdToNotes);
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);
}
// Now, do the same thing for our slides // Now, do the same thing for our slides
org.apache.poi.hslf.record.Slide[] slidesRecords; findSlides(slideIdToNotes);
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);
}
}
}
// 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);
}
}
// Now, build our slide
HSLFSlide hs = new HSLFSlide(slidesRecords[i], notes, sas, slideIdentifier, (i + 1));
hs.setSlideShow(this);
_slides.add(hs);
}
} }
/**
* 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();
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 @Override
public void write(OutputStream out) throws IOException { public void write(OutputStream out) throws IOException {
// check for text paragraph modifications // 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 * Returns an array of the most recent version of all the interesting
* records * records
@ -604,12 +590,6 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
return _documentRecord; return _documentRecord;
} }
/*
* ===============================================================
* Re-ordering Code
* ===============================================================
*/
/** /**
* Re-orders a slide, to a new position. * Re-orders a slide, to a new position.
* *
@ -720,12 +700,6 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
return removedSlide; return removedSlide;
} }
/*
* ===============================================================
* Addition Code
* ===============================================================
*/
/** /**
* Create a blank <code>Slide</code>. * Create a blank <code>Slide</code>.
* *
@ -786,7 +760,7 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
+ " and identifier " + sp.getSlideIdentifier()); + " and identifier " + sp.getSlideIdentifier());
// Add the core records for this new Slide to the record tree // 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); int psrId = addPersistentObject(slideRecord);
sp.setRefID(psrId); sp.setRefID(psrId);
slideRecord.setSheetId(psrId); slideRecord.setSheetId(psrId);
@ -1049,7 +1023,7 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
} else { } else {
ctrl.setLinkURL(link.getAddress()); ctrl.setLinkURL(link.getAddress());
} }
ctrl.setLinkTitle(link.getTitle()); ctrl.setLinkTitle(link.getLabel());
int objectId = addToObjListAtom(ctrl); int objectId = addToObjListAtom(ctrl);
link.setId(objectId); link.setId(objectId);

View File

@ -102,6 +102,8 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
private StyleTextProp9Atom styleTextProp9Atom; private StyleTextProp9Atom styleTextProp9Atom;
private boolean _dirty = false; private boolean _dirty = false;
private final List<HSLFTextParagraph> parentList;
/** /**
* Constructs a Text Run from a Unicode text block. * Constructs a Text Run from a Unicode text block.
@ -110,11 +112,13 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
* @param tha the TextHeaderAtom that defines what's what * @param tha the TextHeaderAtom that defines what's what
* @param tba the TextBytesAtom containing the text or null if {@link TextCharsAtom} is provided * @param tba the TextBytesAtom containing the text or null if {@link TextCharsAtom} is provided
* @param tca the TextCharsAtom containing the text or null if {@link TextBytesAtom} is provided * @param tca the TextCharsAtom containing the text or null if {@link TextBytesAtom} is provided
* @param parentList the list which contains this paragraph
*/ */
/* package */ HSLFTextParagraph( /* package */ HSLFTextParagraph(
TextHeaderAtom tha, TextHeaderAtom tha,
TextBytesAtom tba, TextBytesAtom tba,
TextCharsAtom tca TextCharsAtom tca,
List<HSLFTextParagraph> parentList
) { ) {
if (tha == null) { if (tha == null) {
throw new IllegalArgumentException("TextHeaderAtom must be set."); throw new IllegalArgumentException("TextHeaderAtom must be set.");
@ -122,6 +126,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
_headerAtom = tha; _headerAtom = tha;
_byteAtom = tba; _byteAtom = tba;
_charAtom = tca; _charAtom = tca;
this.parentList = parentList;
} }
/* package */HSLFTextParagraph(HSLFTextParagraph other) { /* package */HSLFTextParagraph(HSLFTextParagraph other) {
@ -133,6 +138,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
_ruler = other._ruler; _ruler = other._ruler;
shapeId = other.shapeId; shapeId = other.shapeId;
_paragraphStyle.copy(other._paragraphStyle); _paragraphStyle.copy(other._paragraphStyle);
parentList = other.parentList;
} }
public void addTextRun(HSLFTextRun run) { public void addTextRun(HSLFTextRun run) {
@ -168,7 +174,23 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
* Supply the Sheet we belong to, which might have an assigned SlideShow * Supply the Sheet we belong to, which might have an assigned SlideShow
* Also passes it on to our child RichTextRuns * Also passes it on to our child RichTextRuns
*/ */
public void supplySheet(HSLFSheet sheet) { public static void supplySheet(List<HSLFTextParagraph> paragraphs, HSLFSheet sheet) {
if (paragraphs == null) {
return;
}
for (HSLFTextParagraph p : paragraphs) {
p.supplySheet(sheet);
}
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
*/
private void supplySheet(HSLFSheet sheet) {
this._sheet = sheet; this._sheet = sheet;
if (_runs == null) return; if (_runs == null) return;
@ -942,7 +964,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
if (addParagraph && !lastParaEmpty) { if (addParagraph && !lastParaEmpty) {
TextPropCollection tpc = htp.getParagraphStyle(); TextPropCollection tpc = htp.getParagraphStyle();
HSLFTextParagraph prevHtp = htp; HSLFTextParagraph prevHtp = htp;
htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom); htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom, paragraphs);
htp.getParagraphStyle().copy(tpc); htp.getParagraphStyle().copy(tpc);
htp.setParentShape(prevHtp.getParentShape()); htp.setParentShape(prevHtp.getParentShape());
htp.setShapeId(prevHtp.getShapeId()); htp.setShapeId(prevHtp.getShapeId());
@ -1211,7 +1233,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
// split, but keep delimiter // split, but keep delimiter
for (String para : rawText.split("(?<=\r)")) { for (String para : rawText.split("(?<=\r)")) {
HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars); HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars, paragraphs);
paragraphs.add(tpara); paragraphs.add(tpara);
tpara._ruler = ruler; tpara._ruler = ruler;
tpara.getParagraphStyle().updateTextSize(para.length()); tpara.getParagraphStyle().updateTextSize(para.length());
@ -1235,6 +1257,22 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
return paragraphCollection; return paragraphCollection;
} }
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());
}
csIdx += r.getLength();
}
}
}
}
protected static void applyCharacterStyles(List<HSLFTextParagraph> paragraphs, List<TextPropCollection> charStyles) { protected static void applyCharacterStyles(List<HSLFTextParagraph> paragraphs, List<TextPropCollection> charStyles) {
int paraIdx = 0, runIdx = 0; int paraIdx = 0, runIdx = 0;
HSLFTextRun trun; HSLFTextRun trun;
@ -1340,15 +1378,17 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
TextPropCollection charStyle = sta.addCharacterTextPropCollection(1); TextPropCollection charStyle = sta.addCharacterTextPropCollection(1);
wrapper.appendChildRecord(sta); wrapper.appendChildRecord(sta);
HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null); List<HSLFTextParagraph> paragraphs = new ArrayList<HSLFTextParagraph>(1);
HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null, paragraphs);
htp.setParagraphStyle(paraStyle); htp.setParagraphStyle(paraStyle);
paragraphs.add(htp);
HSLFTextRun htr = new HSLFTextRun(htp); HSLFTextRun htr = new HSLFTextRun(htp);
htr.setCharacterStyle(charStyle); htr.setCharacterStyle(charStyle);
htr.setText(""); htr.setText("");
htp.addTextRun(htr); htp.addTextRun(htr);
return Arrays.asList(htp); return paragraphs;
} }
public EscherTextboxWrapper getTextboxWrapper() { public EscherTextboxWrapper getTextboxWrapper() {
@ -1397,4 +1437,23 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
public boolean isDirty() { public boolean isDirty() {
return _dirty; return _dirty;
} }
/**
* Calculates the start index of the given text run
*
* @param textrun the text run to search for
* @return the start index with the paragraph collection or -1 if not found
*/
/* package */ int getStartIdxOfTextRun(HSLFTextRun textrun) {
int idx = 0;
for (HSLFTextParagraph p : parentList) {
for (HSLFTextRun r : p) {
if (r == textrun) {
return idx;
}
idx += r.getLength();
}
}
return -1;
}
} }

View 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.TextProp;
import org.apache.poi.hslf.model.textproperties.TextPropCollection; import org.apache.poi.hslf.model.textproperties.TextPropCollection;
import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType; 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.draw.DrawPaint;
import org.apache.poi.sl.usermodel.PaintStyle; import org.apache.poi.sl.usermodel.PaintStyle;
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
@ -47,6 +51,7 @@ public final class HSLFTextRun implements TextRun {
private HSLFTextParagraph parentParagraph; private HSLFTextParagraph parentParagraph;
private String _runText = ""; private String _runText = "";
private String _fontFamily; private String _fontFamily;
private int hyperlinkId = -1;
/** /**
* Our paragraph and character style. * Our paragraph and character style.
@ -390,4 +395,64 @@ public final class HSLFTextRun implements TextRun {
public byte getPitchAndFamily() { public byte getPitchAndFamily() {
return 0; 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;
}
} }

View File

@ -594,13 +594,8 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
// (We can't do it in the constructor because the sheet // (We can't do it in the constructor because the sheet
// is not assigned then, it's only built once we have // is not assigned then, it's only built once we have
// all the records) // all the records)
List<HSLFTextParagraph> paras = getTextParagraphs(); List<HSLFTextParagraph> ltp = getTextParagraphs();
if (paras != null) { HSLFTextParagraph.supplySheet(ltp, sheet);
for (HSLFTextParagraph htp : paras) {
// Supply the sheet to our child RichTextRuns
htp.supplySheet(_sheet);
}
}
} }
/** /**

View File

@ -57,11 +57,11 @@ public final class TestHyperlink {
assertNotNull(links); assertNotNull(links);
assertEquals(2, links.size()); 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/", links.get(0).getAddress());
assertEquals("http://jakarta.apache.org/poi/", rawText.substring(links.get(0).getStartIndex(), links.get(0).getEndIndex()-1)); 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/", links.get(1).getAddress());
assertEquals("http://slashdot.org/", rawText.substring(links.get(1).getStartIndex(), links.get(1).getEndIndex()-1)); 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); assertNotNull(links);
assertEquals(1, links.size()); 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("http://jakarta.apache.org/poi/hssf/", links.get(0).getAddress());
assertEquals("Jakarta HSSF", rawText.substring(links.get(0).getStartIndex(), links.get(0).getEndIndex()-1)); assertEquals("Jakarta HSSF", rawText.substring(links.get(0).getStartIndex(), links.get(0).getEndIndex()-1));
} }

View File

@ -26,6 +26,10 @@ import static org.junit.Assert.assertTrue;
import java.awt.Color; import java.awt.Color;
import java.io.File; import java.io.File;
import java.io.IOException; 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.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap; 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;
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
import org.apache.poi.hslf.record.TextHeaderAtom; 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.DrawPaint;
import org.apache.poi.sl.draw.DrawTextParagraph;
import org.apache.poi.sl.usermodel.PaintStyle; import org.apache.poi.sl.usermodel.PaintStyle;
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
import org.apache.poi.sl.usermodel.PictureData.PictureType; import org.apache.poi.sl.usermodel.PictureData.PictureType;
@ -67,8 +73,6 @@ import org.junit.Test;
/** /**
* Testcases for bugs entered in bugzilla * Testcases for bugs entered in bugzilla
* the Test name contains the bugzilla bug id * the Test name contains the bugzilla bug id
*
* @author Yegor Kozlov
*/ */
public final class TestBugs { public final class TestBugs {
/** /**
@ -822,6 +826,59 @@ public final class TestBugs {
ppt.close(); 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 { private static HSLFSlideShow open(String fileName) throws IOException {
File sample = HSLFTestDataSamples.getSampleFile(fileName); File sample = HSLFTestDataSamples.getSampleFile(fileName);

Binary file not shown.