aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndreas Beeker <kiwiwings@apache.org>2013-12-26 00:46:16 +0000
committerAndreas Beeker <kiwiwings@apache.org>2013-12-26 00:46:16 +0000
commitc0c40081247a229ca542ef621123ff92bd7ecd2c (patch)
tree34a187237412293452c6c648056fefe099ca7260 /src
parent8372eb89ecc27fa75d7a031c5f61afa99e9e58e4 (diff)
downloadpoi-c0c40081247a229ca542ef621123ff92bd7ecd2c.tar.gz
poi-c0c40081247a229ca542ef621123ff92bd7ecd2c.zip
Bug 55579 - [PATCH] Patch for add/embed OLE objects into HSLF
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1553435 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r--src/java/org/apache/poi/hpsf/ClassID.java6
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java38
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/record/ExEmbedAtom.java10
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java252
-rw-r--r--src/scratchpad/testcases/org/apache/poi/hslf/model/TestOleEmbedding.java59
5 files changed, 276 insertions, 89 deletions
diff --git a/src/java/org/apache/poi/hpsf/ClassID.java b/src/java/org/apache/poi/hpsf/ClassID.java
index 9fab2227a8..a49d9df588 100644
--- a/src/java/org/apache/poi/hpsf/ClassID.java
+++ b/src/java/org/apache/poi/hpsf/ClassID.java
@@ -34,6 +34,12 @@ public class ClassID
public static final ClassID PPT_SHOW = new ClassID("{64818D10-4F9B-11CF-86EA-00AA00B929E8}");
public static final ClassID XLS_WORKBOOK = new ClassID("{00020841-0000-0000-C000-000000000046}");
public static final ClassID TXT_ONLY = new ClassID("{5e941d80-bf96-11cd-b579-08002b30bfeb}"); // ???
+ public static final ClassID EXCEL97 = new ClassID("{00020820-0000-0000-C000-000000000046}");
+ public static final ClassID EXCEL95 = new ClassID("{00020810-0000-0000-C000-000000000046}");
+ public static final ClassID WORD97 = new ClassID("{00020906-0000-0000-C000-000000000046}");
+ public static final ClassID WORD95 = new ClassID("{00020900-0000-0000-C000-000000000046}");
+ public static final ClassID POWERPOINT97 = new ClassID("{64818D10-4F9B-11CF-86EA-00AA00B929E8}");
+ public static final ClassID POWERPOINT95 = new ClassID("{EA7BAE70-FB3B-11CD-A903-00AA00510EA3}");
/**
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java
index 717f7a06be..659b340e1c 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java
@@ -23,6 +23,8 @@ import org.apache.poi.hslf.usermodel.ObjectData;
import org.apache.poi.hslf.record.ExObjList;
import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.ExEmbed;
+import org.apache.poi.hslf.record.RecordTypes;
+import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.POILogger;
@@ -74,6 +76,42 @@ public final class OLEShape extends Picture {
}
/**
+ * Set the unique identifier for the OLE object and
+ * register it in the necessary structures
+ *
+ * @param objectId the unique identifier for the OLE object
+ */
+ public void setObjectID(int objectId){
+ setEscherProperty(EscherProperties.BLIP__PICTUREID, objectId);
+
+ EscherContainerRecord ecr = getSpContainer();
+ EscherSpRecord spRecord = ecr.getChildById(EscherSpRecord.RECORD_ID);
+ spRecord.setFlags(spRecord.getFlags()|EscherSpRecord.FLAG_OLESHAPE);
+
+ EscherContainerRecord uerCont = ecr.getChildById((short)RecordTypes.EscherClientData);
+ if (uerCont == null) {
+ uerCont = new EscherContainerRecord();
+ ecr.addChildRecord(uerCont);
+ }
+ uerCont.setRecordId((short)RecordTypes.EscherClientData);
+ uerCont.setVersion((short)0x000F); // yes, we are still a container ...
+
+ UnknownEscherRecord uer = uerCont.getChildById((short)RecordTypes.ExObjRefAtom.typeID);
+ if (uer == null) {
+ uer = new UnknownEscherRecord();
+ uerCont.addChildRecord(uer);
+ }
+
+ byte uerData[] = new byte[12];
+ LittleEndian.putShort( uerData, 0, (short)0 ); // options = 0
+ LittleEndian.putShort( uerData, 2, (short)RecordTypes.ExObjRefAtom.typeID); // recordId
+ LittleEndian.putInt( uerData, 4, 4 ); // remaining bytes
+ LittleEndian.putInt( uerData, 8, objectId ); // the data
+ uer.fillFields(uerData, 0, null);
+ }
+
+
+ /**
* Returns unique identifier for the OLE object.
*
* @return the unique identifier for the OLE object
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbedAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbedAtom.java
index d2344c4154..94aba471de 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbedAtom.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbedAtom.java
@@ -70,7 +70,7 @@ public class ExEmbedAtom extends RecordAtom {
*/
protected ExEmbedAtom() {
_header = new byte[8];
- _data = new byte[7];
+ _data = new byte[8];
LittleEndian.putShort(_header, 2, (short)getRecordType());
LittleEndian.putInt(_header, 4, _data.length);
@@ -94,8 +94,8 @@ public class ExEmbedAtom extends RecordAtom {
_data = new byte[len-8];
System.arraycopy(source,start+8,_data,0,len-8);
- // Must be at least 4 bytes long
- if(_data.length < 7) {
+ // Must be at least 8 bytes long
+ if(_data.length < 8) {
throw new IllegalArgumentException("The length of the data for a ExEmbedAtom must be at least 4 bytes, but was only " + _data.length);
}
}
@@ -120,6 +120,10 @@ public class ExEmbedAtom extends RecordAtom {
return _data[4] != 0;
}
+ public void setCantLockServerB(boolean cantBeLocked) {
+ _data[4] = (byte)(cantBeLocked ? 1 : 0);
+ }
+
/**
* Gets whether it is not required to send the dimensions to the embedded object.
*
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
index ca7d839ebb..edb46d06c1 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
@@ -18,6 +18,7 @@
package org.apache.poi.hslf.usermodel;
import java.awt.Dimension;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -30,6 +31,7 @@ import org.apache.poi.ddf.EscherBSERecord;
import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.ddf.EscherOptRecord;
import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.hpsf.ClassID;
import org.apache.poi.hslf.HSLFSlideShow;
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
import org.apache.poi.hslf.exceptions.HSLFException;
@@ -38,6 +40,8 @@ import org.apache.poi.hslf.model.Notes;
import org.apache.poi.hslf.model.Slide;
import org.apache.poi.hslf.record.*;
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@@ -723,53 +727,10 @@ public final class SlideShow {
// Add the core records for this new Slide to the record tree
org.apache.poi.hslf.record.Slide slideRecord = slide.getSlideRecord();
- int slideRecordPos = _hslfSlideShow.appendRootLevelRecord(slideRecord);
- _records = _hslfSlideShow.getRecords();
-
- // Add the new Slide into the PersistPtr stuff
- int offset = 0;
- int slideOffset = 0;
- PersistPtrHolder ptr = null;
- UserEditAtom usr = null;
- for (int i = 0; i < _records.length; i++) {
- Record record = _records[i];
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- try {
- record.writeOut(out);
- } catch (IOException e) {
- throw new HSLFException(e);
- }
-
- // Grab interesting records as they come past
- if (_records[i].getRecordType() == RecordTypes.PersistPtrIncrementalBlock.typeID) {
- ptr = (PersistPtrHolder) _records[i];
- }
- if (_records[i].getRecordType() == RecordTypes.UserEditAtom.typeID) {
- usr = (UserEditAtom) _records[i];
- }
-
- if (i == slideRecordPos) {
- slideOffset = offset;
- }
- offset += out.size();
- }
-
- // persist ID is UserEditAtom.maxPersistWritten + 1
- int psrId = usr.getMaxPersistWritten() + 1;
+ int psrId = addPersistentObject(slideRecord);
sp.setRefID(psrId);
slideRecord.setSheetId(psrId);
-
- // Last view is now of the slide
- usr.setLastViewType((short) UserEditAtom.LAST_VIEW_SLIDE_VIEW);
- usr.setMaxPersistWritten(psrId); // increment the number of persit
- // objects
-
- // Add the new slide into the last PersistPtr
- // (Also need to tell it where it is)
- slideRecord.setLastOnDiskOffset(slideOffset);
- ptr.addSlideLookup(sp.getRefID(), slideOffset);
- logger.log(POILogger.INFO, "New slide ended up at " + slideOffset);
-
+
slide.setMasterSheet(_masters[0]);
// All done and added
return slide;
@@ -978,16 +939,6 @@ public final class SlideShow {
* @return 0-based index of the movie
*/
public int addMovie(String path, int type) {
- ExObjList lst = (ExObjList) _documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID);
- if (lst == null) {
- lst = new ExObjList();
- _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom());
- }
-
- ExObjListAtom objAtom = lst.getExObjListAtom();
- // increment the object ID seed
- int objectId = (int) objAtom.getObjectIDSeed() + 1;
- objAtom.setObjectIDSeed(objectId);
ExMCIMovie mci;
switch (type) {
case MovieShape.MOVIE_MPEG:
@@ -1000,11 +951,13 @@ public final class SlideShow {
throw new IllegalArgumentException("Unsupported Movie: " + type);
}
- lst.appendChildRecord(mci);
ExVideoContainer exVideo = mci.getExVideo();
- exVideo.getExMediaAtom().setObjectId(objectId);
exVideo.getExMediaAtom().setMask(0xE80000);
exVideo.getPathAtom().setText(path);
+
+ int objectId = addToObjListAtom(mci);
+ exVideo.getExMediaAtom().setObjectId(objectId);
+
return objectId;
}
@@ -1019,27 +972,18 @@ public final class SlideShow {
* @return 0-based index of the control
*/
public int addControl(String name, String progId) {
- ExObjList lst = (ExObjList) _documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID);
- if (lst == null) {
- lst = new ExObjList();
- _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom());
- }
- ExObjListAtom objAtom = lst.getExObjListAtom();
- // increment the object ID seed
- int objectId = (int) objAtom.getObjectIDSeed() + 1;
- objAtom.setObjectIDSeed(objectId);
ExControl ctrl = new ExControl();
+ ctrl.setProgId(progId);
+ ctrl.setMenuName(name);
+ ctrl.setClipboardName(name);
+
ExOleObjAtom oleObj = ctrl.getExOleObjAtom();
- oleObj.setObjID(objectId);
oleObj.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE);
oleObj.setType(ExOleObjAtom.TYPE_CONTROL);
oleObj.setSubType(ExOleObjAtom.SUBTYPE_DEFAULT);
-
- ctrl.setProgId(progId);
- ctrl.setMenuName(name);
- ctrl.setClipboardName(name);
- lst.addChildAfter(ctrl, objAtom);
-
+
+ int objectId = addToObjListAtom(ctrl);
+ oleObj.setObjID(objectId);
return objectId;
}
@@ -1049,28 +993,166 @@ public final class SlideShow {
* @return 0-based index of the hyperlink
*/
public int addHyperlink(Hyperlink link) {
- ExObjList lst = (ExObjList) _documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID);
- if (lst == null) {
- lst = new ExObjList();
- _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom());
- }
- ExObjListAtom objAtom = lst.getExObjListAtom();
- // increment the object ID seed
- int objectId = (int) objAtom.getObjectIDSeed() + 1;
- objAtom.setObjectIDSeed(objectId);
-
ExHyperlink ctrl = new ExHyperlink();
ExHyperlinkAtom obj = ctrl.getExHyperlinkAtom();
- obj.setNumber(objectId);
if(link.getType() == Hyperlink.LINK_SLIDENUMBER) {
ctrl.setLinkURL(link.getAddress(), 0x30);
} else {
ctrl.setLinkURL(link.getAddress());
}
ctrl.setLinkTitle(link.getTitle());
- lst.addChildAfter(ctrl, objAtom);
+
+ int objectId = addToObjListAtom(ctrl);
link.setId(objectId);
+ obj.setNumber(objectId);
+
+ return objectId;
+ }
+
+ /**
+ * Add a embedded object to this presentation
+ *
+ * @return 0-based index of the embedded object
+ */
+ public int addEmbed(POIFSFileSystem poiData) {
+ DirectoryNode root = poiData.getRoot();
+
+ // prepare embedded data
+ if (new ClassID().equals(root.getStorageClsid())) {
+ // need to set class id
+ Map<String,ClassID> olemap = getOleMap();
+ ClassID classID = null;
+ for (Map.Entry<String,ClassID> entry : olemap.entrySet()) {
+ if (root.hasEntry(entry.getKey())) {
+ classID = entry.getValue();
+ break;
+ }
+ }
+ if (classID == null) {
+ throw new IllegalArgumentException("Unsupported embedded document");
+ }
+
+ root.setStorageClsid(classID);
+ }
+
+ ExEmbed exEmbed = new ExEmbed();
+ // remove unneccessary infos, so we don't need to specify the type
+ // of the ole object multiple times
+ Record children[] = exEmbed.getChildRecords();
+ exEmbed.removeChild(children[2]);
+ exEmbed.removeChild(children[3]);
+ exEmbed.removeChild(children[4]);
+
+ ExEmbedAtom eeEmbed = exEmbed.getExEmbedAtom();
+ eeEmbed.setCantLockServerB(true);
+
+ ExOleObjAtom eeAtom = exEmbed.getExOleObjAtom();
+ eeAtom.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE);
+ eeAtom.setType(ExOleObjAtom.TYPE_EMBEDDED);
+ // eeAtom.setSubType(ExOleObjAtom.SUBTYPE_EXCEL);
+ // should be ignored?!?, see MS-PPT ExOleObjAtom, but Libre Office sets it ...
+ eeAtom.setOptions(1226240);
+
+ ExOleObjStg exOleObjStg = new ExOleObjStg();
+ try {
+ final String OLESTREAM_NAME = "\u0001Ole";
+ if (!root.hasEntry(OLESTREAM_NAME)) {
+ // the following data was taken from an example libre office document
+ // beside this "\u0001Ole" record there were several other records, e.g. CompObj,
+ // OlePresXXX, but it seems, that they aren't neccessary
+ byte oleBytes[] = { 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ poiData.createDocument(new ByteArrayInputStream(oleBytes), OLESTREAM_NAME);
+ }
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ poiData.writeFilesystem(bos);
+ exOleObjStg.setData(bos.toByteArray());
+ } catch (IOException e) {
+ throw new HSLFException(e);
+ }
+
+ int psrId = addPersistentObject(exOleObjStg);
+ exOleObjStg.setPersistId(psrId);
+ eeAtom.setObjStgDataRef(psrId);
+
+ int objectId = addToObjListAtom(exEmbed);
+ eeAtom.setObjID(objectId);
+ return objectId;
+ }
+
+ protected int addToObjListAtom(RecordContainer exObj) {
+ ExObjList lst = (ExObjList) _documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID);
+ if (lst == null) {
+ lst = new ExObjList();
+ _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom());
+ }
+ ExObjListAtom objAtom = lst.getExObjListAtom();
+ // increment the object ID seed
+ int objectId = (int) objAtom.getObjectIDSeed() + 1;
+ objAtom.setObjectIDSeed(objectId);
+ lst.addChildAfter(exObj, objAtom);
+
return objectId;
}
+
+ protected static Map<String,ClassID> getOleMap() {
+ Map<String,ClassID> olemap = new HashMap<String,ClassID>();
+ olemap.put("PowerPoint Document", ClassID.PPT_SHOW);
+ olemap.put("Workbook", ClassID.EXCEL97); // as per BIFF8 spec
+ olemap.put("WORKBOOK", ClassID.EXCEL97); // Typically from third party programs
+ olemap.put("BOOK", ClassID.EXCEL97); // Typically odd Crystal Reports exports
+ // ... to be continued
+ return olemap;
+ }
+
+ protected int addPersistentObject(PositionDependentRecord slideRecord) {
+ int slideRecordPos = _hslfSlideShow.appendRootLevelRecord((Record)slideRecord);
+ _records = _hslfSlideShow.getRecords();
+
+ // Add the new Slide into the PersistPtr stuff
+ int offset = 0;
+ int slideOffset = 0;
+ PersistPtrHolder ptr = null;
+ UserEditAtom usr = null;
+ int i = 0;
+ for (Record record : _records) {
+ // Grab interesting records as they come past
+ int recordType = (int)record.getRecordType();
+ if (recordType == RecordTypes.PersistPtrIncrementalBlock.typeID) {
+ ptr = (PersistPtrHolder)record;
+ }
+ if (recordType == RecordTypes.UserEditAtom.typeID) {
+ usr = (UserEditAtom)record;
+ }
+
+ if (i++ == slideRecordPos) {
+ slideOffset = offset;
+ }
+
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ record.writeOut(out);
+ offset += out.size();
+ } catch (IOException e) {
+ throw new HSLFException(e);
+ }
+ }
+
+ // persist ID is UserEditAtom.maxPersistWritten + 1
+ int psrId = usr.getMaxPersistWritten() + 1;
+
+ // Last view is now of the slide
+ usr.setLastViewType((short) UserEditAtom.LAST_VIEW_SLIDE_VIEW);
+ // increment the number of persistent objects
+ usr.setMaxPersistWritten(psrId);
+
+ // Add the new slide into the last PersistPtr
+ // (Also need to tell it where it is)
+ slideRecord.setLastOnDiskOffset(slideOffset);
+ ptr.addSlideLookup(psrId, slideOffset);
+ logger.log(POILogger.INFO, "New slide/object ended up at " + slideOffset);
+
+ return psrId;
+ }
}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestOleEmbedding.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestOleEmbedding.java
index 28445210fb..a6819388d1 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestOleEmbedding.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestOleEmbedding.java
@@ -17,8 +17,16 @@
package org.apache.poi.hslf.model;
+import java.awt.geom.Rectangle2D;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.util.Arrays;
+
import junit.framework.TestCase;
+import org.apache.poi.POIDataSamples;
import org.apache.poi.hslf.HSLFSlideShow;
import org.apache.poi.hslf.usermodel.ObjectData;
import org.apache.poi.hslf.usermodel.PictureData;
@@ -26,7 +34,8 @@ import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hwpf.HWPFDocument;
-import org.apache.poi.POIDataSamples;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.util.IOUtils;
public final class TestOleEmbedding extends TestCase {
private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance();
@@ -82,4 +91,52 @@ public final class TestOleEmbedding extends TestCase {
}
assertEquals("Expected 2 OLE shapes", 2, cnt);
}
+
+ public void testEmbedding() throws Exception {
+ HSLFSlideShow _hslfSlideShow = HSLFSlideShow.create();
+ SlideShow ppt = new SlideShow(_hslfSlideShow);
+
+ File pict = POIDataSamples.getSlideShowInstance().getFile("clock.jpg");
+ int pictId = ppt.addPicture(pict, Picture.JPEG);
+
+ InputStream is = POIDataSamples.getSpreadSheetInstance().openResourceAsStream("Employee.xls");
+ POIFSFileSystem poiData1 = new POIFSFileSystem(is);
+ is.close();
+
+ int oleObjectId1 = ppt.addEmbed(poiData1);
+
+ Slide slide1 = ppt.createSlide();
+ OLEShape oleShape1 = new OLEShape(pictId);
+ oleShape1.setObjectID(oleObjectId1);
+ slide1.addShape(oleShape1);
+ oleShape1.setAnchor(new Rectangle2D.Double(100,100,100,100));
+
+ // add second slide with different order in object creation
+ Slide slide2 = ppt.createSlide();
+ OLEShape oleShape2 = new OLEShape(pictId);
+
+ is = POIDataSamples.getSpreadSheetInstance().openResourceAsStream("SimpleWithImages.xls");
+ POIFSFileSystem poiData2 = new POIFSFileSystem(is);
+ is.close();
+
+ int oleObjectId2 = ppt.addEmbed(poiData2);
+
+ oleShape2.setObjectID(oleObjectId2);
+ slide2.addShape(oleShape2);
+ oleShape2.setAnchor(new Rectangle2D.Double(100,100,100,100));
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ppt.write(bos);
+
+ ppt = new SlideShow(new ByteArrayInputStream(bos.toByteArray()));
+ OLEShape comp = (OLEShape)ppt.getSlides()[0].getShapes()[0];
+ byte compData[] = IOUtils.toByteArray(comp.getObjectData().getData());
+
+ bos.reset();
+ poiData1.writeFilesystem(bos);
+ byte expData[] = bos.toByteArray();
+
+ assertTrue(Arrays.equals(expData, compData));
+
+ }
}