diff --git a/src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java b/src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java index 9a9719b0d..24cf2fae1 100644 --- a/src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java +++ b/src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java @@ -43,8 +43,15 @@ public class EmbeddedObjectRefSubRecord public String field_5_ole_classname; // Classname of the embedded OLE document (e.g. Word.Document.8) public int field_6_stream_id; // ID of the OLE stream containing the actual data. + private int field_5_ole_classname_padding; // developer laziness... + public byte[] remainingBytes; + public EmbeddedObjectRefSubRecord() { + field_2_unknown = new short[0]; + remainingBytes = new byte[0]; + field_1_stream_id_offset = 6; + field_5_ole_classname = ""; } /** @@ -93,19 +100,25 @@ public class EmbeddedObjectRefSubRecord // Padded with NUL bytes. The -2 is because field_1_stream_id_offset // is relative to after the offset field, whereas in.getRecordOffset() - // is relative to the start of this record. + // is relative to the start of this record (minus the header.) + field_5_ole_classname_padding = 0; while (in.getRecordOffset() - 2 < field_1_stream_id_offset) { + field_5_ole_classname_padding++; in.readByte(); // discard } field_6_stream_id = in.readInt(); + remainingBytes = in.readRemainder(); } public int serialize(int offset, byte[] data) { int pos = offset; + LittleEndian.putShort(data, pos, sid); pos += 2; + LittleEndian.putShort(data, pos, (short)(getRecordSize() - 4)); pos += 2; + LittleEndian.putShort(data, pos, field_1_stream_id_offset); pos += 2; LittleEndian.putShortArray(data, pos, field_2_unknown); pos += field_2_unknown.length * 2 + 2; LittleEndian.putShort(data, pos, field_3_unicode_len); pos += 2; @@ -120,11 +133,14 @@ public class EmbeddedObjectRefSubRecord StringUtil.putCompressedUnicode( field_5_ole_classname, data, pos ); pos += field_5_ole_classname.length(); } - // Padded with NUL bytes. - pos = field_1_stream_id_offset; - + // Padded with the same number of NUL bytes as were originally skipped. + // XXX: This is only accurate until we make the classname mutable. + pos += field_5_ole_classname_padding; + LittleEndian.putInt(data, pos, field_6_stream_id); pos += 4; + System.arraycopy(remainingBytes, 0, data, pos, remainingBytes.length); + return getRecordSize(); } @@ -133,8 +149,9 @@ public class EmbeddedObjectRefSubRecord */ public int getRecordSize() { - // Conveniently this stores the length of all the crap before the final int value. - return field_1_stream_id_offset + 4; + // The stream id offset is relative to after the stream ID. + // Add 2 bytes for the stream id offset and 4 bytes for the stream id itself and 4 byts for the record header. + return remainingBytes.length + field_1_stream_id_offset + 2 + 4 + 4; } /** @@ -160,7 +177,7 @@ public class EmbeddedObjectRefSubRecord .append(System.getProperty("line.separator")); buffer.append(" .unknown = ") .append("0x").append(HexDump.toHex( field_2_unknown )) - .append(" (").append( field_2_unknown ).append(" )") + .append(" (").append( field_2_unknown.length ).append(" )") .append(System.getProperty("line.separator")); buffer.append(" .unicodeLen = ") .append("0x").append(HexDump.toHex( field_3_unicode_len )) diff --git a/src/testcases/org/apache/poi/hssf/record/TestEmbeddedObjectRefSubRecord.java b/src/testcases/org/apache/poi/hssf/record/TestEmbeddedObjectRefSubRecord.java new file mode 100644 index 000000000..feb13757f --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/TestEmbeddedObjectRefSubRecord.java @@ -0,0 +1,82 @@ +/* ==================================================================== + 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.hssf.record; + +import junit.framework.TestCase; +import org.apache.poi.util.HexRead; + +import java.io.IOException; +import java.io.ByteArrayInputStream; +import java.util.Arrays; + +/** + * Tests the serialization and deserialization of the TestEmbeddedObjectRefSubRecord + * class works correctly. Test data taken directly from a real + * Excel file. + * + * @author Yegor Kozlov + */ +public class TestEmbeddedObjectRefSubRecord extends TestCase { + + String data1 = "[20, 00, 05, 00, FC, 10, 76, 01, 02, 24, 14, DF, 00, 03, 10, 00, 00, 46, 6F, 72, 6D, 73, 2E, 43, 68, 65, 63, 6B, 42, 6F, 78, 2E, 31, 00, 00, 00, 00, 00, 70, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, ]"; + + public void testStore() throws IOException { + + byte[] src = HexRead.readFromString(data1); + src = TestcaseRecordInputStream.mergeDataAndSid(EmbeddedObjectRefSubRecord.sid, (short)src.length, src); + + RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(src)); + in.nextRecord(); + + EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(in); + + byte[] ser = record1.serialize(); + + RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser)); + in2.nextRecord(); + EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2); + + assertTrue(Arrays.equals(src, ser)); + assertEquals(record1.field_1_stream_id_offset, record2.field_1_stream_id_offset); + assertTrue(Arrays.equals(record1.field_2_unknown, record2.field_2_unknown)); + assertEquals(record1.field_3_unicode_len, record2.field_3_unicode_len); + assertEquals(record1.field_4_unicode_flag, record2.field_4_unicode_flag); + assertEquals(record1.field_5_ole_classname, record2.field_5_ole_classname); + assertEquals(record1.field_6_stream_id, record2.field_6_stream_id); + assertTrue(Arrays.equals(record1.remainingBytes, record2.remainingBytes)); + } + + public void testCreate() throws IOException { + + + EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(); + + byte[] ser = record1.serialize(); + RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser)); + in2.nextRecord(); + EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2); + + assertEquals(record1.field_1_stream_id_offset, record2.field_1_stream_id_offset); + assertTrue(Arrays.equals(record1.field_2_unknown, record2.field_2_unknown)); + assertEquals(record1.field_3_unicode_len, record2.field_3_unicode_len); + assertEquals(record1.field_4_unicode_flag, record2.field_4_unicode_flag); + assertEquals(record1.field_5_ole_classname, record2.field_5_ole_classname); + assertEquals(record1.field_6_stream_id, record2.field_6_stream_id); + assertTrue(Arrays.equals(record1.remainingBytes, record2.remainingBytes)); + + } +}