diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 21a6f6702..688410c89 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,7 @@ + 44840 - Improved handling of HSSFObjectData, especially for entries with data held not in POIFS 45043 - Support for getting excel cell comments when extracting text Extend the support for specifying a policy to HSSF on missing / blank cells when fetching, to be able to specify the policy at the HSSFWorkbook level 45025 - improved FormulaParser parse error messages diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 456ee3ebc..9b81f2a01 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 44840 - Improved handling of HSSFObjectData, especially for entries with data held not in POIFS 45043 - Support for getting excel cell comments when extracting text Extend the support for specifying a policy to HSSF on missing / blank cells when fetching, to be able to specify the policy at the HSSFWorkbook level 45025 - improved FormulaParser parse error messages diff --git a/src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java b/src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java index 24cf2fae1..0e59282f5 100644 --- a/src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java +++ b/src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java @@ -108,7 +108,10 @@ public class EmbeddedObjectRefSubRecord in.readByte(); // discard } - field_6_stream_id = in.readInt(); + // Fetch the stream ID + field_6_stream_id = in.readInt(); + + // Store what's left remainingBytes = in.readRemainder(); } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFObjectData.java b/src/java/org/apache/poi/hssf/usermodel/HSSFObjectData.java index b1c5c66e0..697c33b9e 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFObjectData.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFObjectData.java @@ -55,36 +55,72 @@ public class HSSFObjectData this.record = record; this.poifs = poifs; } + + /** + * Returns the OLE2 Class Name of the object + */ + public String getOLE2ClassName() { + EmbeddedObjectRefSubRecord subRecord = findObjectRecord(); + return subRecord.field_5_ole_classname; + } /** - * Gets the object data. + * Gets the object data. Only call for ones that have + * data though. See {@link #hasDirectoryEntry()} * * @return the object data as an OLE2 directory. * @throws IOException if there was an error reading the data. */ - public DirectoryEntry getDirectory() throws IOException - { - Iterator subRecordIter = record.getSubRecords().iterator(); - while (subRecordIter.hasNext()) - { - Object subRecord = subRecordIter.next(); - if (subRecord instanceof EmbeddedObjectRefSubRecord) - { - int streamId = ((EmbeddedObjectRefSubRecord) subRecord).getStreamId(); - String streamName = "MBD" + HexDump.toHex(streamId); + public DirectoryEntry getDirectory() throws IOException { + EmbeddedObjectRefSubRecord subRecord = findObjectRecord(); - Entry entry = poifs.getRoot().getEntry(streamName); - if (entry instanceof DirectoryEntry) - { - return (DirectoryEntry) entry; - } - else - { - throw new IOException("Stream " + streamName + " was not an OLE2 directory"); - } + int streamId = ((EmbeddedObjectRefSubRecord) subRecord).getStreamId(); + String streamName = "MBD" + HexDump.toHex(streamId); + + Entry entry = poifs.getRoot().getEntry(streamName); + if (entry instanceof DirectoryEntry) { + return (DirectoryEntry) entry; + } else { + throw new IOException("Stream " + streamName + " was not an OLE2 directory"); + } + } + + /** + * Returns the data portion, for an ObjectData + * that doesn't have an associated POIFS Directory + * Entry + */ + public byte[] getObjectData() { + EmbeddedObjectRefSubRecord subRecord = findObjectRecord(); + return subRecord.remainingBytes; + } + + /** + * Does this ObjectData have an associated POIFS + * Directory Entry? + * (Not all do, those that don't have a data portion) + */ + public boolean hasDirectoryEntry() { + EmbeddedObjectRefSubRecord subRecord = findObjectRecord(); + + // Field 6 tells you + return (subRecord.field_6_stream_id != 0); + } + + /** + * Finds the EmbeddedObjectRefSubRecord, or throws an + * Exception if there wasn't one + */ + protected EmbeddedObjectRefSubRecord findObjectRecord() { + Iterator subRecordIter = record.getSubRecords().iterator(); + + while (subRecordIter.hasNext()) { + Object subRecord = subRecordIter.next(); + if (subRecord instanceof EmbeddedObjectRefSubRecord) { + return (EmbeddedObjectRefSubRecord)subRecord; } } - + throw new IllegalStateException("Object data does not contain a reference to an embedded object OLE2 directory"); } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java index 53e06ab29..cbe175a0e 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java @@ -18,14 +18,17 @@ package org.apache.poi.hssf.usermodel; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.Iterator; +import java.util.List; import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord; import org.apache.poi.hssf.util.Region; import org.apache.poi.util.TempFile; @@ -950,4 +953,40 @@ public final class TestBugs extends TestCase { writeOutAndReadBack(wb); assertTrue("no errors writing sample xls", true); } + + /** + * Problems with extracting check boxes from + * HSSFObjectData + * @throws Exception + */ + public void test44840() throws Exception { + HSSFWorkbook wb = openSample("WithCheckBoxes.xls"); + + // Take a look at the embeded objects + List objects = wb.getAllEmbeddedObjects(); + assertEquals(1, objects.size()); + + HSSFObjectData obj = (HSSFObjectData)objects.get(0); + assertNotNull(obj); + + // Peek inside the underlying record + EmbeddedObjectRefSubRecord rec = obj.findObjectRecord(); + assertNotNull(rec); + + assertEquals(32, rec.field_1_stream_id_offset); + assertEquals(0, rec.field_6_stream_id); // WRONG! + assertEquals("Forms.CheckBox.1", rec.field_5_ole_classname); + assertEquals(12, rec.remainingBytes.length); + + // Doesn't have a directory + assertFalse(obj.hasDirectoryEntry()); + assertNotNull(obj.getObjectData()); + assertEquals(12, obj.getObjectData().length); + assertEquals("Forms.CheckBox.1", obj.getOLE2ClassName()); + + try { + obj.getDirectory(); + fail(); + } catch(FileNotFoundException e) {} + } }