<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>
<anchor id="Bullets"/>
<section><title>How to create bulleted lists</title>
<source>
- SlideShow ppt = new SlideShow();
-
- Slide slide = ppt.createSlide();
-
- TextBox shape = new TextBox();
- RichTextRun rt = shape.getTextRun().getRichTextRuns()[0];
- shape.setText(
- "January\r" +
- "February\r" +
- "March\r" +
- "April");
- rt.setFontSize(42);
- rt.setBullet(true);
- rt.setBulletOffset(0); //bullet offset
- rt.setTextOffset(50); //text offset (should be greater than bullet offset)
- rt.setBulletChar('\u263A'); //bullet character
- slide.addShape(shape);
+ SlideShow ppt = new SlideShow();
- shape.setAnchor(new java.awt.Rectangle(50, 50, 500, 300)); //position of the text box in the slide
- slide.addShape(shape);
+ Slide slide = ppt.createSlide();
- FileOutputStream out = new FileOutputStream("bullets.ppt");
- ppt.write(out);
- out.close();
- </source>
+ TextBox shape = new TextBox();
+ RichTextRun rt = shape.getTextRun().getRichTextRuns()[0];
+ shape.setText(
+ "January\r" +
+ "February\r" +
+ "March\r" +
+ "April");
+ rt.setFontSize(42);
+ rt.setBullet(true);
+ rt.setBulletOffset(0); //bullet offset
+ rt.setTextOffset(50); //text offset (should be greater than bullet offset)
+ rt.setBulletChar('\u263A'); //bullet character
+ slide.addShape(shape);
+
+ shape.setAnchor(new java.awt.Rectangle(50, 50, 500, 300)); //position of the text box in the slide
+ slide.addShape(shape);
+
+ FileOutputStream out = new FileOutputStream("bullets.ppt");
+ ppt.write(out);
+ 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 < slide.length; j++) {
+
+ //read 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();
+ 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 < sh.length; k++) {
+ Hyperlink link = sh[k].getHyperlink();
+ if(link != null) {
+ String title = link.getTitle();
+ String address = link.getAddress();
+ }
+ }
+ }
+ </source>
</section>
</section>
--- /dev/null
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.examples;\r
+\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hslf.model.Slide;\r
+import org.apache.poi.hslf.model.TextRun;\r
+import org.apache.poi.hslf.model.Hyperlink;\r
+import org.apache.poi.hslf.model.Shape;\r
+\r
+import java.io.FileInputStream;\r
+\r
+/**\r
+ * Demonstrates how to read hyperlinks from a presentation\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class Hyperlinks {\r
+\r
+ public static void main(String[] args) throws Exception {\r
+ for (int i = 0; i < args.length; i++) {\r
+ FileInputStream is = new FileInputStream(args[i]);\r
+ SlideShow ppt = new SlideShow(is);\r
+ is.close();\r
+\r
+ Slide[] slide = ppt.getSlides();\r
+ for (int j = 0; j < slide.length; j++) {\r
+ System.out.println("slide " + slide[j].getSlideNumber());\r
+\r
+ //read hyperlinks from the slide's text runs\r
+ System.out.println("reading hyperlinks from the text runs");\r
+ TextRun[] txt = slide[j].getTextRuns();\r
+ for (int k = 0; k < txt.length; k++) {\r
+ String text = txt[k].getText();\r
+ Hyperlink[] links = txt[k].getHyperlinks();\r
+ if(links != null) for (int l = 0; l < links.length; l++) {\r
+ Hyperlink link = links[l];\r
+ String title = link.getTitle();\r
+ String address = link.getAddress();\r
+ System.out.println(" " + title);\r
+ System.out.println(" " + address);\r
+ String substring = text.substring(link.getStartIndex(), link.getEndIndex()-1);//in ppt end index is inclusive\r
+ System.out.println(" " + substring);\r
+ }\r
+ }\r
+\r
+ //in PowerPoint you can assign a hyperlink to a shape without text,\r
+ //for example to a Line object. The code below demonstrates how to\r
+ //read such hyperlinks\r
+ System.out.println(" reading hyperlinks from the slide's shapes");\r
+ Shape[] sh = slide[j].getShapes();\r
+ for (int k = 0; k < sh.length; k++) {\r
+ Hyperlink link = sh[k].getHyperlink();\r
+ if(link != null) {\r
+ String title = link.getTitle();\r
+ String address = link.getAddress();\r
+ System.out.println(" " + title);\r
+ System.out.println(" " + address);\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ }\r
+}\r
--- /dev/null
+\r
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hslf.model;\r
+\r
+import org.apache.poi.hslf.record.*;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.ddf.EscherContainerRecord;\r
+import org.apache.poi.ddf.EscherRecord;\r
+import org.apache.poi.ddf.EscherClientDataRecord;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import java.util.Iterator;\r
+\r
+/**\r
+ * Represents a hyperlink in a PowerPoint document\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class Hyperlink {\r
+\r
+ private int type;\r
+ private String address;\r
+ private String title;\r
+ private int startIndex, endIndex;\r
+\r
+ /**\r
+ * Gets the type of the hyperlink action.\r
+ * Must be a <code>ACTION_*</code> constant defined in <code>InteractiveInfoAtom</code>\r
+ *\r
+ * @return the hyperlink URL\r
+ * @see InteractiveInfoAtom\r
+ */\r
+ public int getType() {\r
+ return type;\r
+ }\r
+\r
+ /**\r
+ * Gets the hyperlink URL\r
+ *\r
+ * @return the hyperlink URL\r
+ */\r
+ public String getAddress() {\r
+ return address;\r
+ }\r
+\r
+ /**\r
+ * Gets the hyperlink user-friendly title (if different from URL)\r
+ *\r
+ * @return the hyperlink user-friendly title\r
+ */\r
+ public String getTitle() {\r
+ return title;\r
+ }\r
+\r
+ /**\r
+ * Gets the beginning character position\r
+ *\r
+ * @return the beginning character position\r
+ */\r
+ public int getStartIndex() {\r
+ return startIndex;\r
+ }\r
+\r
+ /**\r
+ * Gets the ending character position\r
+ *\r
+ * @return the ending character position\r
+ */\r
+ public int getEndIndex() {\r
+ return endIndex;\r
+ }\r
+\r
+ /**\r
+ * Find hyperlinks in a text run\r
+ *\r
+ * @param run <code>TextRun</code> to lookup hyperlinks in\r
+ * @return found hyperlinks or <code>null</code> if not found\r
+ */\r
+ protected static Hyperlink[] find(TextRun run){\r
+ ArrayList lst = new ArrayList();\r
+ SlideShow ppt = run.getSheet().getSlideShow();\r
+ //document-level container which stores info about all links in a presentation\r
+ ExObjList exobj = ppt.getDocumentRecord().getExObjList();\r
+ if (exobj == null) {\r
+ return null;\r
+ }\r
+ Record[] records = run._records;\r
+ if(records != null) find(records, exobj, lst);\r
+\r
+ Hyperlink[] links = null;\r
+ if (lst.size() > 0){\r
+ links = new Hyperlink[lst.size()];\r
+ lst.toArray(links);\r
+ }\r
+ return links;\r
+ }\r
+\r
+ /**\r
+ * Find hyperlink assigned to the supplied shape\r
+ *\r
+ * @param shape <code>Shape</code> to lookup hyperlink in\r
+ * @return found hyperlink or <code>null</code>\r
+ */\r
+ protected static Hyperlink find(Shape shape){\r
+ ArrayList lst = new ArrayList();\r
+ SlideShow ppt = shape.getSheet().getSlideShow();\r
+ //document-level container which stores info about all links in a presentation\r
+ ExObjList exobj = ppt.getDocumentRecord().getExObjList();\r
+ if (exobj == null) {\r
+ return null;\r
+ }\r
+\r
+ EscherContainerRecord spContainer = shape.getSpContainer();\r
+ List spchild = spContainer.getChildRecords();\r
+ for (Iterator it = spchild.iterator(); it.hasNext(); ) {\r
+ EscherRecord obj = (EscherRecord)it.next();\r
+ if (obj.getRecordId() == EscherClientDataRecord.RECORD_ID){\r
+ byte[] data = ((EscherContainerRecord)obj).serialize();\r
+ Record[] records = Record.findChildRecords(data, 8, data.length-8);\r
+ if(records != null) find(records, exobj, lst);\r
+ }\r
+ }\r
+\r
+ return lst.size() == 1 ? (Hyperlink)lst.get(0) : null;\r
+ }\r
+\r
+ private static void find(Record[] records, ExObjList exobj, List out){\r
+ for (int i = 0; i < records.length; i++) {\r
+ //see if we have InteractiveInfo in the textrun's records\r
+ if( records[i] instanceof InteractiveInfo){\r
+ InteractiveInfo hldr = (InteractiveInfo)records[i];\r
+ InteractiveInfoAtom info = hldr.getInteractiveInfoAtom();\r
+ int id = info.getHyperlinkID();\r
+ ExHyperlink linkRecord = exobj.get(id);\r
+ if (linkRecord != null){\r
+ Hyperlink link = new Hyperlink();\r
+ link.title = linkRecord.getLinkTitle();\r
+ link.address = linkRecord.getLinkURL();\r
+ link.type = info.getAction();\r
+\r
+ if (++i < records.length && records[i] instanceof TxInteractiveInfoAtom){\r
+ TxInteractiveInfoAtom txinfo = (TxInteractiveInfoAtom)records[i];\r
+ link.startIndex = txinfo.getStartIndex();\r
+ link.endIndex = txinfo.getEndIndex();\r
+ }\r
+ out.add(link);\r
+ }\r
+ }\r
+ }\r
+ }\r
+}\r
// For logging
protected POILogger logger = POILogFactory.getLogger(this.getClass());
-
+
/**
* In Escher absolute distances are specified in
* English Metric Units (EMUs), occasionally referred to as A units;
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);
+ }
+
}
return spRecord.getOptions() >> 4;
}
+ /**
+ * Returns <code>null</code> - shape groups can't have hyperlinks
+ *
+ * @return <code>null</code>.
+ */
+ public Hyperlink getHyperlink(){
+ return null;
+ }
+
}
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;
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++) {
}
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 {
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);
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.io.IOException;
-import java.util.Vector;
/**
* Represents a TextFrame shape in PowerPoint.
* @return the text string for this textbox.
*/
public String getText(){
- return _txtrun.getText();
+ return _txtrun == null ? null : _txtrun.getText();
}
/**
}
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);
}
- }
-
- // 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");
+ 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);
+ }
}
+
}
}
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
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);
+ }
}
{
private EscherTextboxRecord _escherRecord;
private long _type;
+ private int shapeId;
/**
* Returns the underlying DDF Escher Record
// 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;
+ }
}
/**
* 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();
+ }
+
/**
* Sets the URL of the link
* TODO: Figure out if we should always set both
* 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();
}
/**
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;
+ }
}
* (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.
*/
* 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 byte getAction() {
+ return _data[8];
+ }
+
+ /**
+ * Hyperlink Action
+ * <p>
+ * see <code>ACTION_*</code> constants for the list of actions
+ * </p>
+ *
+ * @param val hyperlink action.
*/
- public int _getNumber3() {
- return LittleEndian.getInt(_data,8);
+ public void setAction(byte val) {
+ _data[8] = val;
}
- protected void _setNumber3(int val) {
- LittleEndian.putInt(_data, 8, val);
+
+ /**
+ * Only valid when action == OLEAction. OLE verb to use, 0 = first verb, 1 = second verb, etc.
+ */
+ public byte getOleVerb() {
+ return _data[9];
}
/**
- * Get the fourth number - meaning unknown
+ * Only valid when action == OLEAction. OLE verb to use, 0 = first verb, 1 = second verb, etc.
*/
- public int _getNumber4() {
- return LittleEndian.getInt(_data,12);
+ public void setOleVerb(byte val) {
+ _data[9] = val;
}
- protected void _setNumber4(int val) {
- LittleEndian.putInt(_data, 12, 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;
}
/**
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()) {
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);
--- /dev/null
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.record;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+import java.io.OutputStream;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * Tne atom that holds starting and ending character positions of a hyperlink\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TxInteractiveInfoAtom extends RecordAtom {\r
+ /**\r
+ * Record header.\r
+ */\r
+ private byte[] _header;\r
+\r
+ /**\r
+ * Record data.\r
+ */\r
+ private byte[] _data;\r
+\r
+ /**\r
+ * Constructs a brand new link related atom record.\r
+ */\r
+ protected TxInteractiveInfoAtom() {\r
+ _header = new byte[8];\r
+ _data = new byte[8];\r
+\r
+ LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+ LittleEndian.putInt(_header, 4, _data.length);\r
+ }\r
+\r
+ /**\r
+ * Constructs the link related atom record from its\r
+ * source data.\r
+ *\r
+ * @param source the source data as a byte array.\r
+ * @param start the start offset into the byte array.\r
+ * @param len the length of the slice in the byte array.\r
+ */\r
+ protected TxInteractiveInfoAtom(byte[] source, int start, int len) {\r
+ // Get the header.\r
+ _header = new byte[8];\r
+ System.arraycopy(source,start,_header,0,8);\r
+\r
+ // Get the record data.\r
+ _data = new byte[len-8];\r
+ System.arraycopy(source,start+8,_data,0,len-8);\r
+\r
+ }\r
+\r
+ /**\r
+ * Gets the beginning character position\r
+ *\r
+ * @return the beginning character position\r
+ */\r
+ public int getStartIndex() {\r
+ return LittleEndian.getInt(_data, 0);\r
+ }\r
+\r
+ /**\r
+ * Sets the beginning character position\r
+ * @param idx the beginning character position\r
+ */\r
+ public void setStartIndex(int idx) {\r
+ LittleEndian.putInt(_data, 0, idx);\r
+ }\r
+\r
+ /**\r
+ * Gets the ending character position\r
+ *\r
+ * @return the ending character position\r
+ */\r
+ public int getEndIndex() {\r
+ return LittleEndian.getInt(_data, 4);\r
+ }\r
+\r
+ /**\r
+ * Sets the ending character position\r
+ *\r
+ * @param idx the ending character position\r
+ */\r
+ public void setEndIndex(int idx) {\r
+ LittleEndian.putInt(_data, 4, idx);\r
+ }\r
+\r
+ /**\r
+ * Gets the record type.\r
+ * @return the record type.\r
+ */\r
+ public long getRecordType() { return RecordTypes.TxInteractiveInfoAtom.typeID; }\r
+\r
+ /**\r
+ * Write the contents of the record back, so it can be written\r
+ * to disk\r
+ *\r
+ * @param out the output stream to write to.\r
+ * @throws java.io.IOException if an error occurs.\r
+ */\r
+ public void writeOut(OutputStream out) throws IOException {\r
+ out.write(_header);\r
+ out.write(_data);\r
+ }\r
+}\r
--- /dev/null
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.model;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import java.io.FileInputStream;\r
+import java.io.File;\r
+\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+\r
+/**\r
+ * Test Hyperlink.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestHyperlink extends TestCase {\r
+ protected String cwd = System.getProperty("HSLF.testdata.path");\r
+\r
+ public void testTextRunHyperlinks() throws Exception {\r
+ FileInputStream is = new FileInputStream(new File(cwd, "WithLinks.ppt"));\r
+ SlideShow ppt = new SlideShow(is);\r
+ is.close();\r
+\r
+ TextRun[] run;\r
+ Slide slide;\r
+ slide = ppt.getSlides()[0];\r
+ run = slide.getTextRuns();\r
+ for (int i = 0; i < run.length; i++) {\r
+ String text = run[i].getText();\r
+ if (text.equals(\r
+ "This page has two links:\n" +\r
+ "http://jakarta.apache.org/poi/\n" +\r
+ "\n" +\r
+ "http://slashdot.org/\n" +\r
+ "\n" +\r
+ "In addition, its notes has one link")){\r
+\r
+ Hyperlink[] links = run[i].getHyperlinks();\r
+ assertNotNull(links);\r
+ assertEquals(2, links.length);\r
+\r
+ assertEquals("http://jakarta.apache.org/poi/", links[0].getTitle());\r
+ assertEquals("http://jakarta.apache.org/poi/", links[0].getAddress());\r
+ assertEquals("http://jakarta.apache.org/poi/", text.substring(links[0].getStartIndex(), links[0].getEndIndex()-1));\r
+\r
+ assertEquals("http://slashdot.org/", links[1].getTitle());\r
+ assertEquals("http://slashdot.org/", links[1].getAddress());\r
+ assertEquals("http://slashdot.org/", text.substring(links[1].getStartIndex(), links[1].getEndIndex()-1));\r
+\r
+ }\r
+ }\r
+\r
+ slide = ppt.getSlides()[1];\r
+ run = slide.getTextRuns();\r
+ for (int i = 0; i < run.length; i++) {\r
+ String text = run[i].getText();\r
+ if (text.equals(\r
+ "I have the one link:\n" +\r
+ "Jakarta HSSF")){\r
+\r
+ Hyperlink[] links = run[i].getHyperlinks();\r
+ assertNotNull(links);\r
+ assertEquals(1, links.length);\r
+\r
+ assertEquals("http://jakarta.apache.org/poi/hssf/", links[0].getTitle());\r
+ assertEquals("http://jakarta.apache.org/poi/hssf/", links[0].getAddress());\r
+ assertEquals("Jakarta HSSF", text.substring(links[0].getStartIndex(), links[0].getEndIndex()-1));\r
+\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+}\r
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];
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 {
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();
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 {
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();
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();
--- /dev/null
+\r
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+ \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.text.SimpleDateFormat;\r
+import java.util.Date;\r
+\r
+/**\r
+ * Tests that TxInteractiveInfoAtom works properly.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestTxInteractiveInfoAtom extends TestCase {\r
+ // From WithLinks.ppt \r
+ private byte[] data_a = new byte[] { \r
+ 00, 00, (byte)0xDF, 0x0F, 0x08, 00, 00, 00,\r
+ 0x19, 00, 00, 00, 0x38, 00, 00, 00\r
+ };\r
+\r
+ private byte[] data_b = new byte[] {\r
+ 00, 00, (byte)0xDF, 0x0F, 0x08, 00, 00, 00,\r
+ 0x39, 00, 00, 00, 0x4E, 00, 00, 00\r
+ };\r
+\r
+ public void testRead() throws Exception {\r
+ TxInteractiveInfoAtom ia1 = new TxInteractiveInfoAtom(data_a, 0, data_a.length);\r
+\r
+ assertEquals(4063, ia1.getRecordType());\r
+ assertEquals(25, ia1.getStartIndex());\r
+ assertEquals(56, ia1.getEndIndex());\r
+\r
+ TxInteractiveInfoAtom ia2 = new TxInteractiveInfoAtom(data_b, 0, data_b.length);\r
+\r
+ assertEquals(4063, ia2.getRecordType());\r
+ assertEquals(57, ia2.getStartIndex());\r
+ assertEquals(78, ia2.getEndIndex());\r
+ }\r
+ \r
+ public void testWrite() throws Exception {\r
+ TxInteractiveInfoAtom atom = new TxInteractiveInfoAtom(data_a, 0, data_a.length);\r
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+ atom.writeOut(baos);\r
+ byte[] b = baos.toByteArray();\r
+\r
+ assertEquals(data_a.length, b.length);\r
+ for(int i=0; i<data_a.length; i++) {\r
+ assertEquals(data_a[i],b[i]);\r
+ }\r
+ }\r
+\r
+ // Create A from scratch\r
+ public void testCreate() throws Exception {\r
+ TxInteractiveInfoAtom ia = new TxInteractiveInfoAtom();\r
+ \r
+ // Set values\r
+ ia.setStartIndex(25);\r
+ ia.setEndIndex(56);\r
+\r
+ // Check it's now the same as a\r
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+ ia.writeOut(baos);\r
+ byte[] b = baos.toByteArray();\r
+ \r
+ assertEquals(data_a.length, b.length);\r
+ for(int i=0; i<data_a.length; i++) {\r
+ assertEquals(data_a[i],b[i]);\r
+ }\r
+ }\r
+\r
+ // Try to turn a into b\r
+ public void testChange() throws Exception {\r
+ TxInteractiveInfoAtom ia = new TxInteractiveInfoAtom(data_a, 0, data_a.length);\r
+\r
+ // Change the number\r
+ ia.setStartIndex(57);\r
+ ia.setEndIndex(78);\r
+\r
+ // Check bytes are now the same\r
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+ ia.writeOut(baos);\r
+ byte[] b = baos.toByteArray();\r
+\r
+ // Should now be the same\r
+ assertEquals(data_b.length, b.length);\r
+ for(int i=0; i<data_b.length; i++) {\r
+ assertEquals(data_b[i],b[i]);\r
+ }\r
+ }\r
+}\r