aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorYegor Kozlov <yegor@apache.org>2012-07-22 10:50:01 +0000
committerYegor Kozlov <yegor@apache.org>2012-07-22 10:50:01 +0000
commit22bdfbbbd2b4225e2463e8eea768f3c101bd4c45 (patch)
treecc911dfd250491634e913cc1f676b9a85c2572ca /src
parent1de04ad5f80caf921f35a6fed21f4f954937580d (diff)
downloadpoi-22bdfbbbd2b4225e2463e8eea768f3c101bd4c45.tar.gz
poi-22bdfbbbd2b4225e2463e8eea768f3c101bd4c45.zip
Bugzilla 53204: Improve performance when opening certain Excel files (PageSettingsBlock)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1364254 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r--src/documentation/content/xdocs/status.xml1
-rw-r--r--src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java1217
2 files changed, 613 insertions, 605 deletions
diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml
index 4d84732c4d..4e5d4384dc 100644
--- a/src/documentation/content/xdocs/status.xml
+++ b/src/documentation/content/xdocs/status.xml
@@ -34,6 +34,7 @@
<changes>
<release version="3.9-beta1" date="2012-??-??">
+ <action dev="poi-developers" type="add">53204 - Improved performanceof PageSettingsBlock in HSSF </action>
<action dev="poi-developers" type="add">53500 - Getter for repeating rows and columns</action>
<action dev="poi-developers" type="fix">53369 - Fixed tests failing on JDK 1.7</action>
<action dev="poi-developers" type="fix">53360 - Fixed SXSSF to correctly write text before escaped Unicode control character</action>
diff --git a/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java b/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java
index 676a8f9831..30d3fc6295 100644
--- a/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java
+++ b/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java
@@ -18,13 +18,15 @@
package org.apache.poi.hssf.record.aggregates;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
-import java.util.Arrays;
+import java.util.Map;
import org.apache.poi.hssf.model.RecordStream;
import org.apache.poi.hssf.model.InternalSheet;
import org.apache.poi.hssf.record.*;
+import org.apache.poi.util.HexDump;
/**
* Groups the page settings records for a worksheet.<p/>
@@ -35,59 +37,59 @@ import org.apache.poi.hssf.record.*;
*/
public final class PageSettingsBlock extends RecordAggregate {
- /**
- * PLS is potentially a <i>continued</i> record, but is currently uninterpreted by POI
- */
- private static final class PLSAggregate extends RecordAggregate {
- private static final ContinueRecord[] EMPTY_CONTINUE_RECORD_ARRAY = { };
- private final Record _pls;
- /**
- * holds any continue records found after the PLS record.<br/>
- * This would not be required if PLS was properly interpreted.
- * Currently, PLS is an {@link UnknownRecord} and does not automatically
- * include any trailing {@link ContinueRecord}s.
- */
- private ContinueRecord[] _plsContinues;
-
- public PLSAggregate(RecordStream rs) {
- _pls = rs.getNext();
- if (rs.peekNextSid()==ContinueRecord.sid) {
- List<ContinueRecord> temp = new ArrayList<ContinueRecord>();
- while (rs.peekNextSid()==ContinueRecord.sid) {
- temp.add((ContinueRecord)rs.getNext());
- }
- _plsContinues = new ContinueRecord[temp.size()];
- temp.toArray(_plsContinues);
- } else {
- _plsContinues = EMPTY_CONTINUE_RECORD_ARRAY;
- }
- }
-
- @Override
- public void visitContainedRecords(RecordVisitor rv) {
- rv.visitRecord(_pls);
- for (int i = 0; i < _plsContinues.length; i++) {
- rv.visitRecord(_plsContinues[i]);
- }
- }
- }
-
- // Every one of these component records is optional
- // (The whole PageSettingsBlock may not be present)
- private PageBreakRecord _rowBreaksRecord;
- private PageBreakRecord _columnBreaksRecord;
- private HeaderRecord _header;
- private FooterRecord _footer;
- private HCenterRecord _hCenter;
- private VCenterRecord _vCenter;
- private LeftMarginRecord _leftMargin;
- private RightMarginRecord _rightMargin;
- private TopMarginRecord _topMargin;
- private BottomMarginRecord _bottomMargin;
- private final List<PLSAggregate> _plsRecords;
- private PrintSetupRecord _printSetup;
- private Record _bitmap;
- private HeaderFooterRecord _headerFooter;
+ /**
+ * PLS is potentially a <i>continued</i> record, but is currently uninterpreted by POI
+ */
+ private static final class PLSAggregate extends RecordAggregate {
+ private static final ContinueRecord[] EMPTY_CONTINUE_RECORD_ARRAY = { };
+ private final Record _pls;
+ /**
+ * holds any continue records found after the PLS record.<br/>
+ * This would not be required if PLS was properly interpreted.
+ * Currently, PLS is an {@link UnknownRecord} and does not automatically
+ * include any trailing {@link ContinueRecord}s.
+ */
+ private ContinueRecord[] _plsContinues;
+
+ public PLSAggregate(RecordStream rs) {
+ _pls = rs.getNext();
+ if (rs.peekNextSid()==ContinueRecord.sid) {
+ List<ContinueRecord> temp = new ArrayList<ContinueRecord>();
+ while (rs.peekNextSid()==ContinueRecord.sid) {
+ temp.add((ContinueRecord)rs.getNext());
+ }
+ _plsContinues = new ContinueRecord[temp.size()];
+ temp.toArray(_plsContinues);
+ } else {
+ _plsContinues = EMPTY_CONTINUE_RECORD_ARRAY;
+ }
+ }
+
+ @Override
+ public void visitContainedRecords(RecordVisitor rv) {
+ rv.visitRecord(_pls);
+ for (int i = 0; i < _plsContinues.length; i++) {
+ rv.visitRecord(_plsContinues[i]);
+ }
+ }
+ }
+
+ // Every one of these component records is optional
+ // (The whole PageSettingsBlock may not be present)
+ private PageBreakRecord _rowBreaksRecord;
+ private PageBreakRecord _columnBreaksRecord;
+ private HeaderRecord _header;
+ private FooterRecord _footer;
+ private HCenterRecord _hCenter;
+ private VCenterRecord _vCenter;
+ private LeftMarginRecord _leftMargin;
+ private RightMarginRecord _rightMargin;
+ private TopMarginRecord _topMargin;
+ private BottomMarginRecord _bottomMargin;
+ private final List<PLSAggregate> _plsRecords;
+ private PrintSetupRecord _printSetup;
+ private Record _bitmap;
+ private HeaderFooterRecord _headerFooter;
/**
* HeaderFooterRecord records belonging to preceding CustomViewSettingsRecordAggregates.
* The indicator of such records is a non-zero GUID,
@@ -96,546 +98,546 @@ public final class PageSettingsBlock extends RecordAggregate {
private List<HeaderFooterRecord> _sviewHeaderFooters = new ArrayList<HeaderFooterRecord>();
private Record _printSize;
- public PageSettingsBlock(RecordStream rs) {
- _plsRecords = new ArrayList<PLSAggregate>();
- while(true) {
- if (!readARecord(rs)) {
- break;
- }
- }
- }
-
- /**
- * Creates a PageSettingsBlock with default settings
- */
- public PageSettingsBlock() {
- _plsRecords = new ArrayList<PLSAggregate>();
- _rowBreaksRecord = new HorizontalPageBreakRecord();
- _columnBreaksRecord = new VerticalPageBreakRecord();
- _header = new HeaderRecord("");
- _footer = new FooterRecord("");
- _hCenter = createHCenter();
- _vCenter = createVCenter();
- _printSetup = createPrintSetup();
- }
-
- /**
- * @return <code>true</code> if the specified Record sid is one belonging to the
- * 'Page Settings Block'.
- */
- public static boolean isComponentRecord(int sid) {
- switch (sid) {
- case HorizontalPageBreakRecord.sid:
- case VerticalPageBreakRecord.sid:
- case HeaderRecord.sid:
- case FooterRecord.sid:
- case HCenterRecord.sid:
- case VCenterRecord.sid:
- case LeftMarginRecord.sid:
- case RightMarginRecord.sid:
- case TopMarginRecord.sid:
- case BottomMarginRecord.sid:
- case UnknownRecord.PLS_004D:
- case PrintSetupRecord.sid:
- case UnknownRecord.BITMAP_00E9:
- case UnknownRecord.PRINTSIZE_0033:
- case HeaderFooterRecord.sid: // extra header/footer settings supported by Excel 2007
- return true;
- }
- return false;
- }
-
- private boolean readARecord(RecordStream rs) {
- switch (rs.peekNextSid()) {
- case HorizontalPageBreakRecord.sid:
- checkNotPresent(_rowBreaksRecord);
- _rowBreaksRecord = (PageBreakRecord) rs.getNext();
- break;
- case VerticalPageBreakRecord.sid:
- checkNotPresent(_columnBreaksRecord);
- _columnBreaksRecord = (PageBreakRecord) rs.getNext();
- break;
- case HeaderRecord.sid:
- checkNotPresent(_header);
- _header = (HeaderRecord) rs.getNext();
- break;
- case FooterRecord.sid:
- checkNotPresent(_footer);
- _footer = (FooterRecord) rs.getNext();
- break;
- case HCenterRecord.sid:
- checkNotPresent(_hCenter);
- _hCenter = (HCenterRecord) rs.getNext();
- break;
- case VCenterRecord.sid:
- checkNotPresent(_vCenter);
- _vCenter = (VCenterRecord) rs.getNext();
- break;
- case LeftMarginRecord.sid:
- checkNotPresent(_leftMargin);
- _leftMargin = (LeftMarginRecord) rs.getNext();
- break;
- case RightMarginRecord.sid:
- checkNotPresent(_rightMargin);
- _rightMargin = (RightMarginRecord) rs.getNext();
- break;
- case TopMarginRecord.sid:
- checkNotPresent(_topMargin);
- _topMargin = (TopMarginRecord) rs.getNext();
- break;
- case BottomMarginRecord.sid:
- checkNotPresent(_bottomMargin);
- _bottomMargin = (BottomMarginRecord) rs.getNext();
- break;
- case UnknownRecord.PLS_004D:
- _plsRecords.add(new PLSAggregate(rs));
- break;
- case PrintSetupRecord.sid:
- checkNotPresent(_printSetup);
- _printSetup = (PrintSetupRecord)rs.getNext();
- break;
- case UnknownRecord.BITMAP_00E9:
- checkNotPresent(_bitmap);
- _bitmap = rs.getNext();
- break;
- case UnknownRecord.PRINTSIZE_0033:
- checkNotPresent(_printSize);
- _printSize = rs.getNext();
- break;
- case HeaderFooterRecord.sid:
- //there can be multiple HeaderFooterRecord records belonging to different sheet views
- HeaderFooterRecord hf = (HeaderFooterRecord)rs.getNext();
+ public PageSettingsBlock(RecordStream rs) {
+ _plsRecords = new ArrayList<PLSAggregate>();
+ while(true) {
+ if (!readARecord(rs)) {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Creates a PageSettingsBlock with default settings
+ */
+ public PageSettingsBlock() {
+ _plsRecords = new ArrayList<PLSAggregate>();
+ _rowBreaksRecord = new HorizontalPageBreakRecord();
+ _columnBreaksRecord = new VerticalPageBreakRecord();
+ _header = new HeaderRecord("");
+ _footer = new FooterRecord("");
+ _hCenter = createHCenter();
+ _vCenter = createVCenter();
+ _printSetup = createPrintSetup();
+ }
+
+ /**
+ * @return <code>true</code> if the specified Record sid is one belonging to the
+ * 'Page Settings Block'.
+ */
+ public static boolean isComponentRecord(int sid) {
+ switch (sid) {
+ case HorizontalPageBreakRecord.sid:
+ case VerticalPageBreakRecord.sid:
+ case HeaderRecord.sid:
+ case FooterRecord.sid:
+ case HCenterRecord.sid:
+ case VCenterRecord.sid:
+ case LeftMarginRecord.sid:
+ case RightMarginRecord.sid:
+ case TopMarginRecord.sid:
+ case BottomMarginRecord.sid:
+ case UnknownRecord.PLS_004D:
+ case PrintSetupRecord.sid:
+ case UnknownRecord.BITMAP_00E9:
+ case UnknownRecord.PRINTSIZE_0033:
+ case HeaderFooterRecord.sid: // extra header/footer settings supported by Excel 2007
+ return true;
+ }
+ return false;
+ }
+
+ private boolean readARecord(RecordStream rs) {
+ switch (rs.peekNextSid()) {
+ case HorizontalPageBreakRecord.sid:
+ checkNotPresent(_rowBreaksRecord);
+ _rowBreaksRecord = (PageBreakRecord) rs.getNext();
+ break;
+ case VerticalPageBreakRecord.sid:
+ checkNotPresent(_columnBreaksRecord);
+ _columnBreaksRecord = (PageBreakRecord) rs.getNext();
+ break;
+ case HeaderRecord.sid:
+ checkNotPresent(_header);
+ _header = (HeaderRecord) rs.getNext();
+ break;
+ case FooterRecord.sid:
+ checkNotPresent(_footer);
+ _footer = (FooterRecord) rs.getNext();
+ break;
+ case HCenterRecord.sid:
+ checkNotPresent(_hCenter);
+ _hCenter = (HCenterRecord) rs.getNext();
+ break;
+ case VCenterRecord.sid:
+ checkNotPresent(_vCenter);
+ _vCenter = (VCenterRecord) rs.getNext();
+ break;
+ case LeftMarginRecord.sid:
+ checkNotPresent(_leftMargin);
+ _leftMargin = (LeftMarginRecord) rs.getNext();
+ break;
+ case RightMarginRecord.sid:
+ checkNotPresent(_rightMargin);
+ _rightMargin = (RightMarginRecord) rs.getNext();
+ break;
+ case TopMarginRecord.sid:
+ checkNotPresent(_topMargin);
+ _topMargin = (TopMarginRecord) rs.getNext();
+ break;
+ case BottomMarginRecord.sid:
+ checkNotPresent(_bottomMargin);
+ _bottomMargin = (BottomMarginRecord) rs.getNext();
+ break;
+ case UnknownRecord.PLS_004D:
+ _plsRecords.add(new PLSAggregate(rs));
+ break;
+ case PrintSetupRecord.sid:
+ checkNotPresent(_printSetup);
+ _printSetup = (PrintSetupRecord)rs.getNext();
+ break;
+ case UnknownRecord.BITMAP_00E9:
+ checkNotPresent(_bitmap);
+ _bitmap = rs.getNext();
+ break;
+ case UnknownRecord.PRINTSIZE_0033:
+ checkNotPresent(_printSize);
+ _printSize = rs.getNext();
+ break;
+ case HeaderFooterRecord.sid:
+ //there can be multiple HeaderFooterRecord records belonging to different sheet views
+ HeaderFooterRecord hf = (HeaderFooterRecord)rs.getNext();
if(hf.isCurrentSheet()) _headerFooter = hf;
else {
_sviewHeaderFooters.add(hf);
}
break;
- default:
- // all other record types are not part of the PageSettingsBlock
- return false;
- }
- return true;
- }
-
- private void checkNotPresent(Record rec) {
- if (rec != null) {
- throw new RecordFormatException("Duplicate PageSettingsBlock record (sid=0x"
- + Integer.toHexString(rec.getSid()) + ")");
- }
- }
-
- private PageBreakRecord getRowBreaksRecord() {
- if (_rowBreaksRecord == null) {
- _rowBreaksRecord = new HorizontalPageBreakRecord();
- }
- return _rowBreaksRecord;
- }
-
- private PageBreakRecord getColumnBreaksRecord() {
- if (_columnBreaksRecord == null) {
- _columnBreaksRecord = new VerticalPageBreakRecord();
- }
- return _columnBreaksRecord;
- }
-
-
- /**
- * Sets a page break at the indicated column
- *
- */
- public void setColumnBreak(short column, short fromRow, short toRow) {
- getColumnBreaksRecord().addBreak(column, fromRow, toRow);
- }
-
- /**
- * Removes a page break at the indicated column
- *
- */
- public void removeColumnBreak(int column) {
- getColumnBreaksRecord().removeBreak(column);
- }
-
-
-
-
- public void visitContainedRecords(RecordVisitor rv) {
- // Replicates record order from Excel 2007, though this is not critical
-
- visitIfPresent(_rowBreaksRecord, rv);
- visitIfPresent(_columnBreaksRecord, rv);
- // Write out empty header / footer records if these are missing
- if (_header == null) {
- rv.visitRecord(new HeaderRecord(""));
- } else {
- rv.visitRecord(_header);
- }
- if (_footer == null) {
- rv.visitRecord(new FooterRecord(""));
- } else {
- rv.visitRecord(_footer);
- }
- visitIfPresent(_hCenter, rv);
- visitIfPresent(_vCenter, rv);
- visitIfPresent(_leftMargin, rv);
- visitIfPresent(_rightMargin, rv);
- visitIfPresent(_topMargin, rv);
- visitIfPresent(_bottomMargin, rv);
- for (RecordAggregate pls : _plsRecords) {
- pls.visitContainedRecords(rv);
- }
- visitIfPresent(_printSetup, rv);
- visitIfPresent(_bitmap, rv);
- visitIfPresent(_printSize, rv);
- visitIfPresent(_headerFooter, rv);
- }
- private static void visitIfPresent(Record r, RecordVisitor rv) {
- if (r != null) {
- rv.visitRecord(r);
- }
- }
- private static void visitIfPresent(PageBreakRecord r, RecordVisitor rv) {
- if (r != null) {
- if (r.isEmpty()) {
- // its OK to not serialize empty page break records
- return;
- }
- rv.visitRecord(r);
- }
- }
-
- /**
- * creates the HCenter Record and sets it to false (don't horizontally center)
- */
- private static HCenterRecord createHCenter() {
- HCenterRecord retval = new HCenterRecord();
-
- retval.setHCenter(false);
- return retval;
- }
-
- /**
- * creates the VCenter Record and sets it to false (don't horizontally center)
- */
- private static VCenterRecord createVCenter() {
- VCenterRecord retval = new VCenterRecord();
-
- retval.setVCenter(false);
- return retval;
- }
-
- /**
- * creates the PrintSetup Record and sets it to defaults and marks it invalid
- * @see org.apache.poi.hssf.record.PrintSetupRecord
- * @see org.apache.poi.hssf.record.Record
- * @return record containing a PrintSetupRecord
- */
- private static PrintSetupRecord createPrintSetup() {
- PrintSetupRecord retval = new PrintSetupRecord();
-
- retval.setPaperSize(( short ) 1);
- retval.setScale(( short ) 100);
- retval.setPageStart(( short ) 1);
- retval.setFitWidth(( short ) 1);
- retval.setFitHeight(( short ) 1);
- retval.setOptions(( short ) 2);
- retval.setHResolution(( short ) 300);
- retval.setVResolution(( short ) 300);
- retval.setHeaderMargin( 0.5);
- retval.setFooterMargin( 0.5);
- retval.setCopies(( short ) 1);
- return retval;
- }
-
-
- /**
- * Returns the HeaderRecord.
- * @return HeaderRecord for the sheet.
- */
- public HeaderRecord getHeader ()
- {
- return _header;
- }
-
- /**
- * Sets the HeaderRecord.
- * @param newHeader The new HeaderRecord for the sheet.
- */
- public void setHeader (HeaderRecord newHeader)
- {
- _header = newHeader;
- }
-
- /**
- * Returns the FooterRecord.
- * @return FooterRecord for the sheet.
- */
- public FooterRecord getFooter ()
- {
- return _footer;
- }
-
- /**
- * Sets the FooterRecord.
- * @param newFooter The new FooterRecord for the sheet.
- */
- public void setFooter (FooterRecord newFooter)
- {
- _footer = newFooter;
- }
-
- /**
- * Returns the PrintSetupRecord.
- * @return PrintSetupRecord for the sheet.
- */
- public PrintSetupRecord getPrintSetup ()
- {
- return _printSetup;
- }
-
- /**
- * Sets the PrintSetupRecord.
- * @param newPrintSetup The new PrintSetupRecord for the sheet.
- */
- public void setPrintSetup (PrintSetupRecord newPrintSetup)
- {
- _printSetup = newPrintSetup;
- }
-
-
- private Margin getMarginRec(int marginIndex) {
- switch (marginIndex) {
- case InternalSheet.LeftMargin: return _leftMargin;
- case InternalSheet.RightMargin: return _rightMargin;
- case InternalSheet.TopMargin: return _topMargin;
- case InternalSheet.BottomMargin: return _bottomMargin;
- }
- throw new IllegalArgumentException( "Unknown margin constant: " + marginIndex );
- }
-
-
- /**
- * Gets the size of the margin in inches.
- * @param margin which margin to get
- * @return the size of the margin
- */
- public double getMargin(short margin) {
- Margin m = getMarginRec(margin);
- if (m != null) {
- return m.getMargin();
- }
- switch (margin) {
- case InternalSheet.LeftMargin: return .75;
- case InternalSheet.RightMargin: return .75;
- case InternalSheet.TopMargin: return 1.0;
- case InternalSheet.BottomMargin: return 1.0;
- }
- throw new IllegalArgumentException( "Unknown margin constant: " + margin );
- }
-
- /**
- * Sets the size of the margin in inches.
- * @param margin which margin to get
- * @param size the size of the margin
- */
- public void setMargin(short margin, double size) {
- Margin m = getMarginRec(margin);
- if (m == null) {
- switch (margin) {
- case InternalSheet.LeftMargin:
- _leftMargin = new LeftMarginRecord();
- m = _leftMargin;
- break;
- case InternalSheet.RightMargin:
- _rightMargin = new RightMarginRecord();
- m = _rightMargin;
- break;
- case InternalSheet.TopMargin:
- _topMargin = new TopMarginRecord();
- m = _topMargin;
- break;
- case InternalSheet.BottomMargin:
- _bottomMargin = new BottomMarginRecord();
- m = _bottomMargin;
- break;
- default :
- throw new IllegalArgumentException( "Unknown margin constant: " + margin );
- }
- }
- m.setMargin( size );
- }
-
- /**
- * Shifts all the page breaks in the range "count" number of rows/columns
- * @param breaks The page record to be shifted
- * @param start Starting "main" value to shift breaks
- * @param stop Ending "main" value to shift breaks
- * @param count number of units (rows/columns) to shift by
- */
- private static void shiftBreaks(PageBreakRecord breaks, int start, int stop, int count) {
-
- Iterator<PageBreakRecord.Break> iterator = breaks.getBreaksIterator();
- List<PageBreakRecord.Break> shiftedBreak = new ArrayList<PageBreakRecord.Break>();
- while(iterator.hasNext())
- {
- PageBreakRecord.Break breakItem = iterator.next();
- int breakLocation = breakItem.main;
- boolean inStart = (breakLocation >= start);
- boolean inEnd = (breakLocation <= stop);
- if(inStart && inEnd)
- shiftedBreak.add(breakItem);
- }
-
- iterator = shiftedBreak.iterator();
- while (iterator.hasNext()) {
- PageBreakRecord.Break breakItem = iterator.next();
- breaks.removeBreak(breakItem.main);
- breaks.addBreak((short)(breakItem.main+count), breakItem.subFrom, breakItem.subTo);
- }
- }
-
-
- /**
- * Sets a page break at the indicated row
- * @param row
- */
- public void setRowBreak(int row, short fromCol, short toCol) {
- getRowBreaksRecord().addBreak((short)row, fromCol, toCol);
- }
-
- /**
- * Removes a page break at the indicated row
- * @param row
- */
- public void removeRowBreak(int row) {
- if (getRowBreaksRecord().getBreaks().length < 1)
- throw new IllegalArgumentException("Sheet does not define any row breaks");
- getRowBreaksRecord().removeBreak((short)row);
- }
-
- /**
- * Queries if the specified row has a page break
- * @param row
- * @return true if the specified row has a page break
- */
- public boolean isRowBroken(int row) {
- return getRowBreaksRecord().getBreak(row) != null;
- }
-
-
- /**
- * Queries if the specified column has a page break
- *
- * @return <code>true</code> if the specified column has a page break
- */
- public boolean isColumnBroken(int column) {
- return getColumnBreaksRecord().getBreak(column) != null;
- }
-
- /**
- * Shifts the horizontal page breaks for the indicated count
- * @param startingRow
- * @param endingRow
- * @param count
- */
- public void shiftRowBreaks(int startingRow, int endingRow, int count) {
- shiftBreaks(getRowBreaksRecord(), startingRow, endingRow, count);
- }
-
- /**
- * Shifts the vertical page breaks for the indicated count
- * @param startingCol
- * @param endingCol
- * @param count
- */
- public void shiftColumnBreaks(short startingCol, short endingCol, short count) {
- shiftBreaks(getColumnBreaksRecord(), startingCol, endingCol, count);
- }
-
- /**
- * @return all the horizontal page breaks, never <code>null</code>
- */
- public int[] getRowBreaks() {
- return getRowBreaksRecord().getBreaks();
- }
-
- /**
- * @return the number of row page breaks
- */
- public int getNumRowBreaks(){
- return getRowBreaksRecord().getNumBreaks();
- }
-
- /**
- * @return all the column page breaks, never <code>null</code>
- */
- public int[] getColumnBreaks(){
- return getColumnBreaksRecord().getBreaks();
- }
-
- /**
- * @return the number of column page breaks
- */
- public int getNumColumnBreaks(){
- return getColumnBreaksRecord().getNumBreaks();
- }
-
- public VCenterRecord getVCenter() {
- return _vCenter;
- }
-
- public HCenterRecord getHCenter() {
- return _hCenter;
- }
-
- /**
- * HEADERFOOTER is new in 2007. Some apps seem to have scattered this record long after
- * the {@link PageSettingsBlock} where it belongs.
- */
- public void addLateHeaderFooter(HeaderFooterRecord rec) {
- if (_headerFooter != null) {
- throw new IllegalStateException("This page settings block already has a header/footer record");
- }
- if (rec.getSid() != HeaderFooterRecord.sid) {
- throw new RecordFormatException("Unexpected header-footer record sid: 0x" + Integer.toHexString(rec.getSid()));
- }
- _headerFooter = rec;
- }
-
- /**
- * This method reads PageSettingsBlock records from the supplied RecordStream until the first
- * non-PageSettingsBlock record is encountered. As each record is read, it is incorporated
- * into this PageSettingsBlock.
- * <p/>
- * The latest Excel version seems to write the PageSettingsBlock uninterrupted. However there
- * are several examples (that Excel reads OK) where these records are not written together:
- * <ul>
- * <li><b>HEADER_FOOTER(0x089C) after WINDOW2</b> - This record is new in 2007. Some apps
- * seem to have scattered this record long after the PageSettingsBlock where it belongs
- * test samples: SharedFormulaTest.xls, ex44921-21902.xls, ex42570-20305.xls</li>
- * <li><b>PLS, WSBOOL, PageSettingsBlock</b> - WSBOOL is not a PSB record.
- * This happens in the test sample file "NoGutsRecords.xls" and "WORKBOOK_in_capitals.xls"</li>
- * <li><b>Margins after DIMENSION</b> - All of PSB should be before DIMENSION. (Bug-47199)</li>
- * </ul>
- * These were probably written by other applications (or earlier versions of Excel). It was
- * decided to not write specific code for detecting each of these cases. POI now tolerates
- * PageSettingsBlock records scattered all over the sheet record stream, and in any order, but
- * does not allow duplicates of any of those records.
- *
- * <p/>
- * <b>Note</b> - when POI writes out this PageSettingsBlock, the records will always be written
- * in one consolidated block (in the standard ordering) regardless of how scattered the records
- * were when they were originally read.
- *
- * @throws RecordFormatException if any PSB record encountered has the same type (sid) as
- * a record that is already part of this PageSettingsBlock
- */
- public void addLateRecords(RecordStream rs) {
- while(true) {
- if (!readARecord(rs)) {
- break;
- }
- }
- }
+ default:
+ // all other record types are not part of the PageSettingsBlock
+ return false;
+ }
+ return true;
+ }
+
+ private void checkNotPresent(Record rec) {
+ if (rec != null) {
+ throw new RecordFormatException("Duplicate PageSettingsBlock record (sid=0x"
+ + Integer.toHexString(rec.getSid()) + ")");
+ }
+ }
+
+ private PageBreakRecord getRowBreaksRecord() {
+ if (_rowBreaksRecord == null) {
+ _rowBreaksRecord = new HorizontalPageBreakRecord();
+ }
+ return _rowBreaksRecord;
+ }
+
+ private PageBreakRecord getColumnBreaksRecord() {
+ if (_columnBreaksRecord == null) {
+ _columnBreaksRecord = new VerticalPageBreakRecord();
+ }
+ return _columnBreaksRecord;
+ }
+
+
+ /**
+ * Sets a page break at the indicated column
+ *
+ */
+ public void setColumnBreak(short column, short fromRow, short toRow) {
+ getColumnBreaksRecord().addBreak(column, fromRow, toRow);
+ }
+
+ /**
+ * Removes a page break at the indicated column
+ *
+ */
+ public void removeColumnBreak(int column) {
+ getColumnBreaksRecord().removeBreak(column);
+ }
+
+
+
+
+ public void visitContainedRecords(RecordVisitor rv) {
+ // Replicates record order from Excel 2007, though this is not critical
+
+ visitIfPresent(_rowBreaksRecord, rv);
+ visitIfPresent(_columnBreaksRecord, rv);
+ // Write out empty header / footer records if these are missing
+ if (_header == null) {
+ rv.visitRecord(new HeaderRecord(""));
+ } else {
+ rv.visitRecord(_header);
+ }
+ if (_footer == null) {
+ rv.visitRecord(new FooterRecord(""));
+ } else {
+ rv.visitRecord(_footer);
+ }
+ visitIfPresent(_hCenter, rv);
+ visitIfPresent(_vCenter, rv);
+ visitIfPresent(_leftMargin, rv);
+ visitIfPresent(_rightMargin, rv);
+ visitIfPresent(_topMargin, rv);
+ visitIfPresent(_bottomMargin, rv);
+ for (RecordAggregate pls : _plsRecords) {
+ pls.visitContainedRecords(rv);
+ }
+ visitIfPresent(_printSetup, rv);
+ visitIfPresent(_bitmap, rv);
+ visitIfPresent(_printSize, rv);
+ visitIfPresent(_headerFooter, rv);
+ }
+ private static void visitIfPresent(Record r, RecordVisitor rv) {
+ if (r != null) {
+ rv.visitRecord(r);
+ }
+ }
+ private static void visitIfPresent(PageBreakRecord r, RecordVisitor rv) {
+ if (r != null) {
+ if (r.isEmpty()) {
+ // its OK to not serialize empty page break records
+ return;
+ }
+ rv.visitRecord(r);
+ }
+ }
+
+ /**
+ * creates the HCenter Record and sets it to false (don't horizontally center)
+ */
+ private static HCenterRecord createHCenter() {
+ HCenterRecord retval = new HCenterRecord();
+
+ retval.setHCenter(false);
+ return retval;
+ }
+
+ /**
+ * creates the VCenter Record and sets it to false (don't horizontally center)
+ */
+ private static VCenterRecord createVCenter() {
+ VCenterRecord retval = new VCenterRecord();
+
+ retval.setVCenter(false);
+ return retval;
+ }
+
+ /**
+ * creates the PrintSetup Record and sets it to defaults and marks it invalid
+ * @see org.apache.poi.hssf.record.PrintSetupRecord
+ * @see org.apache.poi.hssf.record.Record
+ * @return record containing a PrintSetupRecord
+ */
+ private static PrintSetupRecord createPrintSetup() {
+ PrintSetupRecord retval = new PrintSetupRecord();
+
+ retval.setPaperSize(( short ) 1);
+ retval.setScale(( short ) 100);
+ retval.setPageStart(( short ) 1);
+ retval.setFitWidth(( short ) 1);
+ retval.setFitHeight(( short ) 1);
+ retval.setOptions(( short ) 2);
+ retval.setHResolution(( short ) 300);
+ retval.setVResolution(( short ) 300);
+ retval.setHeaderMargin( 0.5);
+ retval.setFooterMargin( 0.5);
+ retval.setCopies(( short ) 1);
+ return retval;
+ }
+
+
+ /**
+ * Returns the HeaderRecord.
+ * @return HeaderRecord for the sheet.
+ */
+ public HeaderRecord getHeader ()
+ {
+ return _header;
+ }
+
+ /**
+ * Sets the HeaderRecord.
+ * @param newHeader The new HeaderRecord for the sheet.
+ */
+ public void setHeader (HeaderRecord newHeader)
+ {
+ _header = newHeader;
+ }
+
+ /**
+ * Returns the FooterRecord.
+ * @return FooterRecord for the sheet.
+ */
+ public FooterRecord getFooter ()
+ {
+ return _footer;
+ }
+
+ /**
+ * Sets the FooterRecord.
+ * @param newFooter The new FooterRecord for the sheet.
+ */
+ public void setFooter (FooterRecord newFooter)
+ {
+ _footer = newFooter;
+ }
+
+ /**
+ * Returns the PrintSetupRecord.
+ * @return PrintSetupRecord for the sheet.
+ */
+ public PrintSetupRecord getPrintSetup ()
+ {
+ return _printSetup;
+ }
+
+ /**
+ * Sets the PrintSetupRecord.
+ * @param newPrintSetup The new PrintSetupRecord for the sheet.
+ */
+ public void setPrintSetup (PrintSetupRecord newPrintSetup)
+ {
+ _printSetup = newPrintSetup;
+ }
+
+
+ private Margin getMarginRec(int marginIndex) {
+ switch (marginIndex) {
+ case InternalSheet.LeftMargin: return _leftMargin;
+ case InternalSheet.RightMargin: return _rightMargin;
+ case InternalSheet.TopMargin: return _topMargin;
+ case InternalSheet.BottomMargin: return _bottomMargin;
+ }
+ throw new IllegalArgumentException( "Unknown margin constant: " + marginIndex );
+ }
+
+
+ /**
+ * Gets the size of the margin in inches.
+ * @param margin which margin to get
+ * @return the size of the margin
+ */
+ public double getMargin(short margin) {
+ Margin m = getMarginRec(margin);
+ if (m != null) {
+ return m.getMargin();
+ }
+ switch (margin) {
+ case InternalSheet.LeftMargin: return .75;
+ case InternalSheet.RightMargin: return .75;
+ case InternalSheet.TopMargin: return 1.0;
+ case InternalSheet.BottomMargin: return 1.0;
+ }
+ throw new IllegalArgumentException( "Unknown margin constant: " + margin );
+ }
+
+ /**
+ * Sets the size of the margin in inches.
+ * @param margin which margin to get
+ * @param size the size of the margin
+ */
+ public void setMargin(short margin, double size) {
+ Margin m = getMarginRec(margin);
+ if (m == null) {
+ switch (margin) {
+ case InternalSheet.LeftMargin:
+ _leftMargin = new LeftMarginRecord();
+ m = _leftMargin;
+ break;
+ case InternalSheet.RightMargin:
+ _rightMargin = new RightMarginRecord();
+ m = _rightMargin;
+ break;
+ case InternalSheet.TopMargin:
+ _topMargin = new TopMarginRecord();
+ m = _topMargin;
+ break;
+ case InternalSheet.BottomMargin:
+ _bottomMargin = new BottomMarginRecord();
+ m = _bottomMargin;
+ break;
+ default :
+ throw new IllegalArgumentException( "Unknown margin constant: " + margin );
+ }
+ }
+ m.setMargin( size );
+ }
+
+ /**
+ * Shifts all the page breaks in the range "count" number of rows/columns
+ * @param breaks The page record to be shifted
+ * @param start Starting "main" value to shift breaks
+ * @param stop Ending "main" value to shift breaks
+ * @param count number of units (rows/columns) to shift by
+ */
+ private static void shiftBreaks(PageBreakRecord breaks, int start, int stop, int count) {
+
+ Iterator<PageBreakRecord.Break> iterator = breaks.getBreaksIterator();
+ List<PageBreakRecord.Break> shiftedBreak = new ArrayList<PageBreakRecord.Break>();
+ while(iterator.hasNext())
+ {
+ PageBreakRecord.Break breakItem = iterator.next();
+ int breakLocation = breakItem.main;
+ boolean inStart = (breakLocation >= start);
+ boolean inEnd = (breakLocation <= stop);
+ if(inStart && inEnd)
+ shiftedBreak.add(breakItem);
+ }
+
+ iterator = shiftedBreak.iterator();
+ while (iterator.hasNext()) {
+ PageBreakRecord.Break breakItem = iterator.next();
+ breaks.removeBreak(breakItem.main);
+ breaks.addBreak((short)(breakItem.main+count), breakItem.subFrom, breakItem.subTo);
+ }
+ }
+
+
+ /**
+ * Sets a page break at the indicated row
+ * @param row
+ */
+ public void setRowBreak(int row, short fromCol, short toCol) {
+ getRowBreaksRecord().addBreak((short)row, fromCol, toCol);
+ }
+
+ /**
+ * Removes a page break at the indicated row
+ * @param row
+ */
+ public void removeRowBreak(int row) {
+ if (getRowBreaksRecord().getBreaks().length < 1)
+ throw new IllegalArgumentException("Sheet does not define any row breaks");
+ getRowBreaksRecord().removeBreak((short)row);
+ }
+
+ /**
+ * Queries if the specified row has a page break
+ * @param row
+ * @return true if the specified row has a page break
+ */
+ public boolean isRowBroken(int row) {
+ return getRowBreaksRecord().getBreak(row) != null;
+ }
+
+
+ /**
+ * Queries if the specified column has a page break
+ *
+ * @return <code>true</code> if the specified column has a page break
+ */
+ public boolean isColumnBroken(int column) {
+ return getColumnBreaksRecord().getBreak(column) != null;
+ }
+
+ /**
+ * Shifts the horizontal page breaks for the indicated count
+ * @param startingRow
+ * @param endingRow
+ * @param count
+ */
+ public void shiftRowBreaks(int startingRow, int endingRow, int count) {
+ shiftBreaks(getRowBreaksRecord(), startingRow, endingRow, count);
+ }
+
+ /**
+ * Shifts the vertical page breaks for the indicated count
+ * @param startingCol
+ * @param endingCol
+ * @param count
+ */
+ public void shiftColumnBreaks(short startingCol, short endingCol, short count) {
+ shiftBreaks(getColumnBreaksRecord(), startingCol, endingCol, count);
+ }
+
+ /**
+ * @return all the horizontal page breaks, never <code>null</code>
+ */
+ public int[] getRowBreaks() {
+ return getRowBreaksRecord().getBreaks();
+ }
+
+ /**
+ * @return the number of row page breaks
+ */
+ public int getNumRowBreaks(){
+ return getRowBreaksRecord().getNumBreaks();
+ }
+
+ /**
+ * @return all the column page breaks, never <code>null</code>
+ */
+ public int[] getColumnBreaks(){
+ return getColumnBreaksRecord().getBreaks();
+ }
+
+ /**
+ * @return the number of column page breaks
+ */
+ public int getNumColumnBreaks(){
+ return getColumnBreaksRecord().getNumBreaks();
+ }
+
+ public VCenterRecord getVCenter() {
+ return _vCenter;
+ }
+
+ public HCenterRecord getHCenter() {
+ return _hCenter;
+ }
+
+ /**
+ * HEADERFOOTER is new in 2007. Some apps seem to have scattered this record long after
+ * the {@link PageSettingsBlock} where it belongs.
+ */
+ public void addLateHeaderFooter(HeaderFooterRecord rec) {
+ if (_headerFooter != null) {
+ throw new IllegalStateException("This page settings block already has a header/footer record");
+ }
+ if (rec.getSid() != HeaderFooterRecord.sid) {
+ throw new RecordFormatException("Unexpected header-footer record sid: 0x" + Integer.toHexString(rec.getSid()));
+ }
+ _headerFooter = rec;
+ }
+
+ /**
+ * This method reads PageSettingsBlock records from the supplied RecordStream until the first
+ * non-PageSettingsBlock record is encountered. As each record is read, it is incorporated
+ * into this PageSettingsBlock.
+ * <p/>
+ * The latest Excel version seems to write the PageSettingsBlock uninterrupted. However there
+ * are several examples (that Excel reads OK) where these records are not written together:
+ * <ul>
+ * <li><b>HEADER_FOOTER(0x089C) after WINDOW2</b> - This record is new in 2007. Some apps
+ * seem to have scattered this record long after the PageSettingsBlock where it belongs
+ * test samples: SharedFormulaTest.xls, ex44921-21902.xls, ex42570-20305.xls</li>
+ * <li><b>PLS, WSBOOL, PageSettingsBlock</b> - WSBOOL is not a PSB record.
+ * This happens in the test sample file "NoGutsRecords.xls" and "WORKBOOK_in_capitals.xls"</li>
+ * <li><b>Margins after DIMENSION</b> - All of PSB should be before DIMENSION. (Bug-47199)</li>
+ * </ul>
+ * These were probably written by other applications (or earlier versions of Excel). It was
+ * decided to not write specific code for detecting each of these cases. POI now tolerates
+ * PageSettingsBlock records scattered all over the sheet record stream, and in any order, but
+ * does not allow duplicates of any of those records.
+ *
+ * <p/>
+ * <b>Note</b> - when POI writes out this PageSettingsBlock, the records will always be written
+ * in one consolidated block (in the standard ordering) regardless of how scattered the records
+ * were when they were originally read.
+ *
+ * @throws RecordFormatException if any PSB record encountered has the same type (sid) as
+ * a record that is already part of this PageSettingsBlock
+ */
+ public void addLateRecords(RecordStream rs) {
+ while(true) {
+ if (!readARecord(rs)) {
+ break;
+ }
+ }
+ }
/**
* Some apps can define multiple HeaderFooterRecord records for a sheet.
@@ -652,26 +654,31 @@ public final class PageSettingsBlock extends RecordAggregate {
// Take a copy to loop over, so we can update the real one
// without concurrency issues
List<HeaderFooterRecord> hfRecordsToIterate = new ArrayList<HeaderFooterRecord>(_sviewHeaderFooters);
-
+
+ final Map<String, HeaderFooterRecord> hfGuidMap = new HashMap<String, HeaderFooterRecord>();
+
+ for(final HeaderFooterRecord hf : hfRecordsToIterate) {
+ hfGuidMap.put(HexDump.toHex(hf.getGuid()), hf);
+ }
+
// loop through HeaderFooterRecord records having not-empty GUID and match them with
// CustomViewSettingsRecordAggregate blocks having UserSViewBegin with the same GUID
- for(final HeaderFooterRecord hf : hfRecordsToIterate) {
- for (RecordBase rb : sheetRecords) {
- if (rb instanceof CustomViewSettingsRecordAggregate) {
- final CustomViewSettingsRecordAggregate cv = (CustomViewSettingsRecordAggregate) rb;
- cv.visitContainedRecords(new RecordVisitor() {
- public void visitRecord(Record r) {
- if (r.getSid() == UserSViewBegin.sid) {
- byte[] guid1 = ((UserSViewBegin) r).getGuid();
- byte[] guid2 = hf.getGuid();
- if (Arrays.equals(guid1, guid2)) {
- cv.append(hf);
- _sviewHeaderFooters.remove(hf);
- }
+ for (RecordBase rb : sheetRecords) {
+ if (rb instanceof CustomViewSettingsRecordAggregate) {
+ final CustomViewSettingsRecordAggregate cv = (CustomViewSettingsRecordAggregate) rb;
+ cv.visitContainedRecords(new RecordVisitor() {
+ public void visitRecord(Record r) {
+ if (r.getSid() == UserSViewBegin.sid) {
+ String guid = HexDump.toHex(((UserSViewBegin) r).getGuid());
+ HeaderFooterRecord hf = hfGuidMap.get(guid);
+
+ if (hf != null) {
+ cv.append(hf);
+ _sviewHeaderFooters.remove(hf);
}
}
- });
- }
+ }
+ });
}
}
}