HSLF: readonly support for hyperlinks

git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@542804 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2007-05-30 11:56:46 +00:00
parent d9c17a60ad
commit 49a0737b06
20 changed files with 936 additions and 110 deletions

View File

@ -38,6 +38,7 @@
<li><link href="#SlideTitle">How to set slide title</link></li>
<li><link href="#Fill">How to work with slide/shape background</link></li>
<li><link href="#Bullets">How to create bulleted lists</link></li>
<li><link href="#Hyperlinks">Hyperlinks</link></li>
</ul>
</section>
<section><title>Features</title>
@ -349,6 +350,43 @@
out.close();
</source>
</section>
<anchor id="Hyperlinks"/>
<section><title>How to read hyperlinks from a slide show</title>
<source>
FileInputStream is = new FileInputStream("slideshow.ppt");
SlideShow ppt = new SlideShow(is);
is.close();
Slide[] slide = ppt.getSlides();
for (int j = 0; j &lt; slide.length; j++) {
//read hyperlinks from the text runs
TextRun[] txt = slide[j].getTextRuns();
for (int k = 0; k &lt; txt.length; k++) {
String text = txt[k].getText();
Hyperlink[] links = txt[k].getHyperlinks();
if(links != null) for (int l = 0; l &lt; links.length; l++) {
Hyperlink link = links[l];
String title = link.getTitle();
String address = link.getAddress();
String substring = text.substring(link.getStartIndex(), link.getEndIndex()-1); //in ppt end index is inclusive
}
}
//in PowerPoint you can assign a hyperlink to a shape without text,
//for example to a Line object. The code below demonstrates how to
//read such hyperlinks
Shape[] sh = slide[j].getShapes();
for (int k = 0; k &lt; sh.length; k++) {
Hyperlink link = sh[k].getHyperlink();
if(link != null) {
String title = link.getTitle();
String address = link.getAddress();
}
}
}
</source>
</section>
</section>
</section>

View File

@ -0,0 +1,80 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.examples;
import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.hslf.model.Slide;
import org.apache.poi.hslf.model.TextRun;
import org.apache.poi.hslf.model.Hyperlink;
import org.apache.poi.hslf.model.Shape;
import java.io.FileInputStream;
/**
* Demonstrates how to read hyperlinks from a presentation
*
* @author Yegor Kozlov
*/
public class Hyperlinks {
public static void main(String[] args) throws Exception {
for (int i = 0; i < args.length; i++) {
FileInputStream is = new FileInputStream(args[i]);
SlideShow ppt = new SlideShow(is);
is.close();
Slide[] slide = ppt.getSlides();
for (int j = 0; j < slide.length; j++) {
System.out.println("slide " + slide[j].getSlideNumber());
//read hyperlinks from the slide's text runs
System.out.println("reading hyperlinks from the text runs");
TextRun[] txt = slide[j].getTextRuns();
for (int k = 0; k < txt.length; k++) {
String text = txt[k].getText();
Hyperlink[] links = txt[k].getHyperlinks();
if(links != null) for (int l = 0; l < links.length; l++) {
Hyperlink link = links[l];
String title = link.getTitle();
String address = link.getAddress();
System.out.println(" " + title);
System.out.println(" " + address);
String substring = text.substring(link.getStartIndex(), link.getEndIndex()-1);//in ppt end index is inclusive
System.out.println(" " + substring);
}
}
//in PowerPoint you can assign a hyperlink to a shape without text,
//for example to a Line object. The code below demonstrates how to
//read such hyperlinks
System.out.println(" reading hyperlinks from the slide's shapes");
Shape[] sh = slide[j].getShapes();
for (int k = 0; k < sh.length; k++) {
Hyperlink link = sh[k].getHyperlink();
if(link != null) {
String title = link.getTitle();
String address = link.getAddress();
System.out.println(" " + title);
System.out.println(" " + address);
}
}
}
}
}
}

View File

