// However, EscherTextboxRecord are containers of records for the
// host application, not of other Escher records, so treat them
// differently
- if ( ( options & (short) 0x000F ) == (short) 0x000F
- && recordId != EscherTextboxRecord.RECORD_ID ) {
+ if (isContainer(options, recordId)) {
EscherContainerRecord r = new EscherContainerRecord();
r.setRecordId( recordId );
r.setOptions( options );
}
return result;
}
+
+ public static boolean isContainer(short options, short recordId){
+ if(recordId >= EscherContainerRecord.DGG_CONTAINER && recordId
+ <= EscherContainerRecord.SOLVER_CONTAINER){
+ return true;
+ } else {
+ if (recordId == EscherTextboxRecord.RECORD_ID) {
+ return false;
+ } else {
+ return ( options & (short) 0x000F ) == (short) 0x000F;
+ }
+ }
+ }
}
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
/**
* Escher container records store other escher records as children.
public static final short SP_CONTAINER = (short)0xF004;
public static final short SOLVER_CONTAINER = (short)0xF005;
+ private static POILogger log = POILogFactory.getLogger(EscherContainerRecord.class);
+
+ /**
+ * in case if document contains any charts we have such document structure:
+ * BOF
+ * ...
+ * DrawingRecord
+ * ...
+ * ObjRecord|TxtObjRecord
+ * ...
+ * EOF
+ * ...
+ * BOF(Chart begin)
+ * ...
+ * DrawingRecord
+ * ...
+ * ObjRecord|TxtObjRecord
+ * ...
+ * EOF
+ * So, when we call EscherAggregate.createAggregate() we have not all needed data.
+ * When we got warning "WARNING: " + bytesRemaining + " bytes remaining but no space left"
+ * we should save value of bytesRemaining
+ * and add it to container size when we serialize it
+ */
+ private int _remainingLength;
+
private final List<EscherRecord> _childRecords = new ArrayList<EscherRecord>();
public int fillFields(byte[] data, int pOffset, EscherRecordFactory recordFactory) {
bytesRemaining -= childBytesWritten;
addChildRecord(child);
if (offset >= data.length && bytesRemaining > 0) {
- System.out.println("WARNING: " + bytesRemaining + " bytes remaining but no space left");
+ _remainingLength = bytesRemaining;
+ log.log(POILogger.WARN, "Not enough Escher data: " + bytesRemaining + " bytes remaining but no space left");
}
}
return bytesWritten;
EscherRecord r = iterator.next();
remainingBytes += r.getRecordSize();
}
+ remainingBytes += _remainingLength;
LittleEndian.putInt(data, offset+4, remainingBytes);
int pos = offset+8;
iterator = _childRecords.iterator();
package org.apache.poi.hssf.model;\r
\r
import junit.framework.TestCase;\r
-import org.apache.poi.ddf.EscherContainerRecord;\r
-import org.apache.poi.ddf.EscherDggRecord;\r
+import org.apache.poi.ddf.*;\r
import org.apache.poi.hssf.HSSFTestDataSamples;\r
import org.apache.poi.hssf.record.ContinueRecord;\r
import org.apache.poi.hssf.record.DrawingRecord;\r
import org.apache.poi.hssf.usermodel.HSSFSheet;\r
import org.apache.poi.hssf.usermodel.HSSFTestHelper;\r
import org.apache.poi.hssf.usermodel.HSSFWorkbook;\r
+import org.apache.poi.util.HexDump;\r
import org.apache.poi.util.HexRead;\r
\r
import java.io.ByteArrayInputStream;\r
// System.out.println("[WARN] Cannot read " + file.getName());\r
continue;\r
}\r
- try {\r
- assertWriteAndReadBack(wb);\r
- } catch (Throwable e){\r
- //e.printStackTrace();\r
- System.err.println("[ERROR] assertion failed for " + file.getName() + ": " + e.getMessage());\r
- }\r
+ assertWriteAndReadBack(wb);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * when reading incomplete data ensure that the serialized bytes\r
+ match the source\r
+ */\r
+ public void testIncompleteData(){\r
+ //EscherDgContainer and EscherSpgrContainer length exceeds the actual length of the data\r
+ String hex =\r
+ " 0F 00 02 F0 30 03 00 00 10 00 08 F0 08 00 00 " +\r
+ " 00 07 00 00 00 B2 04 00 00 0F 00 03 F0 18 03 00 " +\r
+ " 00 0F 00 04 F0 28 00 00 00 01 00 09 F0 10 00 00 " +\r
+ " 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " +\r
+ " 00 02 00 0A F0 08 00 00 00 00 04 00 00 05 00 00 " +\r
+ " 00 0F 00 04 F0 74 00 00 00 92 0C 0A F0 08 00 00 " +\r
+ " 00 AD 04 00 00 00 0A 00 00 63 00 0B F0 3A 00 00 " +\r
+ " 00 7F 00 04 01 E5 01 BF 00 08 00 08 00 81 01 4E " +\r
+ " 00 00 08 BF 01 10 00 10 00 80 C3 16 00 00 00 BF " +\r
+ " 03 00 00 02 00 44 00 69 00 61 00 67 00 72 00 61 " +\r
+ " 00 6D 00 6D 00 20 00 32 00 00 00 00 00 10 F0 12 " +\r
+ " 00 00 00 00 00 05 00 00 00 01 00 00 00 0B 00 00 " +\r
+ " 00 0F 00 66 00 00 00 11 F0 00 00 00 00 ";\r
+ byte[] buffer = HexRead.readFromString(hex);\r
+\r
+ List<EscherRecord> records = new ArrayList<EscherRecord>();\r
+ EscherRecordFactory recordFactory = new DefaultEscherRecordFactory();\r
+ int pos = 0;\r
+ while (pos < buffer.length) {\r
+ EscherRecord r = recordFactory.createRecord(buffer, pos);\r
+ int bytesRead = r.fillFields(buffer, pos, recordFactory);\r
+ records.add(r);\r
+ pos += bytesRead;\r
+ }\r
+ assertEquals("data was not fully read", buffer.length, pos);\r
+\r
+ // serialize to byte array\r
+ ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+ try {\r
+ for(EscherRecord r : records) out.write(r.serialize());\r
+ } catch (IOException e){\r
+ throw new RuntimeException(e);\r
}\r
+ assertEquals(HexDump.toHex(buffer, 10), HexDump.toHex(out.toByteArray(), 10));\r
}\r
\r
/**\r
assertTrue("drawing data before and after save is different", Arrays.equals(dgBytes, dgBytesAfterSave));\r
}\r
\r
+ public void testFileWithCharts(){\r
+ HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("49581.xls");\r
+ HSSFSheet sh = wb.getSheetAt(0);\r
+ InternalSheet ish = HSSFTestHelper.getSheetForTest(sh);\r
+ List<RecordBase> records = ish.getRecords();\r
+ // records to be aggregated\r
+ List<RecordBase> dgRecords = records.subList(19, 21);\r
+ byte[] dgBytes = toByteArray(dgRecords);\r
+ sh.getDrawingPatriarch();\r
+\r
+ // collect drawing records into a byte buffer.\r
+ EscherAggregate agg = (EscherAggregate) ish.findFirstRecordBySid(EscherAggregate.sid);\r
+ byte[] dgBytesAfterSave = agg.serialize();\r
+ assertEquals("different size of drawing data before and after save", dgBytes.length, dgBytesAfterSave.length);\r
+ for (int i=0; i< dgBytes.length; i++){\r
+ if (dgBytes[i] != dgBytesAfterSave[i]){\r
+ System.out.println("pos = " + i);\r
+ }\r
+ }\r
+ assertTrue("drawing data before and after save is different", Arrays.equals(dgBytes, dgBytesAfterSave));\r
+ }\r
+\r
/**\r
* test reading drawing aggregate from a test file from Bugzilla 45129\r
*/\r
--- /dev/null
+package org.apache.poi.hssf.model;\r
+\r
+import junit.framework.TestCase;\r
+import org.apache.poi.ddf.DefaultEscherRecordFactory;\r
+import org.apache.poi.ddf.EscherContainerRecord;\r
+import org.apache.poi.ddf.EscherTextboxRecord;\r
+import org.apache.poi.hssf.HSSFTestDataSamples;\r
+import org.apache.poi.hssf.record.EscherAggregate;\r
+import org.apache.poi.hssf.record.Record;\r
+import org.apache.poi.hssf.record.RecordBase;\r
+import org.apache.poi.hssf.usermodel.HSSFSheet;\r
+import org.apache.poi.hssf.usermodel.HSSFTestHelper;\r
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+import java.util.Arrays;\r
+import java.util.List;\r
+import java.util.Random;\r
+\r
+/**\r
+ * @author Evgeniy Berlog\r
+ * @date 18.06.12\r
+ */\r
+public class TestEscherRecordFactory extends TestCase{\r
+\r
+ private static byte[] toByteArray(List<RecordBase> records) {\r
+ ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+ for (RecordBase rb : records) {\r
+ Record r = (Record) rb;\r
+ try {\r
+ out.write(r.serialize());\r
+ } catch (IOException e) {\r
+ throw new RuntimeException(e);\r
+ }\r
+ }\r
+ return out.toByteArray();\r
+ }\r
+\r
+ public void testDetectContainer() {\r
+ Random rnd = new Random();\r
+ assertEquals(true, DefaultEscherRecordFactory.isContainer((short) 0x0, EscherContainerRecord.DG_CONTAINER));\r
+ assertEquals(true, DefaultEscherRecordFactory.isContainer((short) 0x0, EscherContainerRecord.SOLVER_CONTAINER));\r
+ assertEquals(true, DefaultEscherRecordFactory.isContainer((short) 0x0, EscherContainerRecord.SP_CONTAINER));\r
+ assertEquals(true, DefaultEscherRecordFactory.isContainer((short) 0x0, EscherContainerRecord.DGG_CONTAINER));\r
+ assertEquals(true, DefaultEscherRecordFactory.isContainer((short) 0x0, EscherContainerRecord.BSTORE_CONTAINER));\r
+ assertEquals(true, DefaultEscherRecordFactory.isContainer((short) 0x0, EscherContainerRecord.SPGR_CONTAINER));\r
+\r
+ for (Short i=EscherContainerRecord.DGG_CONTAINER; i<= EscherContainerRecord.SOLVER_CONTAINER; i++){\r
+ assertEquals(true, DefaultEscherRecordFactory.isContainer(Integer.valueOf(rnd.nextInt(Short.MAX_VALUE)).shortValue(), i));\r
+ }\r
+\r
+ assertEquals(false, DefaultEscherRecordFactory.isContainer((short) 0x0, Integer.valueOf(EscherContainerRecord.DGG_CONTAINER-1).shortValue()));\r
+ assertEquals(false, DefaultEscherRecordFactory.isContainer((short) 0x0, Integer.valueOf(EscherContainerRecord.SOLVER_CONTAINER+1).shortValue()));\r
+\r
+ assertEquals(true, DefaultEscherRecordFactory.isContainer((short) 0x000F, Integer.valueOf(EscherContainerRecord.DGG_CONTAINER-1).shortValue()));\r
+ assertEquals(true, DefaultEscherRecordFactory.isContainer((short) 0xFFFF, Integer.valueOf(EscherContainerRecord.DGG_CONTAINER-1).shortValue()));\r
+ assertEquals(false, DefaultEscherRecordFactory.isContainer((short) 0x000C, Integer.valueOf(EscherContainerRecord.DGG_CONTAINER-1).shortValue()));\r
+ assertEquals(false, DefaultEscherRecordFactory.isContainer((short) 0xCCCC, Integer.valueOf(EscherContainerRecord.DGG_CONTAINER-1).shortValue()));\r
+ assertEquals(false, DefaultEscherRecordFactory.isContainer((short) 0x000F, EscherTextboxRecord.RECORD_ID));\r
+ assertEquals(false, DefaultEscherRecordFactory.isContainer((short) 0xCCCC, EscherTextboxRecord.RECORD_ID));\r
+ }\r
+\r
+ public void testDgContainerMustBeRootOfHSSFSheetEscherRecords() throws IOException {\r
+ HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("47251.xls");\r
+ HSSFSheet sh = wb.getSheetAt(0);\r
+ InternalSheet ish = HSSFTestHelper.getSheetForTest(sh);\r
+ List<RecordBase> records = ish.getRecords();\r
+ // records to be aggregated\r
+ List<RecordBase> dgRecords = records.subList(19, 23);\r
+ byte[] dgBytes = toByteArray(dgRecords);\r
+ sh.getDrawingPatriarch();\r
+ EscherAggregate agg = (EscherAggregate) ish.findFirstRecordBySid(EscherAggregate.sid);\r
+ assertEquals(true, agg.getEscherRecords().get(0) instanceof EscherContainerRecord);\r
+ assertEquals(EscherContainerRecord.DG_CONTAINER, agg.getEscherRecords().get(0).getRecordId());\r
+ assertEquals((short) 0x0, agg.getEscherRecords().get(0).getOptions());\r
+ agg = (EscherAggregate) ish.findFirstRecordBySid(EscherAggregate.sid);\r
+ byte[] dgBytesAfterSave = agg.serialize();\r
+ assertEquals("different size of drawing data before and after save", dgBytes.length, dgBytesAfterSave.length);\r
+ assertTrue("drawing data before and after save is different", Arrays.equals(dgBytes, dgBytesAfterSave));\r
+ }\r
+}\r