<!-- Don't forget to update status.xml too! -->
<release version="3.0.3-beta1" date="2008-04-??">
+ <action dev="POI-DEVELOPERS" type="add">HSLF: Support for getting OLE object data from slide show </action>
<action dev="POI-DEVELOPERS" type="add">HSLF: Implemented more methods in PPGraphics2D</action>
<action dev="POI-DEVELOPERS" type="add">HSLF: Added Freeform shape which can contain both lines and Bezier curves</action>
<action dev="POI-DEVELOPERS" type="fix">41071 - Improved text extraction in HSLF</action>
<li><link href="#Hyperlinks">Hyperlinks</link></li>
<li><link href="#Tables">Tables</link></li>
<li><link href="#RemoveShape">How to remove shapes</link></li>
+ <li><link href="#OLE">How to retrieve embedded OLE objects</link></li>
+ <li><link href="#Freeform">How to create shapes of arbitrary geometry</link></li>
+ <li><link href="#Graphics2D">Shapes and Graphics2D</link></li>
</ul>
</section>
<section><title>Features</title>
</section>
<anchor id="GetShapes"/>
<section><title>How to get shapes contained in a particular slide</title>
- <p>The superclass of all shapes in HSLF is the Shape class - the elemental object that composes a drawing.
- The following pictute shows the class tree of HSLF shapes:
- </p>
- <p>
- <img src="images/hslf_shapes.gif" alt="Class Tree of HSLF Shapes" width="611" height="412"/>
- </p>
<p>
- The following fragment demonstrates how to iterate over shapes for each slide.
+ The following code demonstrates how to iterate over shapes for each slide.
</p>
<source>
SlideShow ppt = new SlideShow(new HSLFSlideShow("slideshow.ppt"));
}
</source>
</section>
+ <anchor id="OLE"/>
+ <section><title>How to retrieve embedded OLE objects</title>
+ <source>
- </section>
+ Shape[] shape = slide.getShapes();
+ for (int i = 0; i < shape.length; i++) {
+ if (shape[i] instanceof OLEShape) {
+ OLEShape ole = (OLEShape) shape[i];
+ ObjectData data = ole.getObjectData();
+ String name = ole.getInstanceName();
+ if ("Worksheet".equals(name)) {
+ HSSFWorkbook wb = new HSSFWorkbook(data.getData());
+ } else if ("Document".equals(name)) {
+ HWPFDocument doc = new HWPFDocument(data.getData());
+ }
+ }
+ }
+ </source>
+ </section>
+
+ <anchor id="Freeform"/>
+ <section><title>How to create shapes of arbitrary geometry</title>
+ <source>
+
+ SlideShow ppt = new SlideShow();
+ Slide slide = ppt.createSlide();
+
+ java.awt.geom.GeneralPath path = new java.awt.geom.GeneralPath();
+ path.moveTo(100, 100);
+ path.lineTo(200, 100);
+ path.curveTo(50, 45, 134, 22, 78, 133);
+ path.curveTo(10, 45, 134, 56, 78, 100);
+ path.lineTo(100, 200);
+ path.closePath();
+
+ Freeform shape = new Freeform();
+ shape.setPath(path);
+ slide.addShape(shape);
+ </source>
+ </section>
+
+ <anchor id="Graphics2D"/>
+ <section><title>How to draw into a slide using Graphics2D</title>
+ <warning>
+ Current implementation of the PowerPoint Graphics2D driver is not fully compliant with the java.awt.Graphics2D specification.
+ Some features like clipping, drawing of images are not yet supported.
+ </warning>
+ <source>
+ SlideShow ppt = new SlideShow();
+ Slide slide = ppt.createSlide();
+
+ //draw a simple bar graph
+ //bar chart data. The first value is the bar color, the second is the width
+ Object[] def = new Object[]{
+ Color.yellow, new Integer(100),
+ Color.green, new Integer(150),
+ Color.gray, new Integer(75),
+ Color.red, new Integer(200),
+ };
+
+ //all objects are drawn into a shape group so we need to create one
+
+ ShapeGroup group = new ShapeGroup();
+ //define position of the drawing in the slide
+ Rectangle bounds = new java.awt.Rectangle(200, 100, 350, 300);
+ //if you want to draw in the entire slide area then define the anchor as follows:
+ //Dimension pgsize = ppt.getPageSize();
+ //java.awt.Rectangle bounds = new java.awt.Rectangle(0, 0, pgsize.width, pgsize.height);
+
+ group.setAnchor(bounds);
+ slide.addShape(group);
+
+ //draw a simple bar chart
+ Graphics2D graphics = new PPGraphics2D(group);
+ int x = bounds.x + 50, y = bounds.y + 50;
+ graphics.setFont(new Font("Arial", Font.BOLD, 10));
+ for (int i = 0, idx = 1; i < def.length; i+=2, idx++) {
+ graphics.setColor(Color.black);
+ int width = ((Integer)def[i+1]).intValue();
+ graphics.drawString("Q" + idx, x-20, y+20);
+ graphics.drawString(width + "%", x + width + 10, y + 20);
+ graphics.setColor((Color)def[i]);
+ graphics.fill(new Rectangle(x, y, width, 30));
+ y += 40;
+ }
+ graphics.setColor(Color.black);
+ graphics.setFont(new Font("Arial", Font.BOLD, 14));
+ graphics.draw(bounds);
+ graphics.drawString("Performance", x + 70, y + 40);
+
+ FileOutputStream out = new FileOutputStream("hslf-graphics2d.ppt");
+ ppt.write(out);
+ out.close();
+
+ </source>
+ </section>
+
+ </section>
</section>
</body>
</document>
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.0.3-beta1" date="2008-04-??">
+ <action dev="POI-DEVELOPERS" type="add">HSLF: Support for getting OLE object data from slide show </action>
<action dev="POI-DEVELOPERS" type="add">HSLF: Implemented more methods in PPGraphics2D</action>
<action dev="POI-DEVELOPERS" type="add">HSLF: Added Freeform shape which can contain both lines and Bezier curves</action>
<action dev="POI-DEVELOPERS" type="fix">41071 - Improved text extraction in HSLF</action>
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
import org.apache.poi.POIDocument;
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
import org.apache.poi.hslf.exceptions.HSLFException;
-import org.apache.poi.hslf.record.CurrentUserAtom;
-import org.apache.poi.hslf.record.ExOleObjStg;
-import org.apache.poi.hslf.record.PersistPtrHolder;
-import org.apache.poi.hslf.record.PositionDependentRecord;
-import org.apache.poi.hslf.record.Record;
-import org.apache.poi.hslf.record.UserEditAtom;
+import org.apache.poi.hslf.record.*;
import org.apache.poi.hslf.usermodel.ObjectData;
import org.apache.poi.hslf.usermodel.PictureData;
import org.apache.poi.hslf.model.Shape;
private Record[] read(byte[] docstream, int usrOffset){
ArrayList lst = new ArrayList();
+ HashMap offset2id = new HashMap();
while (usrOffset != 0){
UserEditAtom usr = (UserEditAtom) Record.buildRecordAtOffset(docstream, usrOffset);
lst.add(new Integer(usrOffset));
Integer offset = (Integer)entries.get(id);
lst.add(offset);
+ offset2id.put(offset, id);
}
usrOffset = usr.getLastUserEditAtomOffset();
for (int i = 0; i < a.length; i++) {
Integer offset = (Integer)a[i];
rec[i] = (Record)Record.buildRecordAtOffset(docstream, offset.intValue());
+ if(rec[i] instanceof PersistRecord) {
+ PersistRecord psr = (PersistRecord)rec[i];
+ Integer id = (Integer)offset2id.get(offset);
+ psr.setPersistId(id.intValue());
+ }
}
return rec;
--- /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 org.apache.poi.ddf.*;\r
+import org.apache.poi.hslf.usermodel.PictureData;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hslf.usermodel.ObjectData;\r
+import org.apache.poi.hslf.record.Document;\r
+import org.apache.poi.hslf.record.ExObjList;\r
+import org.apache.poi.hslf.record.Record;\r
+import org.apache.poi.hslf.record.ExEmbed;\r
+import org.apache.poi.hslf.blip.Bitmap;\r
+import org.apache.poi.util.POILogger;\r
+\r
+import javax.imageio.ImageIO;\r
+import java.awt.image.BufferedImage;\r
+import java.awt.*;\r
+import java.io.ByteArrayInputStream;\r
+import java.io.IOException;\r
+import java.util.List;\r
+import java.util.Arrays;\r
+\r
+\r
+/**\r
+ * A shape representing embedded OLE obejct.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class OLEShape extends Picture {\r
+ protected ExEmbed _exEmbed;\r
+\r
+ /**\r
+ * Create a new <code>OLEShape</code>\r
+ *\r
+ * @param idx the index of the picture\r
+ */\r
+ public OLEShape(int idx){\r
+ super(idx);\r
+ }\r
+\r
+ /**\r
+ * Create a new <code>OLEShape</code>\r
+ *\r
+ * @param idx the index of the picture\r
+ * @param parent the parent shape\r
+ */\r
+ public OLEShape(int idx, Shape parent) {\r
+ super(idx, parent);\r
+ }\r
+\r
+ /**\r
+ * Create a <code>OLEShape</code> object\r
+ *\r
+ * @param escherRecord the <code>EscherSpContainer</code> record which holds information about\r
+ * this picture in the <code>Slide</code>\r
+ * @param parent the parent shape of this picture\r
+ */\r
+ protected OLEShape(EscherContainerRecord escherRecord, Shape parent){\r
+ super(escherRecord, parent);\r
+ }\r
+\r
+ /**\r
+ * Returns unique identifier for the OLE object.\r
+ *\r
+ * @return the unique identifier for the OLE object\r
+ */\r
+ public int getObjectID(){\r
+ return getEscherProperty(EscherProperties.BLIP__PICTUREID);\r
+ }\r
+\r
+ /**\r
+ * Returns unique identifier for the OLE object.\r
+ *\r
+ * @return the unique identifier for the OLE object\r
+ */\r
+ public ObjectData getObjectData(){\r
+ SlideShow ppt = getSheet().getSlideShow();\r
+ ObjectData[] ole = ppt.getEmbeddedObjects();\r
+\r
+ //persist reference\r
+ int ref = getExEmbed().getExOleObjAtom().getObjStgDataRef();\r
+ for (int i = 0; i < ole.length; i++) {\r
+ if(ole[i].getExOleObjStg().getPersistId() == ref) return ole[i];\r
+\r
+ }\r
+ logger.log(POILogger.WARN, "OLE data not found");\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Return the record container for this embedded object.\r
+ *\r
+ * <p>\r
+ * It contains:\r
+ * 1. ExEmbedAtom.(4045)\r
+ * 2. ExOleObjAtom (4035)\r
+ * 3. CString (4026), Instance MenuName (1) used for menus and the Links dialog box.\r
+ * 4. CString (4026), Instance ProgID (2) that stores the OLE Programmatic Identifier.\r
+ * A ProgID is a string that uniquely identifies a given object.\r
+ * 5. CString (4026), Instance ClipboardName (3) that appears in the paste special dialog.\r
+ * 6. MetaFile( 4033), optional\r
+ * </p>\r
+ * @return\r
+ */\r
+ public ExEmbed getExEmbed(){\r
+ if(_exEmbed == null){\r
+ SlideShow ppt = getSheet().getSlideShow();\r
+\r
+ ExObjList lst = ppt.getDocumentRecord().getExObjList();\r
+ if(lst == null){\r
+ logger.log(POILogger.WARN, "ExObjList not found");\r
+ return null;\r
+ }\r
+\r
+ int id = getObjectID();\r
+ Record[] ch = lst.getChildRecords();\r
+ for (int i = 0; i < ch.length; i++) {\r
+ if(ch[i] instanceof ExEmbed){\r
+ ExEmbed embd = (ExEmbed)ch[i];\r
+ if( embd.getExOleObjAtom().getObjID() == id) _exEmbed = embd;\r
+ }\r
+ }\r
+ }\r
+ return _exEmbed;\r
+ }\r
+\r
+ /**\r
+ * Returns the instance name of the embedded object, e.g. "Document" or "Workbook".\r
+ *\r
+ * @return the instance name of the embedded object\r
+ */\r
+ public String getInstanceName(){\r
+ return getExEmbed().getMenuName();\r
+ }\r
+\r
+ /**\r
+ * Returns the full name of the embedded object,\r
+ * e.g. "Microsoft Word Document" or "Microsoft Office Excel Worksheet".\r
+ *\r
+ * @return the full name of the embedded object\r
+ */\r
+ public String getFullName(){\r
+ return getExEmbed().getClipboardName();\r
+ }\r
+\r
+ /**\r
+ * Returns the ProgID that stores the OLE Programmatic Identifier.\r
+ * A ProgID is a string that uniquely identifies a given object, for example,\r
+ * "Word.Document.8" or "Excel.Sheet.8".\r
+ *\r
+ * @return the ProgID\r
+ */\r
+ public String getProgID(){\r
+ return getExEmbed().getProgId();\r
+ }\r
+}\r
public int getEscherProperty(short propId){
EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, propId);
- return prop == null ? 0 : prop.getPropertyNumber();
+ return prop == null ? 0 : prop.getPropertyValue();
}
/**
case ShapeTypes.TextBox:
shape = new TextBox(spContainer, parent);
break;
- case ShapeTypes.PictureFrame:
- shape = new Picture(spContainer, parent);
+ case ShapeTypes.PictureFrame: {
+ EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(spContainer, EscherOptRecord.RECORD_ID);
+ EscherProperty prop = Shape.getEscherProperty(opt, EscherProperties.BLIP__PICTUREID);
+ if(prop != null)
+ shape = new OLEShape(spContainer, parent); //presence of BLIP__PICTUREID indicates it is an embedded object
+ else
+ shape = new Picture(spContainer, parent);
break;
+ }
case ShapeTypes.Line:
shape = new Line(spContainer, parent);
break;
- case ShapeTypes.NotPrimitive:
+ case ShapeTypes.NotPrimitive: {
EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(spContainer, EscherOptRecord.RECORD_ID);
EscherProperty prop = Shape.getEscherProperty(opt, EscherProperties.GEOMETRY__VERTICES);
if(prop != null)
shape = new AutoShape(spContainer, parent);
}
break;
+ }
default:
shape = new AutoShape(spContainer, parent);
break;
*
* @author Daniel Noll
*/
-public class ExOleObjStg extends RecordAtom {
+public class ExOleObjStg extends RecordAtom implements PersistRecord {
+
+ private int _persistId; // Found from PersistPtrHolder
+
/**
* Record header.
*/
out.write(_header);
out.write(_data);
}
+
+ /**
+ * Fetch our sheet ID, as found from a PersistPtrHolder.
+ * Should match the RefId of our matching SlidePersistAtom
+ */
+ public int getPersistId() {
+ return _persistId;
+ }
+
+ /**
+ * Set our sheet ID, as found from a PersistPtrHolder
+ */
+ public void setPersistId(int id) {
+ _persistId = id;
+ }
}
--- /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.record;\r
+\r
+/**\r
+ * A record that can be referenced in PersistPtr storage.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public interface PersistRecord {\r
+\r
+ /**\r
+ * Fetch the persist ID\r
+ */\r
+ public int getPersistId();\r
+\r
+ /**\r
+ * Set the persist ID\r
+ */\r
+ public void setPersistId(int id);\r
+}\r
public InputStream getData() {
return storage.getData();
}
+
+ /**
+ * Return the record that contains the object data.
+ *
+ * @return the record that contains the object data.
+ */
+ public ExOleObjStg getExOleObjStg() {
+ return storage;
+ }
}
public PictureData[] getPictureData() {
return _hslfSlideShow.getPictures();
}
-
+
+ /**
+ * Returns the data of all the embedded OLE object in the SlideShow
+ */
+ public ObjectData[] getEmbeddedObjects() {
+ return _hslfSlideShow.getEmbeddedObjects();
+ }
/**
* Return the current page size
*/
package org.apache.poi.hslf.model;\r
\r
import java.io.*;\r
+import java.util.List;\r
+import java.util.ArrayList;\r
\r
import org.apache.poi.hslf.HSLFSlideShow;\r
import org.apache.poi.hslf.usermodel.ObjectData;\r
import org.apache.poi.hslf.usermodel.PictureData;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;\r
+import org.apache.poi.hssf.usermodel.HSSFSheet;\r
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;\r
+import org.apache.poi.hwpf.HWPFDocument;\r
+import org.apache.poi.hwpf.usermodel.Range;\r
+import org.apache.poi.hwpf.usermodel.Paragraph;\r
\r
import junit.framework.TestCase;\r
\r
slideShow.close();\r
}\r
}\r
+\r
+ public void testOLEShape() throws Exception {\r
+ String dirname = System.getProperty("HSLF.testdata.path");\r
+ File file = new File(dirname, "ole2-embedding-2003.ppt");\r
+ FileInputStream is = new FileInputStream(file);\r
+ SlideShow ppt = new SlideShow(is);\r
+ is.close();\r
+\r
+ Slide slide = ppt.getSlides()[0];\r
+ Shape[] sh = slide.getShapes();\r
+ int cnt = 0;\r
+ for (int i = 0; i < sh.length; i++) {\r
+ if(sh[i] instanceof OLEShape){\r
+ cnt++;\r
+ OLEShape ole = (OLEShape)sh[i];\r
+ ObjectData data = ole.getObjectData();\r
+ if("Worksheet".equals(ole.getInstanceName())){\r
+ //Voila! we created a workbook from the embedded OLE data\r
+ HSSFWorkbook wb = new HSSFWorkbook(data.getData());\r
+ HSSFSheet sheet = wb.getSheetAt(0);\r
+ //verify we can access the xls data\r
+ assertEquals(1, sheet.getRow(0).getCell((short)0).getNumericCellValue(), 0);\r
+ assertEquals(1, sheet.getRow(1).getCell((short)0).getNumericCellValue(), 0);\r
+ assertEquals(2, sheet.getRow(2).getCell((short)0).getNumericCellValue(), 0);\r
+ assertEquals(3, sheet.getRow(3).getCell((short)0).getNumericCellValue(), 0);\r
+ assertEquals(8, sheet.getRow(5).getCell((short)0).getNumericCellValue(), 0);\r
+ } else if ("Document".equals(ole.getInstanceName())){\r
+ //creating a HWPF document \r
+ HWPFDocument doc = new HWPFDocument(data.getData());\r
+ String txt = doc.getRange().getParagraph(0).text();\r
+ assertEquals("OLE embedding is thoroughly unremarkable.\r", txt);\r
+ }\r
+ }\r
+\r
+ }\r
+ assertEquals("Expected 2 OLE shapes", 2, cnt);\r
+ }\r
}\r