@ -0,0 +1,168 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.model;
import org.apache.poi.hslf.record.*;
import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.ddf.EscherRecord;
import org.apache.poi.ddf.EscherClientDataRecord;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
/**
* Represents a hyperlink in a PowerPoint document
*
* @author Yegor Kozlov
*/
public class Hyperlink {
private int type;
private String address;
private String title;
private int startIndex, endIndex;
/**
* Gets the type of the hyperlink action.
* Must be a <code>ACTION_*</code> constant defined in <code>InteractiveInfoAtom</code>
*
* @return the hyperlink URL
* @see InteractiveInfoAtom
*/
public int getType() {
return type;
}
/**
* Gets the hyperlink URL
*
* @return the hyperlink URL
*/
public String getAddress() {
return address;
}
/**
* Gets the hyperlink user-friendly title (if different from URL)
*
* @return the hyperlink user-friendly title
*/
public String getTitle() {
return title;
}
/**
* Gets the beginning character position
*
* @return the beginning character position
*/
public int getStartIndex() {
return startIndex;
}
/**
* Gets the ending character position
*
* @return the ending character position
*/
public int getEndIndex() {
return endIndex;
}
/**
* Find hyperlinks in a text run
*
* @param run <code>TextRun</code> to lookup hyperlinks in
* @return found hyperlinks or <code>null</code> if not found
*/
protected static Hyperlink[] find(TextRun run){
ArrayList lst = new ArrayList();
SlideShow ppt = run.getSheet().getSlideShow();
//document-level container which stores info about all links in a presentation
ExObjList exobj = ppt.getDocumentRecord().getExObjList();
if (exobj == null) {
return null;
}
Record[] records = run._records;
if(records != null) find(records, exobj, lst);
Hyperlink[] links = null;
if (lst.size() > 0){
links = new Hyperlink[lst.size()];
lst.toArray(links);
}
return links;
}
/**
* Find hyperlink assigned to the supplied shape
*
* @param shape <code>Shape</code> to lookup hyperlink in
* @return found hyperlink or <code>null</code>
*/
protected static Hyperlink find(Shape shape){
ArrayList lst = new ArrayList();
SlideShow ppt = shape.getSheet().getSlideShow();
//document-level container which stores info about all links in a presentation
ExObjList exobj = ppt.getDocumentRecord().getExObjList();
if (exobj == null) {
return null;
}
EscherContainerRecord spContainer = shape.getSpContainer();
List spchild = spContainer.getChildRecords();
for (Iterator it = spchild.iterator(); it.hasNext(); ) {
EscherRecord obj = (EscherRecord)it.next();
if (obj.getRecordId() == EscherClientDataRecord.RECORD_ID){
byte[] data = ((EscherContainerRecord)obj).serialize();
Record[] records = Record.findChildRecords(data, 8, data.length-8);
if(records != null) find(records, exobj, lst);
}
}
return lst.size() == 1 ? (Hyperlink)lst.get(0) : null;
}
private static void find(Record[] records, ExObjList exobj, List out){
for (int i = 0; i < records.length; i++) {
//see if we have InteractiveInfo in the textrun's records
if( records[i] instanceof InteractiveInfo){
InteractiveInfo hldr = (InteractiveInfo)records[i];
InteractiveInfoAtom info = hldr.getInteractiveInfoAtom();
int id = info.getHyperlinkID();
ExHyperlink linkRecord = exobj.get(id);
if (linkRecord != null){
Hyperlink link = new Hyperlink();
link.title = linkRecord.getLinkTitle();
link.address = linkRecord.getLinkURL();
link.type = info.getAction();
if (++i < records.length && records[i] instanceof TxInteractiveInfoAtom){
TxInteractiveInfoAtom txinfo = (TxInteractiveInfoAtom)records[i];
link.startIndex = txinfo.getStartIndex();
link.endIndex = txinfo.getEndIndex();
}
out.add(link);
}
}
}
}
}

View File

@ -309,4 +309,15 @@ public abstract class Shape {
return new Fill(this);
}
/**
* Returns the hyperlink assigned to this shape
*
* @return the hyperlink assigned to this shape
* or <code>null</code> if not found.
*/
public Hyperlink getHyperlink(){
return Hyperlink.find(this);
}
}

View File

@ -211,4 +211,13 @@ public class ShapeGroup extends Shape{
return spRecord.getOptions() >> 4;
}
/**
* Returns <code>null</code> - shape groups can't have hyperlinks
*
* @return <code>null</code>.
*/
public Hyperlink getHyperlink(){
return null;
}
}

