* The obj record is used to hold various graphic objects and controls.
*/
public final class ObjRecord extends Record implements Cloneable {
- public final static short sid = 0x005D;
-
- private static final int NORMAL_PAD_ALIGNMENT = 2;
- private static int MAX_PAD_ALIGNMENT = 4;
-
- private List<SubRecord> subrecords;
- /** used when POI has no idea what is going on */
- private final byte[] _uninterpretedData;
- /**
- * Excel seems to tolerate padding to quad or double byte length
- */
- private boolean _isPaddedToQuadByteMultiple;
-
- //00000000 15 00 12 00 01 00 01 00 11 60 00 00 00 00 00 0D .........`......
- //00000010 26 01 00 00 00 00 00 00 00 00 &.........
-
-
- public ObjRecord() {
- subrecords = new ArrayList<>(2);
- // TODO - ensure 2 sub-records (ftCmo 15h, and ftEnd 00h) are always created
- _uninterpretedData = null;
- }
-
- public ObjRecord(RecordInputStream in) {
- // TODO - problems with OBJ sub-records stream
- // MS spec says first sub-record is always CommonObjectDataSubRecord,
- // and last is always EndSubRecord. OOO spec does not mention ObjRecord(0x005D).
- // Existing POI test data seems to violate that rule. Some test data
- // seems to contain garbage, and a crash is only averted by stopping at
+ public final static short sid = 0x005D;
+
+ private static final int NORMAL_PAD_ALIGNMENT = 2;
+ private static int MAX_PAD_ALIGNMENT = 4;
+
+ private List<SubRecord> subrecords;
+ /** used when POI has no idea what is going on */
+ private final byte[] _uninterpretedData;
+ /**
+ * Excel seems to tolerate padding to quad or double byte length
+ */
+ private boolean _isPaddedToQuadByteMultiple;
+
+ //00000000 15 00 12 00 01 00 01 00 11 60 00 00 00 00 00 0D .........`......
+ //00000010 26 01 00 00 00 00 00 00 00 00 &.........
+
+
+ public ObjRecord() {
+ subrecords = new ArrayList<>(2);
+ // TODO - ensure 2 sub-records (ftCmo 15h, and ftEnd 00h) are always created
+ _uninterpretedData = null;
+ }
+
+ public ObjRecord(RecordInputStream in) {
+ // TODO - problems with OBJ sub-records stream
+ // MS spec says first sub-record is always CommonObjectDataSubRecord,
+ // and last is always EndSubRecord. OOO spec does not mention ObjRecord(0x005D).
+ // Existing POI test data seems to violate that rule. Some test data
+ // seems to contain garbage, and a crash is only averted by stopping at
// what looks like the 'EndSubRecord'
- // Check if this can be continued, if so then the
- // following wont work properly
- byte[] subRecordData = in.readRemainder();
- if (LittleEndian.getUShort(subRecordData, 0) != CommonObjectDataSubRecord.sid) {
- // seems to occur in just one junit on "OddStyleRecord.xls" (file created by CrystalReports)
- // Excel tolerates the funny ObjRecord, and replaces it with a corrected version
- // The exact logic/reasoning is not yet understood
- _uninterpretedData = subRecordData;
- subrecords = null;
- return;
- }
+ // Check if this can be continued, if so then the
+ // following wont work properly
+ byte[] subRecordData = in.readRemainder();
+ if (LittleEndian.getUShort(subRecordData, 0) != CommonObjectDataSubRecord.sid) {
+ // seems to occur in just one junit on "OddStyleRecord.xls" (file created by CrystalReports)
+ // Excel tolerates the funny ObjRecord, and replaces it with a corrected version
+ // The exact logic/reasoning is not yet understood
+ _uninterpretedData = subRecordData;
+ subrecords = null;
+ return;
+ }
//YK: files produced by OO violate the condition below
/*
if (subRecordData.length % 2 != 0) {
- String msg = "Unexpected length of subRecordData : " + HexDump.toHex(subRecordData);
- throw new RecordFormatException(msg);
- }
+ String msg = "Unexpected length of subRecordData : " + HexDump.toHex(subRecordData);
+ throw new RecordFormatException(msg);
+ }
*/
- subrecords = new ArrayList<>();
- LittleEndianByteArrayInputStream subRecStream = new LittleEndianByteArrayInputStream(subRecordData);
- CommonObjectDataSubRecord cmo = (CommonObjectDataSubRecord)SubRecord.createSubRecord(subRecStream, 0);
+ subrecords = new ArrayList<>();
+ LittleEndianByteArrayInputStream subRecStream = new LittleEndianByteArrayInputStream(subRecordData);
+ CommonObjectDataSubRecord cmo = (CommonObjectDataSubRecord)SubRecord.createSubRecord(subRecStream, 0);
subrecords.add(cmo);
while (true) {
- SubRecord subRecord = SubRecord.createSubRecord(subRecStream, cmo.getObjectType());
- subrecords.add(subRecord);
- if (subRecord.isTerminating()) {
- break;
- }
- }
- final int nRemainingBytes = subRecordData.length-subRecStream.getReadIndex();
- if (nRemainingBytes > 0) {
- // At present (Oct-2008), most unit test samples have (subRecordData.length % 2 == 0)
- _isPaddedToQuadByteMultiple = subRecordData.length % MAX_PAD_ALIGNMENT == 0;
- if (nRemainingBytes >= (_isPaddedToQuadByteMultiple ? MAX_PAD_ALIGNMENT : NORMAL_PAD_ALIGNMENT)) {
- if (!canPaddingBeDiscarded(subRecordData, nRemainingBytes)) {
- String msg = "Leftover " + nRemainingBytes
- + " bytes in subrecord data " + HexDump.toHex(subRecordData);
- throw new RecordFormatException(msg);
- }
- _isPaddedToQuadByteMultiple = false;
- }
+ SubRecord subRecord = SubRecord.createSubRecord(subRecStream, cmo.getObjectType());
+ subrecords.add(subRecord);
+ if (subRecord.isTerminating()) {
+ break;
+ }
+ }
+ final int nRemainingBytes = subRecordData.length-subRecStream.getReadIndex();
+ if (nRemainingBytes > 0) {
+ // At present (Oct-2008), most unit test samples have (subRecordData.length % 2 == 0)
+ _isPaddedToQuadByteMultiple = subRecordData.length % MAX_PAD_ALIGNMENT == 0;
+ if (nRemainingBytes >= (_isPaddedToQuadByteMultiple ? MAX_PAD_ALIGNMENT : NORMAL_PAD_ALIGNMENT)) {
+ if (!canPaddingBeDiscarded(subRecordData, nRemainingBytes)) {
+ String msg = "Leftover " + nRemainingBytes
+ + " bytes in subrecord data " + HexDump.toHex(subRecordData);
+ throw new RecordFormatException(msg);
+ }
+ _isPaddedToQuadByteMultiple = false;
+ }
} else {
- _isPaddedToQuadByteMultiple = false;
- }
- _uninterpretedData = null;
- }
-
- /**
- * Some XLS files have ObjRecords with nearly 8Kb of excessive padding. These were probably
- * written by a version of POI (around 3.1) which incorrectly interpreted the second short of
- * the ftLbs subrecord (0x1FEE) as a length, and read that many bytes as padding (other bugs
- * helped allow this to occur).
- *
- * Excel reads files with this excessive padding OK, truncating the over-sized ObjRecord back
- * to the its proper size. POI does the same.
- */
- private static boolean canPaddingBeDiscarded(byte[] data, int nRemainingBytes) {
+ _isPaddedToQuadByteMultiple = false;
+ }
+ _uninterpretedData = null;
+ }
+
+ /**
+ * Some XLS files have ObjRecords with nearly 8Kb of excessive padding. These were probably
+ * written by a version of POI (around 3.1) which incorrectly interpreted the second short of
+ * the ftLbs subrecord (0x1FEE) as a length, and read that many bytes as padding (other bugs
+ * helped allow this to occur).
+ *
+ * Excel reads files with this excessive padding OK, truncating the over-sized ObjRecord back
+ * to the its proper size. POI does the same.
+ */
+ private static boolean canPaddingBeDiscarded(byte[] data, int nRemainingBytes) {
// make sure none of the padding looks important
- for(int i=data.length-nRemainingBytes; i<data.length; i++) {
- if (data[i] != 0x00) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
-
- sb.append("[OBJ]\n");
- if(subrecords != null) { // there are special cases where this can be, see comments in constructor above
- for (final SubRecord record : subrecords) {
- sb.append("SUBRECORD: ").append(record);
- }
- }
- sb.append("[/OBJ]\n");
- return sb.toString();
- }
-
- @Override
- public int getRecordSize() {
- if (_uninterpretedData != null) {
- return _uninterpretedData.length + 4;
- }
- int size = 0;
- for (SubRecord record : subrecords) {
- size += record.getDataSize()+4;
- }
- if (_isPaddedToQuadByteMultiple) {
- while (size % MAX_PAD_ALIGNMENT != 0) {
- size++;
- }
- } else {
- while (size % NORMAL_PAD_ALIGNMENT != 0) {
- size++;
- }
- }
- return size + 4;
- }
+ for(int i=data.length-nRemainingBytes; i<data.length; i++) {
+ if (data[i] != 0x00) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("[OBJ]\n");
+ if(subrecords != null) { // there are special cases where this can be, see comments in constructor above
+ for (final SubRecord record : subrecords) {
+ sb.append("SUBRECORD: ").append(record);
+ }
+ }
+ sb.append("[/OBJ]\n");
+ return sb.toString();
+ }
+
+ @Override
+ public int getRecordSize() {
+ if (_uninterpretedData != null) {
+ return _uninterpretedData.length + 4;
+ }
+ int size = 0;
+ for (SubRecord record : subrecords) {
+ size += record.getDataSize()+4;
+ }
+ if (_isPaddedToQuadByteMultiple) {
+ while (size % MAX_PAD_ALIGNMENT != 0) {
+ size++;
+ }
+ } else {
+ while (size % NORMAL_PAD_ALIGNMENT != 0) {
+ size++;
+ }
+ }
+ return size + 4;
+ }
@Override
public int serialize(int offset, byte[] data) {
return recSize;
}
- @Override
- public short getSid() {
- return sid;
- }
+ @Override
+ public short getSid() {
+ return sid;
+ }
// FIXME: return Collections.unmodifiableList?
- public List<SubRecord> getSubRecords() {
- return subrecords;
- }
-
- public void clearSubRecords() {
- subrecords.clear();
- }
-
- public void addSubRecord(int index, SubRecord element) {
- subrecords.add(index, element);
- }
-
- public boolean addSubRecord(SubRecord o) {
- return subrecords.add(o);
- }
-
- @Override
- public ObjRecord clone() {
- ObjRecord rec = new ObjRecord();
-
- for (SubRecord record : subrecords) {
- rec.addSubRecord(record.clone());
- }
- return rec;
- }
+ public List<SubRecord> getSubRecords() {
+ return subrecords;
+ }
+
+ public void clearSubRecords() {
+ subrecords.clear();
+ }
+
+ public void addSubRecord(int index, SubRecord element) {
+ subrecords.add(index, element);
+ }
+
+ public boolean addSubRecord(SubRecord o) {
+ return subrecords.add(o);
+ }
+
+ @Override
+ public ObjRecord clone() {
+ ObjRecord rec = new ObjRecord();
+
+ for (SubRecord record : subrecords) {
+ rec.addSubRecord(record.clone());
+ }
+ return rec;
+ }
}