680 lines
21 KiB
Java
680 lines
21 KiB
Java
/* ====================================================================
|
|
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.aggregates;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Arrays;
|
|
|
|
import org.apache.poi.hssf.model.RecordStream;
|
|
import org.apache.poi.hssf.model.InternalSheet;
|
|
import org.apache.poi.hssf.record.*;
|
|
|
|
/**
|
|
* Groups the page settings records for a worksheet.<p/>
|
|
*
|
|
* See OOO excelfileformat.pdf sec 4.4 'Page Settings Block'
|
|
*
|
|
* @author Josh Micich
|
|
*/
|
|
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;
|
|
/**
|
|
* HeaderFooterRecord records belonging to preceding CustomViewSettingsRecordAggregates.
|
|
* The indicator of such records is a non-zero GUID,
|
|
* see {@link org.apache.poi.hssf.record.HeaderFooterRecord#getGuid()}
|
|
*/
|
|
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();
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Some apps can define multiple HeaderFooterRecord records for a sheet.
|
|
* When saving such a file Excel 2007 re-positions them according to the following rules:
|
|
* - take a HeaderFooterRecord and read 16-byte GUID at offset 12. If it is zero,
|
|
* it means the current sheet and the given HeaderFooterRecord belongs to this PageSettingsBlock
|
|
* - If GUID is not zero then search in preceding CustomViewSettingsRecordAggregates.
|
|
* Compare first 16 bytes of UserSViewBegin with the HeaderFooterRecord's GUID. If match,
|
|
* then append the HeaderFooterRecord to this CustomViewSettingsRecordAggregates
|
|
*
|
|
* @param sheetRecords the list of sheet records read so far
|
|
*/
|
|
public void positionRecords(List<RecordBase> sheetRecords) {
|
|
// Take a copy to loop over, so we can update the real one
|
|
// without concurrency issues
|
|
List<HeaderFooterRecord> hfRecordsToIterate = new ArrayList<HeaderFooterRecord>(_sviewHeaderFooters);
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|