diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index d9c53a015..8229f2a3b 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,12 +34,13 @@ - 47701 - fixed RecordFormatException when reading list subrecords (LbsDataSubRecord) - memory usage optimization in XSSF - avoid creating parentless xml beans - 47188 - avoid corruption of workbook when adding cell comments - 48106 - improved work with cell comments in XSSF - Add support for creating SummaryInformation and DocumentSummaryInformation properties - on POIDocuments that don't have them, via POIDocument.createInformationProperties() + 48332 - fixed ColumnInfoRecord to tolerate missing reserved field + 47701 - fixed RecordFormatException when reading list subrecords (LbsDataSubRecord) + memory usage optimization in XSSF - avoid creating parentless xml beans + 47188 - avoid corruption of workbook when adding cell comments + 48106 - improved work with cell comments in XSSF + Add support for creating SummaryInformation and DocumentSummaryInformation properties + on POIDocuments that don't have them, via POIDocument.createInformationProperties() 48180 - be more forgiving of short chart records, which skip some unused fields 48274 - fix erronious wrapping of byte colours in HSSFPalette.findSimilarColor 48269 - fix fetching of error codes from XSSF formula cells diff --git a/src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java b/src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java index 30ce31b17..4982ef92b 100644 --- a/src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java +++ b/src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java @@ -23,42 +23,41 @@ import org.apache.poi.util.BitField; import org.apache.poi.util.BitFieldFactory; /** - * Title: COLINFO Record

+ * Title: COLINFO Record (0x007D)

* Description: Defines with width and formatting for a range of columns

* REFERENCE: PG 293 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)

* @author Andrew C. Oliver (acoliver at apache dot org) - * @version 2.0-pre */ public final class ColumnInfoRecord extends StandardRecord { - public static final short sid = 0x7d; - private int field_1_first_col; - private int field_2_last_col; - private int field_3_col_width; - private int field_4_xf_index; - private int field_5_options; + public static final short sid = 0x007D; + + private int _firstCol; + private int _lastCol; + private int _colWidth; + private int _xfIndex; + private int _options; private static final BitField hidden = BitFieldFactory.getInstance(0x01); private static final BitField outlevel = BitFieldFactory.getInstance(0x0700); private static final BitField collapsed = BitFieldFactory.getInstance(0x1000); // Excel seems write values 2, 10, and 260, even though spec says "must be zero" - private int field_6_reserved; + private int field_6_reserved; /** * Creates a column info record with default width and format */ public ColumnInfoRecord() { setColumnWidth(2275); - field_5_options = 2; - field_4_xf_index = 0x0f; + _options = 2; + _xfIndex = 0x0f; field_6_reserved = 2; // seems to be the most common value } - public ColumnInfoRecord(RecordInputStream in) - { - field_1_first_col = in.readUShort(); - field_2_last_col = in.readUShort(); - field_3_col_width = in.readUShort(); - field_4_xf_index = in.readUShort(); - field_5_options = in.readUShort(); + public ColumnInfoRecord(RecordInputStream in) { + _firstCol = in.readUShort(); + _lastCol = in.readUShort(); + _colWidth = in.readUShort(); + _xfIndex = in.readUShort(); + _options = in.readUShort(); switch(in.remaining()) { case 2: // usual case field_6_reserved = in.readUShort(); @@ -66,7 +65,13 @@ public final class ColumnInfoRecord extends StandardRecord { case 1: // often COLINFO gets encoded 1 byte short // shouldn't matter because this field is unused - field_6_reserved = in.readByte(); + field_6_reserved = in.readByte(); + break; + case 0: + // According to bugzilla 48332, + // "SoftArtisans OfficeWriter for Excel" totally skips field 6 + // Excel seems to be OK with this, and assumes zero. + field_6_reserved = 0; break; default: throw new RuntimeException("Unusual record size remaining=(" + in.remaining() + ")"); @@ -77,30 +82,24 @@ public final class ColumnInfoRecord extends StandardRecord { * set the first column this record defines formatting info for * @param fc - the first column index (0-based) */ - - public void setFirstColumn(int fc) - { - field_1_first_col = fc; + public void setFirstColumn(int fc) { + _firstCol = fc; } /** * set the last column this record defines formatting info for * @param lc - the last column index (0-based) */ - - public void setLastColumn(int lc) - { - field_2_last_col = lc; + public void setLastColumn(int lc) { + _lastCol = lc; } /** * set the columns' width in 1/256 of a character width * @param cw - column width */ - - public void setColumnWidth(int cw) - { - field_3_col_width = cw; + public void setColumnWidth(int cw) { + _colWidth = cw; } /** @@ -108,79 +107,55 @@ public final class ColumnInfoRecord extends StandardRecord { * @param xfi - the extended format index * @see org.apache.poi.hssf.record.ExtendedFormatRecord */ - - public void setXFIndex(int xfi) - { - field_4_xf_index = xfi; + public void setXFIndex(int xfi) { + _xfIndex = xfi; } - - // start options bitfield - /** * set whether or not these cells are hidden * @param ishidden - whether the cells are hidden. - * @see #setOptions(int) */ - - public void setHidden(boolean ishidden) - { - field_5_options = hidden.setBoolean(field_5_options, ishidden); + public void setHidden(boolean ishidden) { + _options = hidden.setBoolean(_options, ishidden); } /** * set the outline level for the cells - * @see #setOptions(int) * @param olevel -outline level for the cells */ - - public void setOutlineLevel(int olevel) - { - field_5_options = outlevel.setValue(field_5_options, olevel); + public void setOutlineLevel(int olevel) { + _options = outlevel.setValue(_options, olevel); } /** * set whether the cells are collapsed - * @param iscollapsed - wether the cells are collapsed - * @see #setOptions(int) + * @param isCollapsed - whether the cells are collapsed */ - - public void setCollapsed(boolean iscollapsed) - { - field_5_options = collapsed.setBoolean(field_5_options, - iscollapsed); + public void setCollapsed(boolean isCollapsed) { + _options = collapsed.setBoolean(_options, isCollapsed); } - // end options bitfield - /** * get the first column this record defines formatting info for * @return the first column index (0-based) */ - - public int getFirstColumn() - { - return field_1_first_col; + public int getFirstColumn() { + return _firstCol; } /** * get the last column this record defines formatting info for * @return the last column index (0-based) */ - - public int getLastColumn() - { - return field_2_last_col; + public int getLastColumn() { + return _lastCol; } /** - * get the columns' width in 1/256 of a character width - * @return column width + * @return column width in units of 1/256 of a character width */ - - public int getColumnWidth() - { - return field_3_col_width; + public int getColumnWidth() { + return _colWidth; } /** @@ -188,82 +163,55 @@ public final class ColumnInfoRecord extends StandardRecord { * @return the extended format index * @see org.apache.poi.hssf.record.ExtendedFormatRecord */ - - public int getXFIndex() - { - return field_4_xf_index; + public int getXFIndex() { + return _xfIndex; } - public int getOptions() { - return field_5_options; - } - public void setOptions(int field_5_options) { - this.field_5_options = field_5_options; - } - - // start options bitfield - /** - * get whether or not these cells are hidden * @return whether the cells are hidden. - * @see #setOptions(int) */ - - public boolean getHidden() - { - return hidden.isSet(field_5_options); + public boolean getHidden() { + return hidden.isSet(_options); } /** - * get the outline level for the cells - * @see #setOptions(int) * @return outline level for the cells */ - - public int getOutlineLevel() - { - return outlevel.getValue(field_5_options); + public int getOutlineLevel() { + return outlevel.getValue(_options); } /** - * get whether the cells are collapsed - * @return wether the cells are collapsed - * @see #setOptions(int) + * @return whether the cells are collapsed */ - - public boolean getCollapsed() - { - return collapsed.isSet(field_5_options); + public boolean getCollapsed() { + return collapsed.isSet(_options); } - // end options bitfield - public boolean containsColumn(int columnIndex) { - return field_1_first_col <= columnIndex && columnIndex <= field_2_last_col; + return _firstCol <= columnIndex && columnIndex <= _lastCol; } public boolean isAdjacentBefore(ColumnInfoRecord other) { - return field_2_last_col == other.field_1_first_col - 1; + return _lastCol == other._firstCol - 1; } - + /** * @return true if the format, options and column width match */ public boolean formatMatches(ColumnInfoRecord other) { - if (field_4_xf_index != other.field_4_xf_index) { + if (_xfIndex != other._xfIndex) { return false; } - if (field_5_options != other.field_5_options) { + if (_options != other._options) { return false; } - if (field_3_col_width != other.field_3_col_width) { + if (_colWidth != other._colWidth) { return false; } return true; } - - - public short getSid() - { + + public short getSid() { return sid; } @@ -272,7 +220,7 @@ public final class ColumnInfoRecord extends StandardRecord { out.writeShort(getLastColumn()); out.writeShort(getColumnWidth()); out.writeShort(getXFIndex()); - out.writeShort(field_5_options); + out.writeShort(_options); out.writeShort(field_6_reserved); } @@ -280,16 +228,15 @@ public final class ColumnInfoRecord extends StandardRecord { return 12; } - public String toString() - { - StringBuffer sb = new StringBuffer(); + public String toString() { + StringBuilder sb = new StringBuilder(); sb.append("[COLINFO]\n"); sb.append(" colfirst = ").append(getFirstColumn()).append("\n"); sb.append(" collast = ").append(getLastColumn()).append("\n"); sb.append(" colwidth = ").append(getColumnWidth()).append("\n"); sb.append(" xfindex = ").append(getXFIndex()).append("\n"); - sb.append(" options = ").append(HexDump.shortToHex(field_5_options)).append("\n"); + sb.append(" options = ").append(HexDump.shortToHex(_options)).append("\n"); sb.append(" hidden = ").append(getHidden()).append("\n"); sb.append(" olevel = ").append(getOutlineLevel()).append("\n"); sb.append(" collapsed= ").append(getCollapsed()).append("\n"); @@ -299,11 +246,11 @@ public final class ColumnInfoRecord extends StandardRecord { public Object clone() { ColumnInfoRecord rec = new ColumnInfoRecord(); - rec.field_1_first_col = field_1_first_col; - rec.field_2_last_col = field_2_last_col; - rec.field_3_col_width = field_3_col_width; - rec.field_4_xf_index = field_4_xf_index; - rec.field_5_options = field_5_options; + rec._firstCol = _firstCol; + rec._lastCol = _lastCol; + rec._colWidth = _colWidth; + rec._xfIndex = _xfIndex; + rec._options = _options; rec.field_6_reserved = field_6_reserved; return rec; } diff --git a/src/testcases/org/apache/poi/hssf/record/TestColumnInfoRecord.java b/src/testcases/org/apache/poi/hssf/record/TestColumnInfoRecord.java index 8f11d9060..fd0e41217 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestColumnInfoRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestColumnInfoRecord.java @@ -48,6 +48,30 @@ public final class TestColumnInfoRecord extends TestCase { assertTrue(Arrays.equals(data, cir.serialize())); } + /** + * Some applications skip the last reserved field when writing {@link ColumnInfoRecord}s + * The attached file was apparently created by "SoftArtisans OfficeWriter for Excel". + * Excel reads that file OK and assumes zero for the value of the reserved field. + */ + public void testZeroResevedBytes_bug48332() { + // Taken from bugzilla attachment 24661 (offset 0x1E73) + byte[] inpData = HexRead.readFromString("7D 00 0A 00 00 00 00 00 D5 19 0F 00 02 00"); + byte[] outData = HexRead.readFromString("7D 00 0C 00 00 00 00 00 D5 19 0F 00 02 00 00 00"); + + RecordInputStream in = TestcaseRecordInputStream.create(inpData); + ColumnInfoRecord cir; + try { + cir = new ColumnInfoRecord(in); + } catch (RuntimeException e) { + if (e.getMessage().equals("Unusual record size remaining=(0)")) { + throw new AssertionFailedError("Identified bug 48332"); + } + throw e; + } + assertEquals(0, in.remaining()); + assertTrue(Arrays.equals(outData, cir.serialize())); + } + /** * Some sample files have just one reserved byte (field 6): * OddStyleRecord.xls, NoGutsRecords.xls, WORKBOOK_in_capitals.xls