#41047 - Support hyperlinks in HSLF shapes and textruns
#47291 - Cannot open link correctly which insert in ppt HSLF hyperlink code was all over the place - moved most of it into HSLFHyperlink extended common sl for hyperlinks extended XSLF shape linking and added XSLFTextShape.appendText to go along with HSLF adapted/fixed documentation added convenience methods to the hyperlink classes to address the different targets git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1726458 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
6d56feee90
commit
5fad9af36f
@ -39,38 +39,26 @@ public abstract class CreateHyperlink {
|
||||
HSLFSlide slideC = ppt.createSlide();
|
||||
|
||||
// link to a URL
|
||||
HSLFTextBox textBox1 = new HSLFTextBox();
|
||||
HSLFTextBox textBox1 = slideA.createTextBox();
|
||||
textBox1.setText("Apache POI");
|
||||
textBox1.setAnchor(new Rectangle(100, 100, 200, 50));
|
||||
|
||||
String text = textBox1.getText();
|
||||
HSLFHyperlink link = new HSLFHyperlink();
|
||||
link.setAddress("http://www.apache.org");
|
||||
link.setLabel(textBox1.getText());
|
||||
int linkId = ppt.addHyperlink(link);
|
||||
|
||||
// apply link to the text
|
||||
textBox1.setHyperlink(linkId, 0, text.length());
|
||||
|
||||
slideA.addShape(textBox1);
|
||||
HSLFHyperlink link1 = textBox1.getTextParagraphs().get(0).getTextRuns().get(0).createHyperlink();
|
||||
link1.linkToUrl("http://www.apache.org");
|
||||
link1.setLabel(textBox1.getText());
|
||||
|
||||
// link to another slide
|
||||
HSLFTextBox textBox2 = new HSLFTextBox();
|
||||
HSLFTextBox textBox2 = slideA.createTextBox();
|
||||
textBox2.setText("Go to slide #3");
|
||||
textBox2.setAnchor(new Rectangle(100, 300, 200, 50));
|
||||
|
||||
HSLFHyperlink link2 = new HSLFHyperlink();
|
||||
link2.setAddress(slideC);
|
||||
ppt.addHyperlink(link2);
|
||||
|
||||
// apply link to the whole shape
|
||||
textBox2.setHyperlink(link2);
|
||||
|
||||
slideA.addShape(textBox2);
|
||||
HSLFHyperlink link2 = textBox2.getTextParagraphs().get(0).getTextRuns().get(0).createHyperlink();
|
||||
link2.linkToSlide(slideC);
|
||||
|
||||
FileOutputStream out = new FileOutputStream("hyperlink.ppt");
|
||||
ppt.write(out);
|
||||
out.close();
|
||||
|
||||
ppt.close();
|
||||
}
|
||||
}
|
||||
|
@ -19,12 +19,15 @@ package org.apache.poi.hslf.examples;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.poi.hslf.usermodel.HSLFHyperlink;
|
||||
import org.apache.poi.hslf.usermodel.HSLFShape;
|
||||
import org.apache.poi.hslf.usermodel.HSLFSimpleShape;
|
||||
import org.apache.poi.hslf.usermodel.HSLFSlide;
|
||||
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
|
||||
import org.apache.poi.hslf.usermodel.HSLFTextParagraph;
|
||||
import org.apache.poi.hslf.usermodel.HSLFTextRun;
|
||||
|
||||
/**
|
||||
* Demonstrates how to read hyperlinks from a presentation
|
||||
@ -44,12 +47,14 @@ public final class Hyperlinks {
|
||||
|
||||
// read hyperlinks from the slide's text runs
|
||||
System.out.println("- reading hyperlinks from the text runs");
|
||||
for (List<HSLFTextParagraph> txtParas : slide.getTextParagraphs()) {
|
||||
List<HSLFHyperlink> links = HSLFHyperlink.find(txtParas);
|
||||
String text = HSLFTextParagraph.getRawText(txtParas);
|
||||
|
||||
for (HSLFHyperlink link : links) {
|
||||
System.out.println(toStr(link, text));
|
||||
for (List<HSLFTextParagraph> paras : slide.getTextParagraphs()) {
|
||||
for (HSLFTextParagraph para : paras) {
|
||||
for (HSLFTextRun run : para) {
|
||||
HSLFHyperlink link = run.getHyperlink();
|
||||
if (link != null) {
|
||||
System.out.println(toStr(link, run.getRawText()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,18 +63,21 @@ public final class Hyperlinks {
|
||||
// read such hyperlinks
|
||||
System.out.println("- reading hyperlinks from the slide's shapes");
|
||||
for (HSLFShape sh : slide.getShapes()) {
|
||||
HSLFHyperlink link = HSLFHyperlink.find(sh);
|
||||
if (link == null) continue;
|
||||
System.out.println(toStr(link, null));
|
||||
if (sh instanceof HSLFSimpleShape) {
|
||||
HSLFHyperlink link = ((HSLFSimpleShape)sh).getHyperlink();
|
||||
if (link != null) {
|
||||
System.out.println(toStr(link, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ppt.close();
|
||||
}
|
||||
}
|
||||
|
||||
static String toStr(HSLFHyperlink link, String rawText) {
|
||||
//in ppt end index is inclusive
|
||||
String formatStr = "title: %1$s, address: %2$s" + (rawText == null ? "" : ", start: %3$s, end: %4$s, substring: %5$s");
|
||||
String substring = (rawText == null) ? "" : rawText.substring(link.getStartIndex(), link.getEndIndex()-1);
|
||||
return String.format(formatStr, link.getLabel(), link.getAddress(), link.getStartIndex(), link.getEndIndex(), substring);
|
||||
return String.format(Locale.ROOT, formatStr, link.getLabel(), link.getAddress(), link.getStartIndex(), link.getEndIndex(), rawText);
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ public class Tutorial6 {
|
||||
XSLFTextRun r2 = shape2.addNewTextParagraph().addNewTextRun();
|
||||
XSLFHyperlink link2 = r2.createHyperlink();
|
||||
r2.setText("Go to the second slide"); // visible text
|
||||
link2.setAddress(slide2); // link address
|
||||
link2.linkToSlide(slide2); // link address
|
||||
|
||||
|
||||
|
||||
|
@ -61,7 +61,7 @@ public class DrawPictureShape extends DrawSimpleShape {
|
||||
* Returns an ImageRenderer for the PictureData
|
||||
*
|
||||
* @param graphics
|
||||
* @return
|
||||
* @return the image renderer
|
||||
*/
|
||||
public static ImageRenderer getImageRenderer(Graphics2D graphics, String contentType) {
|
||||
ImageRenderer renderer = (ImageRenderer)graphics.getRenderingHint(Drawable.IMAGE_RENDERER);
|
||||
|
@ -20,5 +20,59 @@ package org.apache.poi.sl.usermodel;
|
||||
/**
|
||||
* A PowerPoint hyperlink
|
||||
*/
|
||||
public interface Hyperlink extends org.apache.poi.common.usermodel.Hyperlink {
|
||||
public interface Hyperlink<
|
||||
S extends Shape<S,P>,
|
||||
P extends TextParagraph<S,P,?>
|
||||
> extends org.apache.poi.common.usermodel.Hyperlink {
|
||||
/**
|
||||
* Link to an email
|
||||
*
|
||||
* @param emailAddress the email address
|
||||
* @since POI 3.14-Beta2
|
||||
*/
|
||||
void linkToEmail(String emailAddress);
|
||||
|
||||
/**
|
||||
* Link to a web page / URL
|
||||
*
|
||||
* @param url the url
|
||||
* @since POI 3.14-Beta2
|
||||
*/
|
||||
void linkToUrl(String url);
|
||||
|
||||
/**
|
||||
* Link to a slide in this slideshow
|
||||
*
|
||||
* @param slide the linked slide
|
||||
* @since POI 3.14-Beta2
|
||||
*/
|
||||
void linkToSlide(Slide<S,P> slide);
|
||||
|
||||
/**
|
||||
* Link to the next slide (relative from the current)
|
||||
*
|
||||
* @since POI 3.14-Beta2
|
||||
*/
|
||||
void linkToNextSlide();
|
||||
|
||||
/**
|
||||
* Link to the previous slide (relative from the current)
|
||||
*
|
||||
* @since POI 3.14-Beta2
|
||||
*/
|
||||
void linkToPreviousSlide();
|
||||
|
||||
/**
|
||||
* Link to the first slide in this slideshow
|
||||
*
|
||||
* @since POI 3.14-Beta2
|
||||
*/
|
||||
void linkToFirstSlide();
|
||||
|
||||
/**
|
||||
* Link to the last slide in this slideshow
|
||||
*
|
||||
* @since POI 3.14-Beta2
|
||||
*/
|
||||
void linkToLastSlide();
|
||||
}
|
||||
|
@ -83,4 +83,24 @@ public interface SimpleShape<
|
||||
* the solid fill attribute from the underlying implementation
|
||||
*/
|
||||
void setFillColor(Color color);
|
||||
|
||||
/**
|
||||
* Returns the hyperlink assigned to this shape
|
||||
*
|
||||
* @return the hyperlink assigned to this shape
|
||||
* or <code>null</code> if not found.
|
||||
*
|
||||
* @since POI 3.14-Beta1
|
||||
*/
|
||||
Hyperlink<S,P> getHyperlink();
|
||||
|
||||
/**
|
||||
* Creates a hyperlink and asigns it to this shape.
|
||||
* If the shape has already a hyperlink assigned, return it instead
|
||||
*
|
||||
* @return the hyperlink assigned to this shape
|
||||
*
|
||||
* @since POI 3.14-Beta1
|
||||
*/
|
||||
Hyperlink<S,P> createHyperlink();
|
||||
}
|
||||
|
@ -161,6 +161,19 @@ public interface TextRun {
|
||||
* Return the associated hyperlink
|
||||
*
|
||||
* @return the associated hyperlink or null if no hyperlink was set
|
||||
*
|
||||
* @since POI 3.14-Beta2
|
||||
*/
|
||||
Hyperlink getHyperlink();
|
||||
Hyperlink<?,?> getHyperlink();
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new hyperlink and assigns it to this text run.
|
||||
* If the text run has already a hyperlink assigned, return it instead
|
||||
*
|
||||
* @return the associated hyperlink
|
||||
*
|
||||
* @since POI 3.14-Beta2
|
||||
*/
|
||||
Hyperlink<?,?> createHyperlink();
|
||||
}
|
||||
|
@ -118,6 +118,16 @@ public interface TextShape<
|
||||
OTHER
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text contained in this text frame, which has been made safe
|
||||
* for printing and other use.
|
||||
*
|
||||
* @return the text string for this textbox.
|
||||
*
|
||||
* @since POI 3.14-Beta2
|
||||
*/
|
||||
String getText();
|
||||
|
||||
/**
|
||||
* Sets (overwrites) the current text.
|
||||
* Uses the properties of the first paragraph / textrun.
|
||||
@ -130,6 +140,18 @@ public interface TextShape<
|
||||
*/
|
||||
TextRun setText(String text);
|
||||
|
||||
/**
|
||||
* Adds the supplied text onto the end of the TextParagraphs,
|
||||
* creating a new RichTextRun for it to sit in.
|
||||
*
|
||||
* @param text the text string to be appended.
|
||||
* @param newParagraph if true, a new paragraph will be added,
|
||||
* which will contain the added text
|
||||
*
|
||||
* @since POI 3.14-Beta1
|
||||
*/
|
||||
TextRun appendText(String text, boolean newParagraph);
|
||||
|
||||
/**
|
||||
* @return the TextParagraphs for this text box
|
||||
*/
|
||||
|
@ -16,20 +16,23 @@
|
||||
==================================================================== */
|
||||
package org.apache.poi.xslf.usermodel;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.apache.poi.openxml4j.opc.PackagePart;
|
||||
import org.apache.poi.openxml4j.opc.PackagePartName;
|
||||
import org.apache.poi.openxml4j.opc.PackageRelationship;
|
||||
import org.apache.poi.openxml4j.opc.TargetMode;
|
||||
import org.apache.poi.sl.usermodel.Hyperlink;
|
||||
import org.apache.poi.sl.usermodel.Slide;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
public class XSLFHyperlink implements Hyperlink {
|
||||
final XSLFTextRun _r;
|
||||
public class XSLFHyperlink implements Hyperlink<XSLFShape,XSLFTextParagraph> {
|
||||
final XSLFSheet _sheet;
|
||||
final CTHyperlink _link;
|
||||
|
||||
XSLFHyperlink(CTHyperlink link, XSLFTextRun r){
|
||||
_r = r;
|
||||
XSLFHyperlink(CTHyperlink link, XSLFSheet sheet){
|
||||
_sheet = sheet;
|
||||
_link = link;
|
||||
}
|
||||
|
||||
@ -39,17 +42,20 @@ public class XSLFHyperlink implements Hyperlink {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAddress(String address){
|
||||
XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet();
|
||||
PackageRelationship rel =
|
||||
sheet.getPackagePart().
|
||||
addExternalRelationship(address, XSLFRelation.HYPERLINK.getRelation());
|
||||
_link.setId(rel.getId());
|
||||
public void setAddress(String address) {
|
||||
linkToUrl(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAddress() {
|
||||
return getTargetURI().toASCIIString();
|
||||
if (!_link.isSetId()) {
|
||||
return _link.getAction();
|
||||
}
|
||||
|
||||
String id = _link.getId();
|
||||
URI targetURI = _sheet.getPackagePart().getRelationship(id).getTargetURI();
|
||||
|
||||
return targetURI.toASCIIString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -64,28 +70,88 @@ public class XSLFHyperlink implements Hyperlink {
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
// TODO: currently this just returns nonsense
|
||||
if ("ppaction://hlinksldjump".equals(_link.getAction())) {
|
||||
String action = _link.getAction();
|
||||
if (action == null) {
|
||||
action = "";
|
||||
}
|
||||
if (action.equals("ppaction://hlinksldjump") || action.startsWith("ppaction://hlinkshowjump")) {
|
||||
return LINK_DOCUMENT;
|
||||
}
|
||||
return LINK_URL;
|
||||
|
||||
String address = getAddress();
|
||||
if (address == null) {
|
||||
address = "";
|
||||
}
|
||||
if (address.startsWith("mailto:")) {
|
||||
return LINK_EMAIL;
|
||||
} else {
|
||||
return LINK_URL;
|
||||
}
|
||||
}
|
||||
|
||||
public void setAddress(XSLFSlide slide){
|
||||
XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet();
|
||||
@Override
|
||||
public void linkToEmail(String emailAddress) {
|
||||
linkToExternal("mailto:"+emailAddress);
|
||||
setLabel(emailAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void linkToUrl(String url) {
|
||||
linkToExternal(url);
|
||||
setLabel(url);
|
||||
}
|
||||
|
||||
private void linkToExternal(String url) {
|
||||
PackagePart thisPP = _sheet.getPackagePart();
|
||||
if (_link.isSetId() && !_link.getId().isEmpty()) {
|
||||
thisPP.removeRelationship(_link.getId());
|
||||
}
|
||||
PackageRelationship rel = thisPP.addExternalRelationship(url, XSLFRelation.HYPERLINK.getRelation());
|
||||
_link.setId(rel.getId());
|
||||
if (_link.isSetAction()) {
|
||||
_link.unsetAction();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void linkToSlide(Slide<XSLFShape,XSLFTextParagraph> slide) {
|
||||
PackagePart thisPP = _sheet.getPackagePart();
|
||||
PackagePartName otherPPN = ((XSLFSheet)slide).getPackagePart().getPartName();
|
||||
if (_link.isSetId() && !_link.getId().isEmpty()) {
|
||||
thisPP.removeRelationship(_link.getId());
|
||||
}
|
||||
PackageRelationship rel =
|
||||
sheet.getPackagePart().
|
||||
addRelationship(slide.getPackagePart().getPartName(),
|
||||
TargetMode.INTERNAL,
|
||||
XSLFRelation.SLIDE.getRelation());
|
||||
thisPP.addRelationship(otherPPN, TargetMode.INTERNAL, XSLFRelation.SLIDE.getRelation());
|
||||
_link.setId(rel.getId());
|
||||
_link.setAction("ppaction://hlinksldjump");
|
||||
}
|
||||
|
||||
@Internal
|
||||
public URI getTargetURI(){
|
||||
XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet();
|
||||
String id = _link.getId();
|
||||
return sheet.getPackagePart().getRelationship(id).getTargetURI();
|
||||
@Override
|
||||
public void linkToNextSlide() {
|
||||
linkToRelativeSlide("nextslide");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void linkToPreviousSlide() {
|
||||
linkToRelativeSlide("previousslide");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void linkToFirstSlide() {
|
||||
linkToRelativeSlide("firstslide");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void linkToLastSlide() {
|
||||
linkToRelativeSlide("lastslide");
|
||||
}
|
||||
|
||||
private void linkToRelativeSlide(String jump) {
|
||||
PackagePart thisPP = _sheet.getPackagePart();
|
||||
if (_link.isSetId() && !_link.getId().isEmpty()) {
|
||||
thisPP.removeRelationship(_link.getId());
|
||||
}
|
||||
_link.setId("");
|
||||
_link.setAction("ppaction://hlinkshowjump?jump="+jump);
|
||||
}
|
||||
}
|
@ -34,8 +34,8 @@ import org.apache.poi.sl.usermodel.LineDecoration;
|
||||
import org.apache.poi.sl.usermodel.LineDecoration.DecorationShape;
|
||||
import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize;
|
||||
import org.apache.poi.sl.usermodel.PaintStyle;
|
||||
import org.apache.poi.sl.usermodel.Placeholder;
|
||||
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
|
||||
import org.apache.poi.sl.usermodel.Placeholder;
|
||||
import org.apache.poi.sl.usermodel.ShapeType;
|
||||
import org.apache.poi.sl.usermodel.SimpleShape;
|
||||
import org.apache.poi.sl.usermodel.StrokeStyle;
|
||||
@ -53,6 +53,7 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTGeomGuide;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineEndProperties;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineStyleList;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTOuterShadowEffect;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
|
||||
@ -923,4 +924,23 @@ public abstract class XSLFSimpleShape extends XSLFShape
|
||||
public void setPlaceholder(Placeholder placeholder) {
|
||||
super.setPlaceholder(placeholder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XSLFHyperlink getHyperlink() {
|
||||
CTNonVisualDrawingProps cNvPr = getCNvPr();
|
||||
if (!cNvPr.isSetHlinkClick()) {
|
||||
return null;
|
||||
}
|
||||
return new XSLFHyperlink(cNvPr.getHlinkClick(), getSheet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public XSLFHyperlink createHyperlink() {
|
||||
XSLFHyperlink hl = getHyperlink();
|
||||
if (hl == null) {
|
||||
CTNonVisualDrawingProps cNvPr = getCNvPr();
|
||||
hl = new XSLFHyperlink(cNvPr.addNewHlinkClick(), getSheet());
|
||||
}
|
||||
return hl;
|
||||
}
|
||||
}
|
||||
|
@ -994,4 +994,26 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for appending text and keeping paragraph and character properties.
|
||||
* The character properties are moved to the end paragraph marker
|
||||
*/
|
||||
/* package */ void clearButKeepProperties() {
|
||||
CTTextParagraph thisP = getXmlObject();
|
||||
for (int i=thisP.sizeOfBrArray(); i>0; i--) {
|
||||
thisP.removeBr(i-1);
|
||||
}
|
||||
for (int i=thisP.sizeOfFldArray(); i>0; i--) {
|
||||
thisP.removeFld(i-1);
|
||||
}
|
||||
if (!_runs.isEmpty()) {
|
||||
int size = _runs.size();
|
||||
thisP.setEndParaRPr(_runs.get(size-1).getRPr());
|
||||
for (int i=size; i>0; i--) {
|
||||
thisP.removeR(i-1);
|
||||
}
|
||||
_runs.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -444,17 +444,19 @@ public class XSLFTextRun implements TextRun {
|
||||
return "[" + getClass() + "]" + getRawText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public XSLFHyperlink createHyperlink(){
|
||||
XSLFHyperlink link = new XSLFHyperlink(_r.getRPr().addNewHlinkClick(), this);
|
||||
return link;
|
||||
XSLFHyperlink hl = getHyperlink();
|
||||
if (hl == null) {
|
||||
hl = new XSLFHyperlink(_r.getRPr().addNewHlinkClick(), _p.getParentShape().getSheet());
|
||||
}
|
||||
return hl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XSLFHyperlink getHyperlink(){
|
||||
if(!_r.getRPr().isSetHlinkClick()) return null;
|
||||
|
||||
|
||||
return new XSLFHyperlink(_r.getRPr().getHlinkClick(), this);
|
||||
return new XSLFHyperlink(_r.getRPr().getHlinkClick(), _p.getParentShape().getSheet());
|
||||
}
|
||||
|
||||
private boolean fetchCharacterProperty(CharacterPropertyFetcher<?> fetcher){
|
||||
|
@ -71,10 +71,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
|
||||
return getTextParagraphs().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return text contained within this shape or empty string
|
||||
*/
|
||||
@Override
|
||||
public String getText() {
|
||||
StringBuilder out = new StringBuilder();
|
||||
for (XSLFTextParagraph p : _paragraphs) {
|
||||
@ -95,50 +92,76 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
|
||||
|
||||
@Override
|
||||
public XSLFTextRun setText(String text) {
|
||||
// copy properties from first paragraph / textrun
|
||||
// calling clearText or setting to a new Array leads to a XmlValueDisconnectedException
|
||||
if (!_paragraphs.isEmpty()) {
|
||||
CTTextBody txBody = getTextBody(false);
|
||||
int cntPs = txBody.sizeOfPArray();
|
||||
for (int i = cntPs; i > 1; i--) {
|
||||
txBody.removeP(i-1);
|
||||
_paragraphs.remove(i-1);
|
||||
}
|
||||
|
||||
_paragraphs.get(0).clearButKeepProperties();
|
||||
}
|
||||
|
||||
return appendText(text, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XSLFTextRun appendText(String text, boolean newParagraph) {
|
||||
if (text == null) return null;
|
||||
|
||||
// copy properties from last paragraph / textrun or paragraph end marker
|
||||
CTTextParagraphProperties pPr = null;
|
||||
CTTextCharacterProperties rPr = null;
|
||||
if (!_paragraphs.isEmpty()) {
|
||||
XSLFTextParagraph p0 = _paragraphs.get(0);
|
||||
pPr = p0.getXmlObject().getPPr();
|
||||
if (!p0.getTextRuns().isEmpty()) {
|
||||
XSLFTextRun r0 = p0.getTextRuns().get(0);
|
||||
|
||||
boolean firstPara;
|
||||
XSLFTextParagraph para;
|
||||
if (_paragraphs.isEmpty()) {
|
||||
firstPara = false;
|
||||
para = null;
|
||||
} else {
|
||||
firstPara = !newParagraph;
|
||||
para = _paragraphs.get(_paragraphs.size()-1);
|
||||
CTTextParagraph ctp = para.getXmlObject();
|
||||
pPr = ctp.getPPr();
|
||||
List<XSLFTextRun> runs = para.getTextRuns();
|
||||
if (!runs.isEmpty()) {
|
||||
XSLFTextRun r0 = runs.get(runs.size()-1);
|
||||
rPr = r0.getXmlObject().getRPr();
|
||||
} else if (ctp.isSetEndParaRPr()) {
|
||||
rPr = ctp.getEndParaRPr();
|
||||
}
|
||||
}
|
||||
|
||||
// can't call clearText otherwise we receive a XmlValueDisconnectedException
|
||||
_paragraphs.clear();
|
||||
CTTextBody txBody = getTextBody(true);
|
||||
int cntPs = txBody.sizeOfPArray();
|
||||
|
||||
// split text by paragraph and new line char
|
||||
XSLFTextRun r = null;
|
||||
for (String paraText : text.split("\\r\\n?|\\n")) {
|
||||
XSLFTextParagraph para = addNewTextParagraph();
|
||||
if (pPr != null) {
|
||||
para.getXmlObject().setPPr(pPr);
|
||||
XSLFTextRun run = null;
|
||||
for (String lineTxt : text.split("\\r\\n?|\\n")) {
|
||||
if (!firstPara) {
|
||||
if (para != null && para.getXmlObject().isSetEndParaRPr()) {
|
||||
para.getXmlObject().unsetEndParaRPr();
|
||||
}
|
||||
para = addNewTextParagraph();
|
||||
if (pPr != null) {
|
||||
para.getXmlObject().setPPr(pPr);
|
||||
}
|
||||
}
|
||||
boolean first = true;
|
||||
for (String runText : paraText.split("[\u000b]")) {
|
||||
if (!first) {
|
||||
boolean firstRun = true;
|
||||
for (String runText : lineTxt.split("[\u000b]")) {
|
||||
if (!firstRun) {
|
||||
para.addLineBreak();
|
||||
}
|
||||
r = para.addNewTextRun();
|
||||
r.setText(runText);
|
||||
run = para.addNewTextRun();
|
||||
run.setText(runText);
|
||||
if (rPr != null) {
|
||||
r.getXmlObject().setRPr(rPr);
|
||||
run.getXmlObject().setRPr(rPr);
|
||||
}
|
||||
first = false;
|
||||
firstRun = false;
|
||||
}
|
||||
firstPara = false;
|
||||
}
|
||||
|
||||
// simply setting a new pArray leads to XmlValueDisconnectedException
|
||||
for (int i = cntPs-1; i >= 0; i--) {
|
||||
txBody.removeP(i);
|
||||
}
|
||||
|
||||
return r;
|
||||
assert(run != null);
|
||||
return run;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,19 +19,17 @@ package org.apache.poi.xslf.usermodel;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
||||
import org.apache.poi.openxml4j.opc.PackageRelationship;
|
||||
import org.apache.poi.openxml4j.opc.TargetMode;
|
||||
import org.apache.poi.sl.usermodel.Hyperlink;
|
||||
import org.apache.poi.xslf.XSLFTestDataSamples;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class TestXSLFHyperlink {
|
||||
|
||||
@Test
|
||||
@ -45,19 +43,19 @@ public class TestXSLFHyperlink {
|
||||
assertEquals("Web Page", cell1.getText());
|
||||
XSLFHyperlink link1 = cell1.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
|
||||
assertNotNull(link1);
|
||||
assertEquals(URI.create("http://poi.apache.org/"), link1.getTargetURI());
|
||||
assertEquals("http://poi.apache.org/", link1.getAddress());
|
||||
|
||||
XSLFTableCell cell2 = tbl.getRows().get(2).getCells().get(0);
|
||||
assertEquals("Place in this document", cell2.getText());
|
||||
XSLFHyperlink link2 = cell2.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
|
||||
assertNotNull(link2);
|
||||
assertEquals(URI.create("/ppt/slides/slide2.xml"), link2.getTargetURI());
|
||||
assertEquals("/ppt/slides/slide2.xml", link2.getAddress());
|
||||
|
||||
XSLFTableCell cell3 = tbl.getRows().get(3).getCells().get(0);
|
||||
assertEquals("Email", cell3.getText());
|
||||
XSLFHyperlink link3 = cell3.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
|
||||
assertNotNull(link3);
|
||||
assertEquals(URI.create("mailto:dev@poi.apache.org?subject=Hi%20There"), link3.getTargetURI());
|
||||
assertEquals("mailto:dev@poi.apache.org?subject=Hi%20There", link3.getAddress());
|
||||
|
||||
ppt.close();
|
||||
}
|
||||
@ -75,7 +73,7 @@ public class TestXSLFHyperlink {
|
||||
r1.setText("Web Page");
|
||||
XSLFHyperlink link1 = r1.createHyperlink();
|
||||
link1.setAddress("http://poi.apache.org/");
|
||||
assertEquals(URI.create("http://poi.apache.org/"), link1.getTargetURI());
|
||||
assertEquals("http://poi.apache.org/", link1.getAddress());
|
||||
assertEquals(numRel + 1, slide1.getPackagePart().getRelationships().size());
|
||||
|
||||
String id1 = link1.getXmlObject().getId();
|
||||
@ -90,8 +88,8 @@ public class TestXSLFHyperlink {
|
||||
XSLFTextRun r2 = sh2.addNewTextParagraph().addNewTextRun();
|
||||
r2.setText("Place in this document");
|
||||
XSLFHyperlink link2 = r2.createHyperlink();
|
||||
link2.setAddress(slide2);
|
||||
assertEquals(URI.create("/ppt/slides/slide2.xml"), link2.getTargetURI());
|
||||
link2.linkToSlide(slide2);
|
||||
assertEquals("/ppt/slides/slide2.xml", link2.getAddress());
|
||||
assertEquals(numRel + 2, slide1.getPackagePart().getRelationships().size());
|
||||
|
||||
String id2 = link2.getXmlObject().getId();
|
||||
@ -104,4 +102,76 @@ public class TestXSLFHyperlink {
|
||||
|
||||
ppt.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void bug47291() throws IOException {
|
||||
Rectangle2D anchor = new Rectangle2D.Double(100,100,100,100);
|
||||
XMLSlideShow ppt1 = new XMLSlideShow();
|
||||
XSLFSlide slide1 = ppt1.createSlide();
|
||||
XSLFTextBox tb1 = slide1.createTextBox();
|
||||
tb1.setAnchor(anchor);
|
||||
XSLFTextRun r1 = tb1.setText("page1");
|
||||
XSLFHyperlink hl1 = r1.createHyperlink();
|
||||
hl1.linkToEmail("dev@poi.apache.org");
|
||||
XSLFTextBox tb2 = ppt1.createSlide().createTextBox();
|
||||
tb2.setAnchor(anchor);
|
||||
XSLFTextRun r2 = tb2.setText("page2");
|
||||
XSLFHyperlink hl2 = r2.createHyperlink();
|
||||
hl2.linkToLastSlide();
|
||||
XSLFSlide sl3 = ppt1.createSlide();
|
||||
XSLFTextBox tb3 = sl3.createTextBox();
|
||||
tb3.setAnchor(anchor);
|
||||
tb3.setText("text1 ");
|
||||
XSLFTextRun r3 = tb3.appendText("lin\u000bk", false);
|
||||
tb3.appendText(" text2", false);
|
||||
XSLFHyperlink hl3 = r3.createHyperlink();
|
||||
hl3.linkToSlide(slide1);
|
||||
XSLFTextBox tb4 = ppt1.createSlide().createTextBox();
|
||||
tb4.setAnchor(anchor);
|
||||
XSLFTextRun r4 = tb4.setText("page4");
|
||||
XSLFHyperlink hl4 = r4.createHyperlink();
|
||||
hl4.linkToUrl("http://poi.apache.org");
|
||||
XSLFTextBox tb5 = ppt1.createSlide().createTextBox();
|
||||
tb5.setAnchor(anchor);
|
||||
tb5.setText("page5");
|
||||
XSLFHyperlink hl5 = tb5.createHyperlink();
|
||||
hl5.linkToFirstSlide();
|
||||
|
||||
XMLSlideShow ppt2 = XSLFTestDataSamples.writeOutAndReadBack(ppt1);
|
||||
ppt1.close();
|
||||
|
||||
List<XSLFSlide> slides = ppt2.getSlides();
|
||||
tb1 = (XSLFTextBox)slides.get(0).getShapes().get(0);
|
||||
hl1 = tb1.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
|
||||
assertNotNull(hl1);
|
||||
assertEquals("dev@poi.apache.org", hl1.getLabel());
|
||||
assertEquals(Hyperlink.LINK_EMAIL, hl1.getType());
|
||||
|
||||
tb2 = (XSLFTextBox)slides.get(1).getShapes().get(0);
|
||||
hl2 = tb2.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
|
||||
assertNotNull(hl2);
|
||||
assertEquals("lastslide", hl2.getXmlObject().getAction().split("=")[1]);
|
||||
assertEquals(Hyperlink.LINK_DOCUMENT, hl2.getType());
|
||||
|
||||
tb3 = (XSLFTextBox)slides.get(2).getShapes().get(0);
|
||||
hl3 = tb3.getTextParagraphs().get(0).getTextRuns().get(3).getHyperlink();
|
||||
assertNotNull(hl3);
|
||||
assertEquals("/ppt/slides/slide1.xml", hl3.getAddress());
|
||||
assertEquals(Hyperlink.LINK_DOCUMENT, hl3.getType());
|
||||
|
||||
tb4 = (XSLFTextBox)slides.get(3).getShapes().get(0);
|
||||
hl4 = tb4.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
|
||||
assertNotNull(hl4);
|
||||
assertEquals("http://poi.apache.org", hl4.getLabel());
|
||||
assertEquals(Hyperlink.LINK_URL, hl4.getType());
|
||||
|
||||
tb5 = (XSLFTextBox)slides.get(4).getShapes().get(0);
|
||||
hl5 = tb5.getHyperlink();
|
||||
assertNotNull(hl5);
|
||||
assertEquals("firstslide", hl5.getXmlObject().getAction().split("=")[1]);
|
||||
assertEquals(Hyperlink.LINK_DOCUMENT, hl5.getType());
|
||||
|
||||
ppt2.close();
|
||||
}
|
||||
}
|
@ -160,7 +160,7 @@ public final class OLEShape extends HSLFPictureShape {
|
||||
if(_exEmbed == null){
|
||||
HSLFSlideShow ppt = getSheet().getSlideShow();
|
||||
|
||||
ExObjList lst = ppt.getDocumentRecord().getExObjList();
|
||||
ExObjList lst = ppt.getDocumentRecord().getExObjList(false);
|
||||
if(lst == null){
|
||||
logger.log(POILogger.WARN, "ExObjList not found");
|
||||
return null;
|
||||
|
@ -46,22 +46,33 @@ public final class Document extends PositionDependentRecordContainer
|
||||
* Returns the DocumentAtom of this Document
|
||||
*/
|
||||
public DocumentAtom getDocumentAtom() { return documentAtom; }
|
||||
|
||||
/**
|
||||
* Returns the Environment of this Notes, which lots of
|
||||
* settings for the document in it
|
||||
* settings for the document in it
|
||||
*/
|
||||
public Environment getEnvironment() { return environment; }
|
||||
|
||||
/**
|
||||
* Returns the PPDrawingGroup, which holds an Escher Structure
|
||||
* that contains information on pictures in the slides.
|
||||
* that contains information on pictures in the slides.
|
||||
*/
|
||||
public PPDrawingGroup getPPDrawingGroup() { return ppDrawing; }
|
||||
|
||||
/**
|
||||
* Returns the ExObjList, which holds the references to
|
||||
* external objects used in the slides. This may be null, if
|
||||
* there are no external references.
|
||||
* external objects used in the slides. This may be null, if
|
||||
* there are no external references.
|
||||
*
|
||||
* @param create if true, create an ExObjList if it doesn't exist
|
||||
*/
|
||||
public ExObjList getExObjList() { return exObjList; }
|
||||
public ExObjList getExObjList(boolean create) {
|
||||
if (exObjList == null && create) {
|
||||
exObjList = new ExObjList();
|
||||
addChildAfter(exObjList, getDocumentAtom());
|
||||
}
|
||||
return exObjList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the SlideListWithTexts that are defined for
|
||||
|
@ -67,9 +67,9 @@ public class ExHyperlink extends RecordContainer {
|
||||
linkDetailsB.setText(url);
|
||||
}
|
||||
}
|
||||
public void setLinkURL(String url, int options) {
|
||||
|
||||
public void setLinkOptions(int options) {
|
||||
if(linkDetailsB != null) {
|
||||
linkDetailsB.setText(url);
|
||||
linkDetailsB.setOptions(options);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import org.apache.poi.hslf.record.ExHyperlink;
|
||||
import org.apache.poi.hslf.record.ExHyperlinkAtom;
|
||||
import org.apache.poi.hslf.record.ExObjList;
|
||||
import org.apache.poi.hslf.record.HSLFEscherClientDataRecord;
|
||||
import org.apache.poi.hslf.record.InteractiveInfo;
|
||||
@ -30,24 +31,95 @@ import org.apache.poi.hslf.record.InteractiveInfoAtom;
|
||||
import org.apache.poi.hslf.record.Record;
|
||||
import org.apache.poi.hslf.record.TxInteractiveInfoAtom;
|
||||
import org.apache.poi.sl.usermodel.Hyperlink;
|
||||
import org.apache.poi.sl.usermodel.Slide;
|
||||
|
||||
/**
|
||||
* Represents a hyperlink in a PowerPoint document
|
||||
*/
|
||||
public final class HSLFHyperlink implements Hyperlink {
|
||||
public static final byte LINK_NEXTSLIDE = InteractiveInfoAtom.LINK_NextSlide;
|
||||
public static final byte LINK_PREVIOUSSLIDE = InteractiveInfoAtom.LINK_PreviousSlide;
|
||||
public static final byte LINK_FIRSTSLIDE = InteractiveInfoAtom.LINK_FirstSlide;
|
||||
public static final byte LINK_LASTSLIDE = InteractiveInfoAtom.LINK_LastSlide;
|
||||
public static final byte LINK_SLIDENUMBER = InteractiveInfoAtom.LINK_SlideNumber;
|
||||
public static final byte LINK_URL = InteractiveInfoAtom.LINK_Url;
|
||||
public static final byte LINK_NULL = InteractiveInfoAtom.LINK_NULL;
|
||||
public final class HSLFHyperlink implements Hyperlink<HSLFShape,HSLFTextParagraph> {
|
||||
private final ExHyperlink exHyper;
|
||||
private final InteractiveInfo info;
|
||||
private TxInteractiveInfoAtom txinfo;
|
||||
|
||||
protected HSLFHyperlink(ExHyperlink exHyper, InteractiveInfo info) {
|
||||
this.info = info;
|
||||
this.exHyper = exHyper;
|
||||
}
|
||||
|
||||
public ExHyperlink getExHyperlink() {
|
||||
return exHyper;
|
||||
}
|
||||
|
||||
public InteractiveInfo getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
public TxInteractiveInfoAtom getTextRunInfo() {
|
||||
return txinfo;
|
||||
}
|
||||
|
||||
protected void setTextRunInfo(TxInteractiveInfoAtom txinfo) {
|
||||
this.txinfo = txinfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Hyperlink and assign it to a shape
|
||||
* This is only a helper method - use {@link HSLFSimpleShape#createHyperlink()} instead!
|
||||
*
|
||||
* @param shape the shape which receives the hyperlink
|
||||
* @return the new hyperlink
|
||||
*
|
||||
* @see HSLFShape#createHyperlink()
|
||||
*/
|
||||
/* package */ static HSLFHyperlink createHyperlink(HSLFSimpleShape shape) {
|
||||
// TODO: check if a hyperlink already exists
|
||||
ExHyperlink exHyper = new ExHyperlink();
|
||||
int linkId = shape.getSheet().getSlideShow().addToObjListAtom(exHyper);
|
||||
ExHyperlinkAtom obj = exHyper.getExHyperlinkAtom();
|
||||
obj.setNumber(linkId);
|
||||
InteractiveInfo info = new InteractiveInfo();
|
||||
info.getInteractiveInfoAtom().setHyperlinkID(linkId);
|
||||
HSLFEscherClientDataRecord cldata = shape.getClientData(true);
|
||||
cldata.addChild(info);
|
||||
HSLFHyperlink hyper = new HSLFHyperlink(exHyper, info);
|
||||
hyper.linkToNextSlide();
|
||||
shape.setHyperlink(hyper);
|
||||
return hyper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Hyperlink for a textrun.
|
||||
* This is only a helper method - use {@link HSLFTextRun#createHyperlink()} instead!
|
||||
*
|
||||
* @param run the run which receives the hyperlink
|
||||
* @return the new hyperlink
|
||||
*
|
||||
* @see HSLFTextRun#createHyperlink()
|
||||
*/
|
||||
/* package */ static HSLFHyperlink createHyperlink(HSLFTextRun run) {
|
||||
// TODO: check if a hyperlink already exists
|
||||
ExHyperlink exHyper = new ExHyperlink();
|
||||
int linkId = run.getTextParagraph().getSheet().getSlideShow().addToObjListAtom(exHyper);
|
||||
ExHyperlinkAtom obj = exHyper.getExHyperlinkAtom();
|
||||
obj.setNumber(linkId);
|
||||
InteractiveInfo info = new InteractiveInfo();
|
||||
info.getInteractiveInfoAtom().setHyperlinkID(linkId);
|
||||
// don't add the hyperlink now to text paragraph records
|
||||
// this will be done, when the paragraph is saved
|
||||
HSLFHyperlink hyper = new HSLFHyperlink(exHyper, info);
|
||||
hyper.linkToNextSlide();
|
||||
|
||||
TxInteractiveInfoAtom txinfo = new TxInteractiveInfoAtom();
|
||||
int startIdx = run.getTextParagraph().getStartIdxOfTextRun(run);
|
||||
int endIdx = startIdx + run.getLength();
|
||||
txinfo.setStartIndex(startIdx);
|
||||
txinfo.setEndIndex(endIdx);
|
||||
hyper.setTextRunInfo(txinfo);
|
||||
|
||||
run.setHyperlink(hyper);
|
||||
return hyper;
|
||||
}
|
||||
|
||||
private int id=-1;
|
||||
private int type;
|
||||
private String address;
|
||||
private String label;
|
||||
private int startIndex, endIndex;
|
||||
|
||||
/**
|
||||
* Gets the type of the hyperlink action.
|
||||
@ -58,70 +130,130 @@ public final class HSLFHyperlink implements Hyperlink {
|
||||
*/
|
||||
@Override
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int val) {
|
||||
type = val;
|
||||
switch(type){
|
||||
case LINK_NEXTSLIDE:
|
||||
label = "NEXT";
|
||||
address = "1,-1,NEXT";
|
||||
break;
|
||||
case LINK_PREVIOUSSLIDE:
|
||||
label = "PREV";
|
||||
address = "1,-1,PREV";
|
||||
break;
|
||||
case LINK_FIRSTSLIDE:
|
||||
label = "FIRST";
|
||||
address = "1,-1,FIRST";
|
||||
break;
|
||||
case LINK_LASTSLIDE:
|
||||
label = "LAST";
|
||||
address = "1,-1,LAST";
|
||||
break;
|
||||
case LINK_SLIDENUMBER:
|
||||
break;
|
||||
default:
|
||||
label = "";
|
||||
address = "";
|
||||
break;
|
||||
switch (info.getInteractiveInfoAtom().getHyperlinkType()) {
|
||||
case InteractiveInfoAtom.LINK_Url:
|
||||
return (exHyper.getLinkURL().startsWith("mailto:")) ? LINK_EMAIL : LINK_URL;
|
||||
case InteractiveInfoAtom.LINK_NextSlide:
|
||||
case InteractiveInfoAtom.LINK_PreviousSlide:
|
||||
case InteractiveInfoAtom.LINK_FirstSlide:
|
||||
case InteractiveInfoAtom.LINK_LastSlide:
|
||||
case InteractiveInfoAtom.LINK_SlideNumber:
|
||||
return LINK_DOCUMENT;
|
||||
case InteractiveInfoAtom.LINK_CustomShow:
|
||||
case InteractiveInfoAtom.LINK_OtherPresentation:
|
||||
case InteractiveInfoAtom.LINK_OtherFile:
|
||||
return LINK_FILE;
|
||||
default:
|
||||
case InteractiveInfoAtom.LINK_NULL:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAddress() {
|
||||
return address;
|
||||
public void linkToEmail(String emailAddress) {
|
||||
InteractiveInfoAtom iia = info.getInteractiveInfoAtom();
|
||||
iia.setAction(InteractiveInfoAtom.ACTION_HYPERLINK);
|
||||
iia.setJump(InteractiveInfoAtom.JUMP_NONE);
|
||||
iia.setHyperlinkType(InteractiveInfoAtom.LINK_Url);
|
||||
exHyper.setLinkURL("mailto:"+emailAddress);
|
||||
exHyper.setLinkTitle(emailAddress);
|
||||
exHyper.setLinkOptions(0x10);
|
||||
}
|
||||
|
||||
public void setAddress(HSLFSlide slide) {
|
||||
String href = slide._getSheetNumber() + ","+slide.getSlideNumber()+",Slide " + slide.getSlideNumber();
|
||||
setAddress(href);;
|
||||
setLabel("Slide " + slide.getSlideNumber());
|
||||
setType(HSLFHyperlink.LINK_SLIDENUMBER);
|
||||
@Override
|
||||
public void linkToUrl(String url) {
|
||||
InteractiveInfoAtom iia = info.getInteractiveInfoAtom();
|
||||
iia.setAction(InteractiveInfoAtom.ACTION_HYPERLINK);
|
||||
iia.setJump(InteractiveInfoAtom.JUMP_NONE);
|
||||
iia.setHyperlinkType(InteractiveInfoAtom.LINK_Url);
|
||||
exHyper.setLinkURL(url);
|
||||
exHyper.setLinkTitle(url);
|
||||
exHyper.setLinkOptions(0x10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void linkToSlide(Slide<HSLFShape,HSLFTextParagraph> slide) {
|
||||
assert(slide instanceof HSLFSlide);
|
||||
HSLFSlide sl = (HSLFSlide)slide;
|
||||
int slideNum = slide.getSlideNumber();
|
||||
String alias = "Slide "+slideNum;
|
||||
|
||||
InteractiveInfoAtom iia = info.getInteractiveInfoAtom();
|
||||
iia.setAction(InteractiveInfoAtom.ACTION_HYPERLINK);
|
||||
iia.setJump(InteractiveInfoAtom.JUMP_NONE);
|
||||
iia.setHyperlinkType(InteractiveInfoAtom.LINK_SlideNumber);
|
||||
|
||||
linkToDocument(sl._getSheetNumber(),slideNum,alias,0x30);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void linkToNextSlide() {
|
||||
InteractiveInfoAtom iia = info.getInteractiveInfoAtom();
|
||||
iia.setAction(InteractiveInfoAtom.ACTION_JUMP);
|
||||
iia.setJump(InteractiveInfoAtom.JUMP_NEXTSLIDE);
|
||||
iia.setHyperlinkType(InteractiveInfoAtom.LINK_NextSlide);
|
||||
|
||||
linkToDocument(1,-1,"NEXT",0x10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void linkToPreviousSlide() {
|
||||
InteractiveInfoAtom iia = info.getInteractiveInfoAtom();
|
||||
iia.setAction(InteractiveInfoAtom.ACTION_JUMP);
|
||||
iia.setJump(InteractiveInfoAtom.JUMP_PREVIOUSSLIDE);
|
||||
iia.setHyperlinkType(InteractiveInfoAtom.LINK_PreviousSlide);
|
||||
|
||||
linkToDocument(1,-1,"PREV",0x10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void linkToFirstSlide() {
|
||||
InteractiveInfoAtom iia = info.getInteractiveInfoAtom();
|
||||
iia.setAction(InteractiveInfoAtom.ACTION_JUMP);
|
||||
iia.setJump(InteractiveInfoAtom.JUMP_FIRSTSLIDE);
|
||||
iia.setHyperlinkType(InteractiveInfoAtom.LINK_FirstSlide);
|
||||
|
||||
linkToDocument(1,-1,"FIRST",0x10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void linkToLastSlide() {
|
||||
InteractiveInfoAtom iia = info.getInteractiveInfoAtom();
|
||||
iia.setAction(InteractiveInfoAtom.ACTION_JUMP);
|
||||
iia.setJump(InteractiveInfoAtom.JUMP_LASTSLIDE);
|
||||
iia.setHyperlinkType(InteractiveInfoAtom.LINK_LastSlide);
|
||||
|
||||
linkToDocument(1,-1,"LAST",0x10);
|
||||
}
|
||||
|
||||
private void linkToDocument(int sheetNumber, int slideNumber, String alias, int options) {
|
||||
exHyper.setLinkURL(sheetNumber+","+slideNumber+","+alias);
|
||||
exHyper.setLinkTitle(alias);
|
||||
exHyper.setLinkOptions(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAddress() {
|
||||
return exHyper.getLinkURL();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAddress(String str) {
|
||||
address = str;
|
||||
exHyper.setLinkURL(str);
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
return exHyper.getExHyperlinkAtom().getNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
return exHyper.getLinkTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel(String str) {
|
||||
label = str;
|
||||
public void setLabel(String label) {
|
||||
exHyper.setLinkTitle(label);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,7 +262,7 @@ public final class HSLFHyperlink implements Hyperlink {
|
||||
* @return the beginning character position
|
||||
*/
|
||||
public int getStartIndex() {
|
||||
return startIndex;
|
||||
return (txinfo == null) ? -1 : txinfo.getStartIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -139,7 +271,9 @@ public final class HSLFHyperlink implements Hyperlink {
|
||||
* @param startIndex the beginning character position
|
||||
*/
|
||||
public void setStartIndex(int startIndex) {
|
||||
this.startIndex = startIndex;
|
||||
if (txinfo != null) {
|
||||
txinfo.setStartIndex(startIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,7 +282,7 @@ public final class HSLFHyperlink implements Hyperlink {
|
||||
* @return the ending character position
|
||||
*/
|
||||
public int getEndIndex() {
|
||||
return endIndex;
|
||||
return (txinfo == null) ? -1 : txinfo.getEndIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,7 +291,9 @@ public final class HSLFHyperlink implements Hyperlink {
|
||||
* @param endIndex the ending character position
|
||||
*/
|
||||
public void setEndIndex(int endIndex) {
|
||||
this.endIndex = endIndex;
|
||||
if (txinfo != null) {
|
||||
txinfo.setEndIndex(endIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,7 +313,7 @@ public final class HSLFHyperlink implements Hyperlink {
|
||||
* @return found hyperlinks
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public static List<HSLFHyperlink> find(List<HSLFTextParagraph> paragraphs){
|
||||
protected static List<HSLFHyperlink> find(List<HSLFTextParagraph> paragraphs){
|
||||
List<HSLFHyperlink> lst = new ArrayList<HSLFHyperlink>();
|
||||
if (paragraphs == null || paragraphs.isEmpty()) return lst;
|
||||
|
||||
@ -185,7 +321,7 @@ public final class HSLFHyperlink implements Hyperlink {
|
||||
|
||||
HSLFSlideShow ppt = firstPara.getSheet().getSlideShow();
|
||||
//document-level container which stores info about all links in a presentation
|
||||
ExObjList exobj = ppt.getDocumentRecord().getExObjList();
|
||||
ExObjList exobj = ppt.getDocumentRecord().getExObjList(false);
|
||||
if (exobj != null) {
|
||||
Record[] records = firstPara.getRecords();
|
||||
find(Arrays.asList(records), exobj, lst);
|
||||
@ -201,10 +337,10 @@ public final class HSLFHyperlink implements Hyperlink {
|
||||
* @return found hyperlink or <code>null</code>
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public static HSLFHyperlink find(HSLFShape shape){
|
||||
protected static HSLFHyperlink find(HSLFShape shape){
|
||||
HSLFSlideShow ppt = shape.getSheet().getSlideShow();
|
||||
//document-level container which stores info about all links in a presentation
|
||||
ExObjList exobj = ppt.getDocumentRecord().getExObjList();
|
||||
ExObjList exobj = ppt.getDocumentRecord().getExObjList(false);
|
||||
HSLFEscherClientDataRecord cldata = shape.getClientData(false);
|
||||
|
||||
if (exobj != null && cldata != null) {
|
||||
@ -228,16 +364,12 @@ public final class HSLFHyperlink implements Hyperlink {
|
||||
InteractiveInfo hldr = (InteractiveInfo)r;
|
||||
InteractiveInfoAtom info = hldr.getInteractiveInfoAtom();
|
||||
int id = info.getHyperlinkID();
|
||||
ExHyperlink linkRecord = exobj.get(id);
|
||||
if (linkRecord == null) {
|
||||
ExHyperlink exHyper = exobj.get(id);
|
||||
if (exHyper == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HSLFHyperlink link = new HSLFHyperlink();
|
||||
link.setId(id);
|
||||
link.setType(info.getAction());
|
||||
link.setLabel(linkRecord.getLinkTitle());
|
||||
link.setAddress(linkRecord.getLinkURL());
|
||||
HSLFHyperlink link = new HSLFHyperlink(exHyper, hldr);
|
||||
out.add(link);
|
||||
|
||||
if (iter.hasNext()) {
|
||||
@ -246,9 +378,7 @@ public final class HSLFHyperlink implements Hyperlink {
|
||||
iter.previous();
|
||||
continue;
|
||||
}
|
||||
TxInteractiveInfoAtom txinfo = (TxInteractiveInfoAtom)r;
|
||||
link.setStartIndex(txinfo.getStartIndex());
|
||||
link.setEndIndex(txinfo.getEndIndex());
|
||||
link.setTextRunInfo((TxInteractiveInfoAtom)r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ import org.apache.poi.ddf.EscherChildAnchorRecord;
|
||||
import org.apache.poi.ddf.EscherClientAnchorRecord;
|
||||
import org.apache.poi.ddf.EscherColorRef;
|
||||
import org.apache.poi.ddf.EscherContainerRecord;
|
||||
import org.apache.poi.ddf.EscherOptRecord;
|
||||
import org.apache.poi.ddf.EscherProperties;
|
||||
import org.apache.poi.ddf.EscherProperty;
|
||||
import org.apache.poi.ddf.EscherRecord;
|
||||
@ -60,8 +59,6 @@ import org.apache.poi.util.Units;
|
||||
* in points (72 points = 1 inch).
|
||||
* </p>
|
||||
* <p>
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public abstract class HSLFShape implements Shape<HSLFShape,HSLFTextParagraph> {
|
||||
|
||||
@ -445,16 +442,6 @@ public abstract class HSLFShape implements Shape<HSLFShape,HSLFTextParagraph> {
|
||||
return getFill().getFillStyle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hyperlink assigned to this shape
|
||||
*
|
||||
* @return the hyperlink assigned to this shape
|
||||
* or <code>null</code> if not found.
|
||||
*/
|
||||
public HSLFHyperlink getHyperlink(){
|
||||
return HSLFHyperlink.find(this);
|
||||
}
|
||||
|
||||
public void draw(Graphics2D graphics){
|
||||
logger.log(POILogger.INFO, "Rendering " + getShapeName());
|
||||
}
|
||||
|
@ -134,6 +134,7 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheet<HSLFShape,H
|
||||
if (trs == null) return;
|
||||
for (List<HSLFTextParagraph> ltp : trs) {
|
||||
HSLFTextParagraph.supplySheet(ltp, this);
|
||||
HSLFTextParagraph.applyHyperlinks(ltp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,6 +172,14 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheet<HSLFShape,H
|
||||
EscherContainerRecord sp = (EscherContainerRecord) it.next();
|
||||
HSLFShape sh = HSLFShapeFactory.createShape(sp, null);
|
||||
sh.setSheet(this);
|
||||
|
||||
if (sh instanceof HSLFSimpleShape) {
|
||||
HSLFHyperlink link = HSLFHyperlink.find(sh);
|
||||
if (link != null) {
|
||||
((HSLFSimpleShape)sh).setHyperlink(link);
|
||||
}
|
||||
}
|
||||
|
||||
shapeList.add(sh);
|
||||
}
|
||||
|
||||
|
@ -32,8 +32,6 @@ import org.apache.poi.ddf.EscherSimpleProperty;
|
||||
import org.apache.poi.ddf.EscherSpRecord;
|
||||
import org.apache.poi.hslf.exceptions.HSLFException;
|
||||
import org.apache.poi.hslf.record.HSLFEscherClientDataRecord;
|
||||
import org.apache.poi.hslf.record.InteractiveInfo;
|
||||
import org.apache.poi.hslf.record.InteractiveInfoAtom;
|
||||
import org.apache.poi.hslf.record.OEPlaceholderAtom;
|
||||
import org.apache.poi.hslf.record.Record;
|
||||
import org.apache.poi.hslf.record.RoundTripHFPlaceholder12;
|
||||
@ -69,6 +67,11 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
|
||||
|
||||
public final static double DEFAULT_LINE_WIDTH = 0.75;
|
||||
|
||||
/**
|
||||
* Hyperlink
|
||||
*/
|
||||
protected HSLFHyperlink _hyperlink;
|
||||
|
||||
/**
|
||||
* Create a SimpleShape object and initialize it from the supplied Record container.
|
||||
*
|
||||
@ -270,56 +273,6 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
|
||||
getFill().setForegroundColor(color);
|
||||
}
|
||||
|
||||
public void setHyperlink(HSLFHyperlink link){
|
||||
if(link.getId() == -1){
|
||||
throw new HSLFException("You must call SlideShow.addHyperlink(Hyperlink link) first");
|
||||
}
|
||||
|
||||
InteractiveInfo info = new InteractiveInfo();
|
||||
InteractiveInfoAtom infoAtom = info.getInteractiveInfoAtom();
|
||||
|
||||
switch(link.getType()){
|
||||
case HSLFHyperlink.LINK_FIRSTSLIDE:
|
||||
infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP);
|
||||
infoAtom.setJump(InteractiveInfoAtom.JUMP_FIRSTSLIDE);
|
||||
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_FirstSlide);
|
||||
break;
|
||||
case HSLFHyperlink.LINK_LASTSLIDE:
|
||||
infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP);
|
||||
infoAtom.setJump(InteractiveInfoAtom.JUMP_LASTSLIDE);
|
||||
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_LastSlide);
|
||||
break;
|
||||
case HSLFHyperlink.LINK_NEXTSLIDE:
|
||||
infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP);
|
||||
infoAtom.setJump(InteractiveInfoAtom.JUMP_NEXTSLIDE);
|
||||
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_NextSlide);
|
||||
break;
|
||||
case HSLFHyperlink.LINK_PREVIOUSSLIDE:
|
||||
infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP);
|
||||
infoAtom.setJump(InteractiveInfoAtom.JUMP_PREVIOUSSLIDE);
|
||||
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_PreviousSlide);
|
||||
break;
|
||||
case HSLFHyperlink.LINK_URL:
|
||||
infoAtom.setAction(InteractiveInfoAtom.ACTION_HYPERLINK);
|
||||
infoAtom.setJump(InteractiveInfoAtom.JUMP_NONE);
|
||||
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_Url);
|
||||
break;
|
||||
case HSLFHyperlink.LINK_SLIDENUMBER:
|
||||
infoAtom.setAction(InteractiveInfoAtom.ACTION_HYPERLINK);
|
||||
infoAtom.setJump(InteractiveInfoAtom.JUMP_NONE);
|
||||
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_SlideNumber);
|
||||
break;
|
||||
default:
|
||||
logger.log(POILogger.WARN, "Ignore unknown hyperlink type : "+link.getLabel());
|
||||
break;
|
||||
}
|
||||
|
||||
infoAtom.setHyperlinkID(link.getId());
|
||||
|
||||
HSLFEscherClientDataRecord cldata = getClientData(true);
|
||||
cldata.addChild(info);
|
||||
}
|
||||
|
||||
public Guide getAdjustValue(String name) {
|
||||
if (name == null || !name.matches("adj([1-9]|10)?")) {
|
||||
throw new IllegalArgumentException("Adjust value '"+name+"' not supported.");
|
||||
@ -653,4 +606,26 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSLFHyperlink getHyperlink(){
|
||||
return _hyperlink;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSLFHyperlink createHyperlink() {
|
||||
if (_hyperlink == null) {
|
||||
_hyperlink = HSLFHyperlink.createHyperlink(this);
|
||||
}
|
||||
return _hyperlink;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the hyperlink - used when the document is parsed
|
||||
*
|
||||
* @param link the hyperlink
|
||||
*/
|
||||
protected void setHyperlink(HSLFHyperlink link) {
|
||||
_hyperlink = link;
|
||||
}
|
||||
}
|
@ -1010,28 +1010,6 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
|
||||
return objectId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a hyperlink to this presentation
|
||||
*
|
||||
* @return 0-based index of the hyperlink
|
||||
*/
|
||||
public int addHyperlink(HSLFHyperlink link) {
|
||||
ExHyperlink ctrl = new ExHyperlink();
|
||||
ExHyperlinkAtom obj = ctrl.getExHyperlinkAtom();
|
||||
if(link.getType() == HSLFHyperlink.LINK_SLIDENUMBER) {
|
||||
ctrl.setLinkURL(link.getAddress(), 0x30);
|
||||
} else {
|
||||
ctrl.setLinkURL(link.getAddress());
|
||||
}
|
||||
ctrl.setLinkTitle(link.getLabel());
|
||||
|
||||
int objectId = addToObjListAtom(ctrl);
|
||||
link.setId(objectId);
|
||||
obj.setNumber(objectId);
|
||||
|
||||
return objectId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a embedded object to this presentation
|
||||
*
|
||||
@ -1104,11 +1082,7 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
|
||||
}
|
||||
|
||||
protected int addToObjListAtom(RecordContainer exObj) {
|
||||
ExObjList lst = (ExObjList) _documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID);
|
||||
if (lst == null) {
|
||||
lst = new ExObjList();
|
||||
_documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom());
|
||||
}
|
||||
ExObjList lst = getDocumentRecord().getExObjList(true);
|
||||
ExObjListAtom objAtom = lst.getExObjListAtom();
|
||||
// increment the object ID seed
|
||||
int objectId = (int) objAtom.getObjectIDSeed() + 1;
|
||||
|
@ -22,7 +22,6 @@ import static org.apache.poi.hslf.record.RecordTypes.OutlineTextRefAtom;
|
||||
import java.awt.Color;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@ -40,6 +39,7 @@ import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType;
|
||||
import org.apache.poi.hslf.record.ColorSchemeAtom;
|
||||
import org.apache.poi.hslf.record.EscherTextboxWrapper;
|
||||
import org.apache.poi.hslf.record.FontCollection;
|
||||
import org.apache.poi.hslf.record.InteractiveInfo;
|
||||
import org.apache.poi.hslf.record.MasterTextPropAtom;
|
||||
import org.apache.poi.hslf.record.OutlineTextRefAtom;
|
||||
import org.apache.poi.hslf.record.PPDrawing;
|
||||
@ -55,6 +55,7 @@ import org.apache.poi.hslf.record.TextCharsAtom;
|
||||
import org.apache.poi.hslf.record.TextHeaderAtom;
|
||||
import org.apache.poi.hslf.record.TextRulerAtom;
|
||||
import org.apache.poi.hslf.record.TextSpecInfoAtom;
|
||||
import org.apache.poi.hslf.record.TxInteractiveInfoAtom;
|
||||
import org.apache.poi.sl.draw.DrawPaint;
|
||||
import org.apache.poi.sl.usermodel.AutoNumberingScheme;
|
||||
import org.apache.poi.sl.usermodel.PaintStyle;
|
||||
@ -183,7 +184,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
|
||||
}
|
||||
|
||||
assert(sheet.getSlideShow() != null);
|
||||
applyHyperlinks(paragraphs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -821,8 +821,21 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
|
||||
*/
|
||||
protected static void storeText(List<HSLFTextParagraph> paragraphs) {
|
||||
fixLineEndings(paragraphs);
|
||||
updateTextAtom(paragraphs);
|
||||
updateStyles(paragraphs);
|
||||
updateHyperlinks(paragraphs);
|
||||
refreshRecords(paragraphs);
|
||||
|
||||
String rawText = toInternalString(getRawText(paragraphs));
|
||||
for (HSLFTextParagraph p : paragraphs) {
|
||||
p._dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the correct text atom depending on the multibyte usage
|
||||
*/
|
||||
private static void updateTextAtom(List<HSLFTextParagraph> paragraphs) {
|
||||
final String rawText = toInternalString(getRawText(paragraphs));
|
||||
|
||||
// Will it fit in a 8 bit atom?
|
||||
boolean isUnicode = StringUtil.hasMultibyte(rawText);
|
||||
@ -888,6 +901,16 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update paragraph and character styles - merges them when subsequential styles match
|
||||
*/
|
||||
private static void updateStyles(List<HSLFTextParagraph> paragraphs) {
|
||||
final String rawText = toInternalString(getRawText(paragraphs));
|
||||
TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;
|
||||
StyleTextPropAtom styleAtom = findStyleAtomPresent(headerAtom, rawText.length());
|
||||
|
||||
// Update the text length for its Paragraph and Character stylings
|
||||
// * reset the length, to the new string's length
|
||||
// * add on +1 if the last block
|
||||
@ -933,7 +956,54 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateHyperlinks(List<HSLFTextParagraph> paragraphs) {
|
||||
TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;
|
||||
RecordContainer _txtbox = headerAtom.getParentRecord();
|
||||
// remove existing hyperlink records
|
||||
for (Record r : _txtbox.getChildRecords()) {
|
||||
if (r instanceof InteractiveInfo || r instanceof TxInteractiveInfoAtom) {
|
||||
_txtbox.removeChild(r);
|
||||
}
|
||||
}
|
||||
// now go through all the textruns and check for hyperlinks
|
||||
HSLFHyperlink lastLink = null;
|
||||
for (HSLFTextParagraph para : paragraphs) {
|
||||
for (HSLFTextRun run : para) {
|
||||
HSLFHyperlink thisLink = run.getHyperlink();
|
||||
if (thisLink != null && thisLink == lastLink) {
|
||||
// the hyperlink extends over this text run, increase its length
|
||||
// TODO: the text run might be longer than the hyperlink
|
||||
thisLink.setEndIndex(thisLink.getEndIndex()+run.getLength());
|
||||
} else {
|
||||
if (lastLink != null) {
|
||||
InteractiveInfo info = lastLink.getInfo();
|
||||
TxInteractiveInfoAtom txinfo = lastLink.getTextRunInfo();
|
||||
assert(info != null && txinfo != null);
|
||||
_txtbox.appendChildRecord(info);
|
||||
_txtbox.appendChildRecord(txinfo);
|
||||
}
|
||||
}
|
||||
lastLink = thisLink;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastLink != null) {
|
||||
InteractiveInfo info = lastLink.getInfo();
|
||||
TxInteractiveInfoAtom txinfo = lastLink.getTextRunInfo();
|
||||
assert(info != null && txinfo != null);
|
||||
_txtbox.appendChildRecord(info);
|
||||
_txtbox.appendChildRecord(txinfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the textbox records back to the document record
|
||||
*/
|
||||
private static void refreshRecords(List<HSLFTextParagraph> paragraphs) {
|
||||
TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;
|
||||
RecordContainer _txtbox = headerAtom.getParentRecord();
|
||||
if (_txtbox instanceof EscherTextboxWrapper) {
|
||||
try {
|
||||
((EscherTextboxWrapper) _txtbox).writeOut(null);
|
||||
@ -941,10 +1011,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
|
||||
throw new RuntimeException("failed dummy write", e);
|
||||
}
|
||||
}
|
||||
|
||||
for (HSLFTextParagraph p : paragraphs) {
|
||||
p._dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1270,11 +1336,37 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
|
||||
for (HSLFHyperlink h : links) {
|
||||
int csIdx = 0;
|
||||
for (HSLFTextParagraph p : paragraphs) {
|
||||
for (HSLFTextRun r : p) {
|
||||
if (h.getStartIndex() <= csIdx && csIdx < h.getEndIndex()) {
|
||||
r.setHyperlinkId(h.getId());
|
||||
if (csIdx > h.getEndIndex()) break;
|
||||
List<HSLFTextRun> runs = p.getTextRuns();
|
||||
for (int rlen=0,rIdx=0; rIdx < runs.size(); csIdx+=rlen, rIdx++) {
|
||||
HSLFTextRun run = runs.get(rIdx);
|
||||
rlen = run.getLength();
|
||||
if (csIdx < h.getEndIndex() && h.getStartIndex() < csIdx+rlen) {
|
||||
String rawText = run.getRawText();
|
||||
int startIdx = h.getStartIndex()-csIdx;
|
||||
if (startIdx > 0) {
|
||||
// hyperlink starts within current textrun
|
||||
HSLFTextRun newRun = new HSLFTextRun(p);
|
||||
newRun.setCharacterStyle(run.getCharacterStyle());
|
||||
newRun.setText(rawText.substring(startIdx));
|
||||
run.setText(rawText.substring(0, startIdx));
|
||||
runs.add(rIdx+1, newRun);
|
||||
rlen = startIdx;
|
||||
continue;
|
||||
}
|
||||
int endIdx = Math.min(rlen, h.getEndIndex()-h.getStartIndex());
|
||||
if (endIdx < rlen) {
|
||||
// hyperlink ends before end of current textrun
|
||||
HSLFTextRun newRun = new HSLFTextRun(p);
|
||||
newRun.setCharacterStyle(run.getCharacterStyle());
|
||||
newRun.setText(rawText.substring(0, endIdx));
|
||||
run.setText(rawText.substring(endIdx));
|
||||
runs.add(rIdx, newRun);
|
||||
rlen = endIdx;
|
||||
run = newRun;
|
||||
}
|
||||
run.setHyperlink(h);
|
||||
}
|
||||
csIdx += r.getLength();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,10 +27,6 @@ import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp;
|
||||
import org.apache.poi.hslf.model.textproperties.TextProp;
|
||||
import org.apache.poi.hslf.model.textproperties.TextPropCollection;
|
||||
import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType;
|
||||
import org.apache.poi.hslf.record.ExHyperlink;
|
||||
import org.apache.poi.hslf.record.InteractiveInfo;
|
||||
import org.apache.poi.hslf.record.InteractiveInfoAtom;
|
||||
import org.apache.poi.hslf.record.Record;
|
||||
import org.apache.poi.sl.draw.DrawPaint;
|
||||
import org.apache.poi.sl.usermodel.PaintStyle;
|
||||
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
|
||||
@ -51,7 +47,7 @@ public final class HSLFTextRun implements TextRun {
|
||||
private HSLFTextParagraph parentParagraph;
|
||||
private String _runText = "";
|
||||
private String _fontFamily;
|
||||
private int hyperlinkId = -1;
|
||||
private HSLFHyperlink link;
|
||||
|
||||
/**
|
||||
* Our paragraph and character style.
|
||||
@ -397,62 +393,25 @@ public final class HSLFTextRun implements TextRun {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the associated hyperlink id - currently this is only used while parsing and
|
||||
* can't be used for update a ppt
|
||||
* Sets the hyperlink - used when parsing the document
|
||||
*
|
||||
* @param hyperlinkId the id or -1 to unset it
|
||||
* @param link the hyperlink
|
||||
*/
|
||||
public void setHyperlinkId(int hyperlinkId) {
|
||||
this.hyperlinkId = hyperlinkId;
|
||||
protected void setHyperlink(HSLFHyperlink link) {
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the associated hyperlink id
|
||||
*
|
||||
* @return the hyperlink id
|
||||
*/
|
||||
public int getHyperlinkId() {
|
||||
return hyperlinkId;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public HSLFHyperlink getHyperlink() {
|
||||
if (hyperlinkId == -1) {
|
||||
return null;
|
||||
return link;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSLFHyperlink createHyperlink() {
|
||||
if (link == null) {
|
||||
link = HSLFHyperlink.createHyperlink(this);
|
||||
parentParagraph.setDirty();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -35,13 +35,10 @@ import org.apache.poi.ddf.EscherTextboxRecord;
|
||||
import org.apache.poi.hslf.exceptions.HSLFException;
|
||||
import org.apache.poi.hslf.model.HSLFMetroShape;
|
||||
import org.apache.poi.hslf.record.EscherTextboxWrapper;
|
||||
import org.apache.poi.hslf.record.InteractiveInfo;
|
||||
import org.apache.poi.hslf.record.InteractiveInfoAtom;
|
||||
import org.apache.poi.hslf.record.OEPlaceholderAtom;
|
||||
import org.apache.poi.hslf.record.PPDrawing;
|
||||
import org.apache.poi.hslf.record.RoundTripHFPlaceholder12;
|
||||
import org.apache.poi.hslf.record.TextHeaderAtom;
|
||||
import org.apache.poi.hslf.record.TxInteractiveInfoAtom;
|
||||
import org.apache.poi.sl.draw.DrawFactory;
|
||||
import org.apache.poi.sl.draw.DrawTextShape;
|
||||
import org.apache.poi.sl.usermodel.Insets2D;
|
||||
@ -622,33 +619,6 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
|
||||
return getClientDataRecord(RoundTripHFPlaceholder12.typeID);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Assigns a hyperlink to this text shape
|
||||
*
|
||||
* @param linkId id of the hyperlink, @see org.apache.poi.hslf.usermodel.SlideShow#addHyperlink(Hyperlink)
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @param endIndex the ending index, exclusive.
|
||||
* @see org.apache.poi.hslf.usermodel.HSLFSlideShow#addHyperlink(HSLFHyperlink)
|
||||
*/
|
||||
public void setHyperlink(int linkId, int beginIndex, int endIndex){
|
||||
//TODO validate beginIndex and endIndex and throw IllegalArgumentException
|
||||
|
||||
InteractiveInfo info = new InteractiveInfo();
|
||||
InteractiveInfoAtom infoAtom = info.getInteractiveInfoAtom();
|
||||
infoAtom.setAction(org.apache.poi.hslf.record.InteractiveInfoAtom.ACTION_HYPERLINK);
|
||||
infoAtom.setHyperlinkType(org.apache.poi.hslf.record.InteractiveInfoAtom.LINK_Url);
|
||||
infoAtom.setHyperlinkID(linkId);
|
||||
|
||||
_txtbox.appendChildRecord(info);
|
||||
|
||||
TxInteractiveInfoAtom txiatom = new TxInteractiveInfoAtom();
|
||||
txiatom.setStartIndex(beginIndex);
|
||||
txiatom.setEndIndex(endIndex);
|
||||
_txtbox.appendChildRecord(txiatom);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlaceholder() {
|
||||
OEPlaceholderAtom oep = getPlaceholderAtom();
|
||||
@ -729,50 +699,38 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
|
||||
return HSLFTextParagraph.getRawText(getTextParagraphs());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text contained in this text frame, which has been made safe
|
||||
* for printing and other use.
|
||||
*
|
||||
* @return the text string for this textbox.
|
||||
*/
|
||||
@Override
|
||||
public String getText() {
|
||||
String rawText = getRawText();
|
||||
return HSLFTextParagraph.toExternalString(rawText, getRunType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSLFTextRun appendText(String text, boolean newParagraph) {
|
||||
// init paragraphs
|
||||
List<HSLFTextParagraph> paras = getTextParagraphs();
|
||||
HSLFTextRun htr = HSLFTextParagraph.appendText(paras, text, newParagraph);
|
||||
setTextId(getRawText().hashCode());
|
||||
return htr;
|
||||
}
|
||||
|
||||
// Update methods follow
|
||||
@Override
|
||||
public HSLFTextRun setText(String text) {
|
||||
// init paragraphs
|
||||
List<HSLFTextParagraph> paras = getTextParagraphs();
|
||||
HSLFTextRun htr = HSLFTextParagraph.setText(paras, text);
|
||||
setTextId(getRawText().hashCode());
|
||||
return htr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the supplied text onto the end of the TextParagraphs,
|
||||
* creating a new RichTextRun for it to sit in.
|
||||
*
|
||||
* @param text the text string used by this object.
|
||||
*/
|
||||
public HSLFTextRun appendText(String text, boolean newParagraph) {
|
||||
// init paragraphs
|
||||
List<HSLFTextParagraph> paras = getTextParagraphs();
|
||||
return HSLFTextParagraph.appendText(paras, text, newParagraph);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSLFTextRun setText(String text) {
|
||||
// init paragraphs
|
||||
List<HSLFTextParagraph> paras = getTextParagraphs();
|
||||
HSLFTextRun htr = HSLFTextParagraph.setText(paras, text);
|
||||
setTextId(text.hashCode());
|
||||
return htr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the modified paragraphs/textrun to the records.
|
||||
* Also updates the styles to the correct text length.
|
||||
*/
|
||||
protected void storeText() {
|
||||
List<HSLFTextParagraph> paras = getTextParagraphs();
|
||||
HSLFTextParagraph.storeText(paras);
|
||||
}
|
||||
// Accesser methods follow
|
||||
/**
|
||||
* Saves the modified paragraphs/textrun to the records.
|
||||
* Also updates the styles to the correct text length.
|
||||
*/
|
||||
protected void storeText() {
|
||||
List<HSLFTextParagraph> paras = getTextParagraphs();
|
||||
HSLFTextParagraph.storeText(paras);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of all hyperlinks in this text run
|
||||
|
@ -22,16 +22,25 @@ import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.toExternalString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.POIDataSamples;
|
||||
import org.apache.poi.hslf.usermodel.*;
|
||||
import org.apache.poi.hslf.HSLFTestDataSamples;
|
||||
import org.apache.poi.hslf.record.InteractiveInfoAtom;
|
||||
import org.apache.poi.hslf.usermodel.HSLFHyperlink;
|
||||
import org.apache.poi.hslf.usermodel.HSLFSlide;
|
||||
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
|
||||
import org.apache.poi.hslf.usermodel.HSLFTextBox;
|
||||
import org.apache.poi.hslf.usermodel.HSLFTextParagraph;
|
||||
import org.apache.poi.hslf.usermodel.HSLFTextRun;
|
||||
import org.apache.poi.sl.usermodel.Hyperlink;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test Hyperlink.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public final class TestHyperlink {
|
||||
private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance();
|
||||
@ -53,8 +62,7 @@ public final class TestHyperlink {
|
||||
"In addition, its notes has one link";
|
||||
assertEquals(expected, rawText);
|
||||
|
||||
List<HSLFHyperlink> links = HSLFHyperlink.find(para);
|
||||
assertNotNull(links);
|
||||
List<HSLFHyperlink> links = findHyperlinks(para);
|
||||
assertEquals(2, links.size());
|
||||
|
||||
assertEquals("http://jakarta.apache.org/poi/", links.get(0).getLabel());
|
||||
@ -73,12 +81,92 @@ public final class TestHyperlink {
|
||||
"Jakarta HSSF";
|
||||
assertEquals(expected, rawText);
|
||||
|
||||
links = HSLFHyperlink.find(para);
|
||||
links.clear();
|
||||
|
||||
links = findHyperlinks(para);
|
||||
assertNotNull(links);
|
||||
assertEquals(1, links.size());
|
||||
|
||||
assertEquals("Open Jakarta POI HSSF module test ", links.get(0).getLabel());
|
||||
assertEquals("http://jakarta.apache.org/poi/hssf/", links.get(0).getAddress());
|
||||
assertEquals("Jakarta HSSF", rawText.substring(links.get(0).getStartIndex(), links.get(0).getEndIndex()-1));
|
||||
|
||||
ppt.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bug47291() throws IOException {
|
||||
HSLFSlideShow ppt1 = new HSLFSlideShow();
|
||||
HSLFSlide slide1 = ppt1.createSlide();
|
||||
HSLFTextRun r1 = slide1.createTextBox().setText("page1");
|
||||
HSLFHyperlink hl1 = r1.createHyperlink();
|
||||
hl1.linkToEmail("dev@poi.apache.org");
|
||||
HSLFTextRun r2 = ppt1.createSlide().createTextBox().setText("page2");
|
||||
HSLFHyperlink hl2 = r2.createHyperlink();
|
||||
hl2.linkToLastSlide();
|
||||
HSLFSlide sl1 = ppt1.createSlide();
|
||||
HSLFTextBox tb1 = sl1.createTextBox();
|
||||
tb1.setAnchor(new Rectangle2D.Double(100,100,100,100));
|
||||
tb1.appendText("text1 ", false);
|
||||
HSLFTextRun r3 = tb1.appendText("lin\u000bk", false);
|
||||
tb1.appendText(" text2", false);
|
||||
HSLFHyperlink hl3 = r3.createHyperlink();
|
||||
hl3.linkToSlide(slide1);
|
||||
HSLFTextRun r4 = ppt1.createSlide().createTextBox().setText("page4");
|
||||
HSLFHyperlink hl4 = r4.createHyperlink();
|
||||
hl4.linkToUrl("http://poi.apache.org");
|
||||
HSLFTextBox tb5 = ppt1.createSlide().createTextBox();
|
||||
tb5.setText("page5");
|
||||
HSLFHyperlink hl5 = tb5.createHyperlink();
|
||||
hl5.linkToFirstSlide();
|
||||
|
||||
HSLFSlideShow ppt2 = HSLFTestDataSamples.writeOutAndReadBack(ppt1);
|
||||
ppt1.close();
|
||||
|
||||
List<HSLFSlide> slides = ppt2.getSlides();
|
||||
tb1 = (HSLFTextBox)slides.get(0).getShapes().get(0);
|
||||
hl1 = tb1.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
|
||||
assertNotNull(hl1);
|
||||
assertEquals("dev@poi.apache.org", hl1.getLabel());
|
||||
assertEquals(Hyperlink.LINK_EMAIL, hl1.getType());
|
||||
|
||||
HSLFTextBox tb2 = (HSLFTextBox)slides.get(1).getShapes().get(0);
|
||||
hl2 = tb2.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
|
||||
assertNotNull(hl2);
|
||||
assertEquals(InteractiveInfoAtom.LINK_LastSlide, hl2.getInfo().getInteractiveInfoAtom().getHyperlinkType());
|
||||
assertEquals(Hyperlink.LINK_DOCUMENT, hl2.getType());
|
||||
|
||||
HSLFTextBox tb3 = (HSLFTextBox)slides.get(2).getShapes().get(0);
|
||||
hl3 = tb3.getTextParagraphs().get(0).getTextRuns().get(1).getHyperlink();
|
||||
assertNotNull(hl3);
|
||||
assertEquals(ppt2.getSlides().get(0)._getSheetNumber(), Integer.parseInt(hl3.getAddress().split(",")[0]));
|
||||
assertEquals(Hyperlink.LINK_DOCUMENT, hl3.getType());
|
||||
|
||||
HSLFTextBox tb4 = (HSLFTextBox)slides.get(3).getShapes().get(0);
|
||||
hl4 = tb4.getTextParagraphs().get(0).getTextRuns().get(0).getHyperlink();
|
||||
assertNotNull(hl4);
|
||||
assertEquals("http://poi.apache.org", hl4.getLabel());
|
||||
assertEquals(Hyperlink.LINK_URL, hl4.getType());
|
||||
|
||||
tb5 = (HSLFTextBox)slides.get(4).getShapes().get(0);
|
||||
hl5 = tb5.getHyperlink();
|
||||
assertNotNull(hl5);
|
||||
assertEquals(InteractiveInfoAtom.LINK_FirstSlide, hl5.getInfo().getInteractiveInfoAtom().getHyperlinkType());
|
||||
assertEquals(Hyperlink.LINK_DOCUMENT, hl5.getType());
|
||||
|
||||
ppt2.close();
|
||||
}
|
||||
|
||||
private static List<HSLFHyperlink> findHyperlinks(List<HSLFTextParagraph> paras) {
|
||||
List<HSLFHyperlink> links = new ArrayList<HSLFHyperlink>();
|
||||
for (HSLFTextParagraph p : paras) {
|
||||
for (HSLFTextRun r : p) {
|
||||
HSLFHyperlink hl = r.getHyperlink();
|
||||
if (hl != null) {
|
||||
links.add(hl);
|
||||
}
|
||||
}
|
||||
}
|
||||
return links;
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ public class TestExObjList extends TestCase {
|
||||
// Get the document
|
||||
Document doc = ss.getDocumentRecord();
|
||||
// Get the ExObjList
|
||||
ExObjList exObjList = doc.getExObjList();
|
||||
ExObjList exObjList = doc.getExObjList(false);
|
||||
assertNotNull(exObjList);
|
||||
assertEquals(1033l, exObjList.getRecordType());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user