View File

@ -21,6 +21,7 @@ package org.apache.poi.hslf.model;
import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.ddf.EscherDgRecord;
import org.apache.poi.ddf.EscherRecord;
import org.apache.poi.ddf.EscherSpRecord;
import org.apache.poi.hslf.record.*;
import org.apache.poi.hslf.usermodel.SlideShow;
@ -127,7 +128,13 @@ public abstract class Sheet {
Vector runsV = new Vector();
EscherTextboxWrapper[] wrappers = ppdrawing.getTextboxWrappers();
for (int i = 0; i < wrappers.length; i++) {
int s1 = runsV.size();
findTextRuns(wrappers[i].getChildRecords(), runsV);
int s2 = runsV.size();
if (s2 != s1){
TextRun t = (TextRun) runsV.get(runsV.size()-1);
t.setShapeId(wrappers[i].getShapeId());
}
}
TextRun[] runs = new TextRun[runsV.size()];
for (int i = 0; i < runs.length; i++) {
@ -176,6 +183,15 @@ public abstract class Sheet {
}
if (trun != null) {
ArrayList lst = new ArrayList();
for (int j = i; j < records.length; j++) {
if(j > i && records[j] instanceof TextHeaderAtom) break;
lst.add(records[j]);
}
Record[] recs = new Record[lst.size()];
lst.toArray(recs);
trun._records = recs;
found.add(trun);
i++;
} else {
@ -232,6 +248,11 @@ public abstract class Sheet {
EscherDgRecord dg = (EscherDgRecord) Shape.getEscherChild(dgContainer, EscherDgRecord.RECORD_ID);
dg.setNumShapes(dg.getNumShapes() + 1);
int shapeId = dg.getLastMSOSPID()+1;
dg.setLastMSOSPID(shapeId);
EscherSpRecord sp = shape.getSpContainer().getChildById(EscherSpRecord.RECORD_ID);
if(sp != null) sp.setShapeId(shapeId);
shape.setSheet(this);
shape.afterInsert(this);

View File

@ -28,7 +28,6 @@ import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.io.IOException;
import java.util.Vector;
/**
* Represents a TextFrame shape in PowerPoint.
@ -170,7 +169,7 @@ public class TextBox extends SimpleShape {
* @return the text string for this textbox.
*/
public String getText(){
return _txtrun.getText();
return _txtrun == null ? null : _txtrun.getText();
}
/**
@ -452,56 +451,37 @@ public class TextBox extends SimpleShape {
}
private void initTextRun(){
TextHeaderAtom tha = null;
TextCharsAtom tca = null;
TextBytesAtom tba = null;
StyleTextPropAtom sta = null;
OutlineTextRefAtom ota = null;
// Find the interesting child records
Record[] child = _txtbox.getChildRecords();
for (int i = 0; i < child.length; i++) {
if (child[i] instanceof TextHeaderAtom) tha = (TextHeaderAtom)child[i];
else if (child[i] instanceof TextBytesAtom) tba = (TextBytesAtom)child[i];
else if (child[i] instanceof StyleTextPropAtom) sta = (StyleTextPropAtom)child[i];
else if (child[i] instanceof OutlineTextRefAtom) ota = (OutlineTextRefAtom)child[i];
else if (child[i] instanceof TextCharsAtom) tca = (TextCharsAtom)child[i];
if (child[i] instanceof OutlineTextRefAtom) {
ota = (OutlineTextRefAtom)child[i];
break;
}
}
// Special handling for cases where there's an OutlineTextRefAtom
Sheet sheet = getSheet();
TextRun[] runs = sheet.getTextRuns();
if (ota != null) {
// TextHeaderAtom, TextBytesAtom and StyleTextPropAtom are
// stored outside of EscherContainerRecord
int idx = ota.getTextIndex();
Slide sl = (Slide)getSheet();
Record[] rec = sl.getSlideAtomsSet().getSlideRecords();
for (int i = 0, j = 0; i < rec.length; i++) {
if(rec[i].getRecordType() == RecordTypes.TextHeaderAtom.typeID){
if(j++ == idx) { //we found j-th TextHeaderAtom, read the text data
for (int k = i; k < rec.length; k++) {
if (rec[k] instanceof TextHeaderAtom) {
if (tha != null) break;
else tha = (TextHeaderAtom)rec[k];
}
else if (rec[k] instanceof TextBytesAtom) tba = (TextBytesAtom)rec[k];
else if (rec[k] instanceof TextCharsAtom) tca = (TextCharsAtom)rec[k];
else if (rec[k] instanceof StyleTextPropAtom) sta = (StyleTextPropAtom)rec[k];
if(idx < runs.length) _txtrun = runs[idx];
if(_txtrun == null) {
logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx);
}
} else {
int shapeId = _escherContainer.getChildById(EscherSpRecord.RECORD_ID).getShapeId();
if(runs != null) for (int i = 0; i < runs.length; i++) {
if(runs[i].getShapeId() == shapeId){
_txtrun = runs[i];
break;
}
}
if(_txtrun == null) {
logger.log(POILogger.WARN, "text run not found for shapeId=" + shapeId);
}
}
// If we found the records we needed, create a TextRun
if(tba != null) {
// Bytes based Text Run
_txtrun = new TextRun(tha,tba,sta);
} else if (tca != null) {
// Characters (unicode) based Text Run
_txtrun = new TextRun(tha,tca,sta);
} else {
// Empty text box
logger.log(POILogger.WARN, "no text records found for TextBox");
}
}
}

View File

@ -49,6 +49,12 @@ public class TextRun
protected RichTextRun[] _rtRuns;
private SlideShow slideShow;
private Sheet sheet;
private int shapeId;
/**
* all text run records that follow TextHeaderAtom.
* (there can be misc InteractiveInfo, TxInteractiveInfo and other records)
*/
protected Record[] _records;
/**
* Constructs a Text Run from a Unicode text block
@ -517,4 +523,28 @@ public class TextRun
public Sheet getSheet(){
return this.sheet;
}
/**
* @return Shape ID
*/
protected int getShapeId(){
return shapeId;
}
/**
* @param id Shape ID
*/
protected void setShapeId(int id){
shapeId = id;
}
/**
* Returns the array of all hyperlinks in this text run
*
* @return the array of all hyperlinks in this text run
* or <code>null</code> if not found.
*/
public Hyperlink[] getHyperlinks(){
return Hyperlink.find(this);
}
}

View File

@ -39,6 +39,7 @@ public class EscherTextboxWrapper extends RecordContainer
{
private EscherTextboxRecord _escherRecord;
private long _type;
private int shapeId;
/**
* Returns the underlying DDF Escher Record
@ -93,4 +94,18 @@ public class EscherTextboxWrapper extends RecordContainer
// Save in the escher layer
_escherRecord.setData(data);
}
/**
* @return Shape ID
*/
public int getShapeId(){
return shapeId;
}
/**
* @param id Shape ID
*/
public void setShapeId(int id){
shapeId = id;
}
}

View File

@ -42,11 +42,20 @@ public class ExHyperlink extends RecordContainer {
/**
* Returns the URL of the link.
* TODO: Figure out which of detailsA or detailsB is the
* one that always holds it
*
* @return the URL of the link
*/
public String getLinkURL() {
return linkDetailsA.getText();
return linkDetailsB == null ? null : linkDetailsB.getText();
}
/**
* Returns the hyperlink's user-readable name
*
* @return the hyperlink's user-readable name
*/
public String getLinkTitle() {
return linkDetailsA == null ? null : linkDetailsA.getText();
}
/**
@ -66,13 +75,13 @@ public class ExHyperlink extends RecordContainer {
* Get the link details (field A)
*/
public String _getDetailsA() {
return linkDetailsA.getText();
return linkDetailsA == null ? null : linkDetailsA.getText();
}
/**
* Get the link details (field B)
*/
public String _getDetailsB() {
return linkDetailsB.getText();
return linkDetailsB == null ? null : linkDetailsB.getText();
}
/**

View File

@ -109,4 +109,21 @@ public class ExObjList extends RecordContainer {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Lookup a hyperlink by its unique id
*
* @param id hyperlink id
* @return found <code>ExHyperlink</code> or <code>null</code>
*/
public ExHyperlink get(int id){
for(int i=0; i<_children.length; i++) {
if(_children[i] instanceof ExHyperlink) {
ExHyperlink rec = (ExHyperlink)_children[i];
if (rec.getExHyperlinkAtom().getNumber() == id){
return rec;
}
}
}
return null;
}
}

View File

@ -30,10 +30,37 @@ import org.apache.poi.util.LittleEndian;
* (The actual link is held Document.ExObjList.ExHyperlink)
*
* @author Nick Burch
* @author Yegor Kozlov
*/
public class InteractiveInfoAtom extends RecordAtom
{
/**
* Action Table
*/
public static final int ACTION_NONE = 0;
public static final int ACTION_MACRO = 1;
public static final int ACTION_RUNPROGRAM = 2;
public static final int ACTION_JUMP = 3;
public static final int ACTION_HYPERLINK = 4;
public static final int ACTION_OLE = 5;
public static final int ACTION_MEDIA = 6;
public static final int ACTION_CUSTOMSHOW = 7;
/**
* Jump Table
*/
public static final int JUMP_NONE = 0;
public static final int JUMP_NEXTSLIDE = 1;
public static final int JUMP_PREVIOUSSLIDE = 2;
public static final int JUMP_FIRSTSLIDE = 3;
public static final int JUMP_LASTSLIDE = 4;
public static final int JUMP_LASTSLIDEVIEWED = 5;
public static final int JUMP_ENDSHOW = 6;
/**
* Record header.
*/
@ -90,46 +117,138 @@ public class InteractiveInfoAtom extends RecordAtom
* ExHyperlink with this number to get the details.
* @return the link number
*/
public int getNumber() {
public int getHyperlinkID() {
return LittleEndian.getInt(_data,4);
}
/**
* Sets the link number
* @param number the link number.
* Sets the persistent unique identifier of the link
*
* @param number the persistent unique identifier of the link
*/
public void setNumber(int number) {
public void setHyperlinkID(int number) {
LittleEndian.putInt(_data,4,number);
}
/**
* Get the first number - meaning unknown
* a reference to a sound in the sound collection.
*/
public int _getNumber1() {
public int getSoundRef() {
return LittleEndian.getInt(_data,0);
}
protected void _setNumber1(int val) {
/**
* a reference to a sound in the sound collection.
*
* @param val a reference to a sound in the sound collection
*/
public void setSoundRef(int val) {
LittleEndian.putInt(_data, 0, val);
}
/**
* Get the third number - meaning unknown
* Hyperlink Action.
* <p>
* see <code>ACTION_*</code> constants for the list of actions
* </p>
*
* @return hyperlink action.
*/
public int _getNumber3() {
return LittleEndian.getInt(_data,8);
}
protected void _setNumber3(int val) {
LittleEndian.putInt(_data, 8, val);
public byte getAction() {
return _data[8];
}
/**
* Get the fourth number - meaning unknown
* Hyperlink Action
* <p>
* see <code>ACTION_*</code> constants for the list of actions
* </p>
*
* @param val hyperlink action.
*/
public int _getNumber4() {
return LittleEndian.getInt(_data,12);
public void setAction(byte val) {
_data[8] = val;
}
protected void _setNumber4(int val) {
LittleEndian.putInt(_data, 12, val);
/**
* Only valid when action == OLEAction. OLE verb to use, 0 = first verb, 1 = second verb, etc.
*/
public byte getOleVerb() {
return _data[9];
}
/**
* Only valid when action == OLEAction. OLE verb to use, 0 = first verb, 1 = second verb, etc.
*/
public void setOleVerb(byte val) {
_data[9] = val;
}
/**
* Jump
* <p>
* see <code>JUMP_*</code> constants for the list of actions
* </p>
*
* @return jump
*/
public byte getJump() {
return _data[10];
}
/**
* Jump
* <p>
* see <code>JUMP_*</code> constants for the list of actions
* </p>
*
* @param val jump
*/
public void setJump(byte val) {
_data[10] = val;
}
/**
* Flags
* <p>
* <li> Bit 1: Animated. If 1, then button is animated
* <li> Bit 2: Stop sound. If 1, then stop current sound when button is pressed.
* <li> Bit 3: CustomShowReturn. If 1, and this is a jump to custom show,
* then return to this slide after custom show.
* </p>
*/
public byte getFlags() {
return _data[11];
}
/**
* Flags
* <p>
* <li> Bit 1: Animated. If 1, then button is animated
* <li> Bit 2: Stop sound. If 1, then stop current sound when button is pressed.
* <li> Bit 3: CustomShowReturn. If 1, and this is a jump to custom show,
* then return to this slide after custom show.
* </p>
*/
public void setFlags(byte val) {
_data[11] = val;
}
/**
* hyperlink type
*
* @return hyperlink type
*/
public byte getHyperlinkType() {
return _data[12];
}
/**
* hyperlink type
*
* @param val hyperlink type
*/
public void setHyperlinkType(byte val) {
_data[12] = val;
}
/**

View File

@ -147,6 +147,13 @@ public class PPDrawing extends RecordAtom
EscherTextboxRecord tbr = (EscherTextboxRecord)toSearch[i];
EscherTextboxWrapper w = new EscherTextboxWrapper(tbr);
found.add(w);
for (int j = i; j >= 0; j--) {
if(toSearch[j] instanceof EscherSpRecord){
EscherSpRecord sp = (EscherSpRecord)toSearch[j];
w.setShapeId(sp.getShapeId());
break;
}
}
} else {
// If it has children, walk them
if(toSearch[i].isContainerRecord()) {

View File

@ -112,7 +112,7 @@ public class RecordTypes {
public static final Type SlideNumberMCAtom = new Type(4056,null);
public static final Type HeadersFooters = new Type(4057,null);
public static final Type HeadersFootersAtom = new Type(4058,null);
public static final Type TxInteractiveInfoAtom = new Type(4063,null);
public static final Type TxInteractiveInfoAtom = new Type(4063,TxInteractiveInfoAtom.class);
public static final Type CharFormatAtom = new Type(4066,null);
public static final Type ParaFormatAtom = new Type(4067,null);
public static final Type RecolorInfoAtom = new Type(4071,null);

View File

@ -0,0 +1,122 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.record;
import org.apache.poi.util.LittleEndian;
import java.io.OutputStream;
import java.io.IOException;
/**
* Tne atom that holds starting and ending character positions of a hyperlink
*
* @author Yegor Kozlov
*/
public class TxInteractiveInfoAtom extends RecordAtom {
/**
* Record header.
*/
private byte[] _header;
/**
* Record data.
*/
private byte[] _data;
/**
* Constructs a brand new link related atom record.
*/
protected TxInteractiveInfoAtom() {
_header = new byte[8];
_data = new byte[8];
LittleEndian.putShort(_header, 2, (short)getRecordType());
LittleEndian.putInt(_header, 4, _data.length);
}
/**
* Constructs the link related atom record from its
* source data.
*
* @param source the source data as a byte array.
* @param start the start offset into the byte array.
* @param len the length of the slice in the byte array.
*/
protected TxInteractiveInfoAtom(byte[] source, int start, int len) {
// Get the header.
_header = new byte[8];
System.arraycopy(source,start,_header,0,8);
// Get the record data.
_data = new byte[len-8];
System.arraycopy(source,start+8,_data,0,len-8);
}
/**
* Gets the beginning character position
*
* @return the beginning character position
*/
public int getStartIndex() {
return LittleEndian.getInt(_data, 0);
}
/**
* Sets the beginning character position
* @param idx the beginning character position
*/
public void setStartIndex(int idx) {
LittleEndian.putInt(_data, 0, idx);
}
/**
* Gets the ending character position
*
* @return the ending character position
*/
public int getEndIndex() {
return LittleEndian.getInt(_data, 4);
}
/**
* Sets the ending character position
*
* @param idx the ending character position
*/
public void setEndIndex(int idx) {
LittleEndian.putInt(_data, 4, idx);
}
/**
* Gets the record type.
* @return the record type.
*/
public long getRecordType() { return RecordTypes.TxInteractiveInfoAtom.typeID; }
/**
* Write the contents of the record back, so it can be written
* to disk
*
* @param out the output stream to write to.
* @throws java.io.IOException if an error occurs.
*/
public void writeOut(OutputStream out) throws IOException {
out.write(_header);
out.write(_data);
}
}

View File

@ -0,0 +1,89 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.model;
import junit.framework.TestCase;
import java.io.FileInputStream;
import java.io.File;
import org.apache.poi.hslf.usermodel.SlideShow;
/**
* Test Hyperlink.
*
* @author Yegor Kozlov
*/
public class TestHyperlink extends TestCase {
protected String cwd = System.getProperty("HSLF.testdata.path");
public void testTextRunHyperlinks() throws Exception {
FileInputStream is = new FileInputStream(new File(cwd, "WithLinks.ppt"));
SlideShow ppt = new SlideShow(is);
is.close();
TextRun[] run;
Slide slide;
slide = ppt.getSlides()[0];
run = slide.getTextRuns();
for (int i = 0; i < run.length; i++) {
String text = run[i].getText();
if (text.equals(
"This page has two links:\n" +
"http://jakarta.apache.org/poi/\n" +
"\n" +
"http://slashdot.org/\n" +
"\n" +
"In addition, its notes has one link")){
Hyperlink[] links = run[i].getHyperlinks();
assertNotNull(links);
assertEquals(2, links.length);
assertEquals("http://jakarta.apache.org/poi/", links[0].getTitle());
assertEquals("http://jakarta.apache.org/poi/", links[0].getAddress());
assertEquals("http://jakarta.apache.org/poi/", text.substring(links[0].getStartIndex(), links[0].getEndIndex()-1));
assertEquals("http://slashdot.org/", links[1].getTitle());
assertEquals("http://slashdot.org/", links[1].getAddress());
assertEquals("http://slashdot.org/", text.substring(links[1].getStartIndex(), links[1].getEndIndex()-1));
}
}
slide = ppt.getSlides()[1];
run = slide.getTextRuns();
for (int i = 0; i < run.length; i++) {
String text = run[i].getText();
if (text.equals(
"I have the one link:\n" +
"Jakarta HSSF")){
Hyperlink[] links = run[i].getHyperlinks();
assertNotNull(links);
assertEquals(1, links.length);
assertEquals("http://jakarta.apache.org/poi/hssf/", links[0].getTitle());
assertEquals("http://jakarta.apache.org/poi/hssf/", links[0].getAddress());
assertEquals("Jakarta HSSF", text.substring(links[0].getStartIndex(), links[0].getEndIndex()-1));
}
}
}
}

View File

@ -161,6 +161,7 @@ public class TestShapes extends TestCase {
out.close();
ppt = new SlideShow(new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray())));
sl = ppt.getSlides()[0];
txtbox = (TextBox)sl.getShapes()[0];
rt = txtbox.getTextRun().getRichTextRuns()[0];

View File

@ -48,7 +48,7 @@ public class TestInteractiveInfo extends TestCase {
InteractiveInfo ii = new InteractiveInfo(data_a, 0, data_a.length);
InteractiveInfoAtom ia = ii.getInteractiveInfoAtom();
assertEquals(1, ia.getNumber());
assertEquals(1, ia.getHyperlinkID());
}
public void testWrite() throws Exception {
@ -69,10 +69,10 @@ public class TestInteractiveInfo extends TestCase {
InteractiveInfoAtom ia = ii.getInteractiveInfoAtom();
// Set values
ia.setNumber(1);
ia._setNumber1(0);
ia._setNumber3(4);
ia._setNumber4(8);
ia.setHyperlinkID(1);
ia.setSoundRef(0);
ia.setAction((byte)4);
ia.setHyperlinkType((byte)8);
// Check it's now the same as a
ByteArrayOutputStream baos = new ByteArrayOutputStream();

View File

@ -53,22 +53,22 @@ public class TestInteractiveInfoAtom extends TestCase {
InteractiveInfoAtom ia = new InteractiveInfoAtom(data_a, 0, data_a.length);
InteractiveInfoAtom ib = new InteractiveInfoAtom(data_b, 0, data_b.length);
assertEquals(1, ia.getNumber());
assertEquals(4, ib.getNumber());
assertEquals(1, ia.getHyperlinkID());
assertEquals(4, ib.getHyperlinkID());
}
public void testGetRest() throws Exception {
InteractiveInfoAtom ia = new InteractiveInfoAtom(data_a, 0, data_a.length);
InteractiveInfoAtom ib = new InteractiveInfoAtom(data_b, 0, data_b.length);
assertEquals(0, ia._getNumber1());
assertEquals(0, ib._getNumber1());
assertEquals(0, ia.getSoundRef());
assertEquals(0, ib.getSoundRef());
assertEquals(4, ia._getNumber3());
assertEquals(4, ib._getNumber3());
assertEquals(4, ia.getAction());
assertEquals(4, ib.getAction());
assertEquals(8, ia._getNumber4());
assertEquals(8, ib._getNumber4());
assertEquals(8, ia.getHyperlinkType());
assertEquals(8, ib.getHyperlinkType());
}
public void testWrite() throws Exception {
@ -88,10 +88,10 @@ public class TestInteractiveInfoAtom extends TestCase {
InteractiveInfoAtom ia = new InteractiveInfoAtom();
// Set values
ia.setNumber(1);
ia._setNumber1(0);
ia._setNumber3(4);
ia._setNumber4(8);
ia.setHyperlinkID(1);
ia.setSoundRef(0);
ia.setAction((byte)4);
ia.setHyperlinkType((byte)8);
// Check it's now the same as a
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@ -109,7 +109,7 @@ public class TestInteractiveInfoAtom extends TestCase {
InteractiveInfoAtom ia = new InteractiveInfoAtom(data_a, 0, data_a.length);
// Change the number
ia.setNumber(4);
ia.setHyperlinkID(4);
// Check bytes are now the same
ByteArrayOutputStream baos = new ByteArrayOutputStream();

View File

@ -0,0 +1,110 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.record;
import junit.framework.TestCase;
import java.io.ByteArrayOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Tests that TxInteractiveInfoAtom works properly.
*
* @author Yegor Kozlov
*/
public class TestTxInteractiveInfoAtom extends TestCase {
// From WithLinks.ppt
private byte[] data_a = new byte[] {
00, 00, (byte)0xDF, 0x0F, 0x08, 00, 00, 00,
0x19, 00, 00, 00, 0x38, 00, 00, 00
};
private byte[] data_b = new byte[] {
00, 00, (byte)0xDF, 0x0F, 0x08, 00, 00, 00,
0x39, 00, 00, 00, 0x4E, 00, 00, 00
};
public void testRead() throws Exception {
TxInteractiveInfoAtom ia1 = new TxInteractiveInfoAtom(data_a, 0, data_a.length);
assertEquals(4063, ia1.getRecordType());
assertEquals(25, ia1.getStartIndex());
assertEquals(56, ia1.getEndIndex());
TxInteractiveInfoAtom ia2 = new TxInteractiveInfoAtom(data_b, 0, data_b.length);
assertEquals(4063, ia2.getRecordType());
assertEquals(57, ia2.getStartIndex());
assertEquals(78, ia2.getEndIndex());
}
public void testWrite() throws Exception {
TxInteractiveInfoAtom atom = new TxInteractiveInfoAtom(data_a, 0, data_a.length);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
atom.writeOut(baos);
byte[] b = baos.toByteArray();
assertEquals(data_a.length, b.length);
for(int i=0; i<data_a.length; i++) {
assertEquals(data_a[i],b[i]);
}
}
// Create A from scratch
public void testCreate() throws Exception {
TxInteractiveInfoAtom ia = new TxInteractiveInfoAtom();
// Set values
ia.setStartIndex(25);
ia.setEndIndex(56);
// Check it's now the same as a
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ia.writeOut(baos);
byte[] b = baos.toByteArray();
assertEquals(data_a.length, b.length);
for(int i=0; i<data_a.length; i++) {
assertEquals(data_a[i],b[i]);
}
}
// Try to turn a into b
public void testChange() throws Exception {
TxInteractiveInfoAtom ia = new TxInteractiveInfoAtom(data_a, 0, data_a.length);
// Change the number
ia.setStartIndex(57);
ia.setEndIndex(78);
// Check bytes are now the same
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ia.writeOut(baos);
byte[] b = baos.toByteArray();
// Should now be the same
assertEquals(data_b.length, b.length);
for(int i=0; i<data_b.length; i++) {
assertEquals(data_b[i],b[i]);
}
}
}