Able to set the `topLeftCell` in freeze panes

Fixes #260
Closes #261
This commit is contained in:
Adrien Cohen 2017-12-17 01:20:50 +09:00 committed by Adrien Crivelli
parent eb58563b4b
commit 11b055b29f
No known key found for this signature in database
GPG Key ID: B182FD79DC6DE92E
9 changed files with 161 additions and 53 deletions

View File

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added
- Support to write merged cells in ODS format [#287](https://github.com/PHPOffice/PhpSpreadsheet/issues/287)
- Able to set the `topLeftCell` in freeze panes [#261](https://github.com/PHPOffice/PhpSpreadsheet/pull/261)
### Changed

View File

@ -4488,9 +4488,17 @@ class Xls extends BaseReader
// offset: 2; size: 2; position of horizontal split
$py = self::getUInt2d($recordData, 2);
// offset: 4; size: 2; top most visible row in the bottom pane
$rwTop = self::getUInt2d($recordData, 4);
// offset: 6; size: 2; first visible left column in the right pane
$colLeft = self::getUInt2d($recordData, 6);
if ($this->frozen) {
// frozen panes
$this->phpSheet->freezePane(Coordinate::stringFromColumnIndex($px + 1) . ($py + 1));
$cell = Coordinate::stringFromColumnIndex($px + 1) . ($py + 1);
$topLeftCell = Coordinate::stringFromColumnIndex($colLeft + 1) . ($rwTop + 1);
$this->phpSheet->freezePane($cell, $topLeftCell);
}
// unfrozen panes; split windows; not supported by PhpSpreadsheet core
}

View File

@ -720,22 +720,23 @@ class Xlsx extends BaseReader
$docSheet->setRightToLeft(self::boolean((string) $xmlSheet->sheetViews->sheetView['rightToLeft']));
}
if (isset($xmlSheet->sheetViews->sheetView->pane)) {
if (isset($xmlSheet->sheetViews->sheetView->pane['topLeftCell'])) {
$docSheet->freezePane((string) $xmlSheet->sheetViews->sheetView->pane['topLeftCell']);
} else {
$xSplit = 0;
$ySplit = 0;
$topLeftCell = null;
if (isset($xmlSheet->sheetViews->sheetView->pane['xSplit'])) {
$xSplit = 1 + (int) ($xmlSheet->sheetViews->sheetView->pane['xSplit']);
$xSplit = (int) ($xmlSheet->sheetViews->sheetView->pane['xSplit']);
}
if (isset($xmlSheet->sheetViews->sheetView->pane['ySplit'])) {
$ySplit = 1 + (int) ($xmlSheet->sheetViews->sheetView->pane['ySplit']);
$ySplit = (int) ($xmlSheet->sheetViews->sheetView->pane['ySplit']);
}
$docSheet->freezePaneByColumnAndRow($xSplit + 1, $ySplit);
if (isset($xmlSheet->sheetViews->sheetView->pane['topLeftCell'])) {
$topLeftCell = (string) $xmlSheet->sheetViews->sheetView->pane['topLeftCell'];
}
$docSheet->freezePane(Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1), $topLeftCell);
}
if (isset($xmlSheet->sheetViews->sheetView->selection)) {

View File

@ -576,8 +576,14 @@ class ReferenceHelper
}
// Update worksheet: freeze pane
if ($pSheet->getFreezePane() != '') {
$pSheet->freezePane($this->updateCellReference($pSheet->getFreezePane(), $pBefore, $pNumCols, $pNumRows));
if ($pSheet->getFreezePane()) {
$splitCell = $pSheet->getFreezePane();
$topLeftCell = $pSheet->getTopLeftCell();
$splitCell = $this->updateCellReference($splitCell, $pBefore, $pNumCols, $pNumRows);
$topLeftCell = $this->updateCellReference($topLeftCell, $pBefore, $pNumCols, $pNumRows);
$pSheet->freezePane($splitCell, $topLeftCell);
}
// Page setup

View File

@ -201,9 +201,16 @@ class Worksheet implements IComparable
/**
* Freeze pane.
*
* @var string
* @var null|string
*/
private $freezePane = '';
private $freezePane;
/**
* Default position of the right bottom pane.
*
* @var null|string
*/
private $topLeftCell;
/**
* Show gridlines?
@ -1975,27 +1982,33 @@ class Worksheet implements IComparable
/**
* Freeze Pane.
*
* @param string $pCell Cell (i.e. A2)
* Examples:
* A2 will freeze the rows above cell A2 (i.e row 1)
* B1 will freeze the columns to the left of cell B1 (i.e column A)
* B2 will freeze the rows above and to the left of cell A2
* (i.e row 1 and column A)
*
* - A2 will freeze the rows above cell A2 (i.e row 1)
* - B1 will freeze the columns to the left of cell B1 (i.e column A)
* - B2 will freeze the rows above and to the left of cell A2 (i.e row 1 and column A)
*
* @param null|string $cell Position of the split
* @param null|string $topLeftCell default position of the right bottom pane
*
* @throws Exception
*
* @return Worksheet
*/
public function freezePane($pCell)
public function freezePane($cell, $topLeftCell = null)
{
// Uppercase coordinate
$pCell = strtoupper($pCell);
if (strpos($pCell, ':') === false && strpos($pCell, ',') === false) {
$this->freezePane = $pCell;
} else {
if (is_string($cell) && (strpos($cell, ':') !== false || strpos($cell, ',') !== false)) {
throw new Exception('Freeze pane can not be set on a range of cells.');
}
if ($cell !== null && $topLeftCell === null) {
$coordinate = Coordinate::coordinateFromString($cell);
$topLeftCell = $coordinate[0] . ($coordinate[1] + 1);
}
$this->freezePane = $cell;
$this->topLeftCell = $topLeftCell;
return $this;
}
@ -2005,8 +2018,6 @@ class Worksheet implements IComparable
* @param int $columnIndex Numeric column coordinate of the cell
* @param int $row Numeric row coordinate of the cell
*
* @throws Exception
*
* @return Worksheet
*/
public function freezePaneByColumnAndRow($columnIndex, $row)
@ -2021,7 +2032,17 @@ class Worksheet implements IComparable
*/
public function unfreezePane()
{
return $this->freezePane('');
return $this->freezePane(null);
}
/**
* Get the default position of the right bottom pane.
*
* @return int
*/
public function getTopLeftCell()
{
return $this->topLeftCell;
}
/**
@ -2622,6 +2643,7 @@ class Worksheet implements IComparable
// Identify the range that we need to extract from the worksheet
$maxCol = $this->getHighestColumn();
$maxRow = $this->getHighestRow();
// Return
return $this->rangeToArray('A1:' . $maxCol . $maxRow, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
}

View File

@ -1589,10 +1589,15 @@ class Worksheet extends BIFFwriter
private function writePanes()
{
$panes = [];
if ($freezePane = $this->phpSheet->getFreezePane()) {
list($column, $row) = Coordinate::coordinateFromString($freezePane);
$panes[0] = $row - 1;
$panes[1] = Coordinate::columnIndexFromString($column) - 1;
if ($this->phpSheet->getFreezePane()) {
list($column, $row) = Coordinate::coordinateFromString($this->phpSheet->getFreezePane());
$panes[0] = Coordinate::columnIndexFromString($column) - 1;
$panes[1] = $row - 1;
list($leftMostColumn, $topRow) = Coordinate::coordinateFromString($this->phpSheet->getTopLeftCell());
//Coordinates are zero-based in xls files
$panes[2] = $topRow - 1;
$panes[3] = Coordinate::columnIndexFromString($leftMostColumn) - 1;
} else {
// thaw panes
return;

View File

@ -244,31 +244,31 @@ class Worksheet extends WriterPart
// Pane
$pane = '';
$topLeftCell = $pSheet->getFreezePane();
if (($topLeftCell != '') && ($topLeftCell != 'A1')) {
$activeCell = $topLeftCell;
// Calculate freeze coordinates
$xSplit = $ySplit = 0;
list($xSplit, $ySplit) = Coordinate::coordinateFromString($topLeftCell);
if ($pSheet->getFreezePane()) {
list($xSplit, $ySplit) = Coordinate::coordinateFromString($pSheet->getFreezePane());
$xSplit = Coordinate::columnIndexFromString($xSplit);
--$xSplit;
--$ySplit;
$topLeftCell = $pSheet->getTopLeftCell();
$activeCell = $topLeftCell;
// pane
$pane = 'topRight';
$objWriter->startElement('pane');
if ($xSplit > 1) {
$objWriter->writeAttribute('xSplit', $xSplit - 1);
if ($xSplit > 0) {
$objWriter->writeAttribute('xSplit', $xSplit);
}
if ($ySplit > 1) {
$objWriter->writeAttribute('ySplit', $ySplit - 1);
$pane = ($xSplit > 1) ? 'bottomRight' : 'bottomLeft';
if ($ySplit > 0) {
$objWriter->writeAttribute('ySplit', $ySplit);
$pane = ($xSplit > 0) ? 'bottomRight' : 'bottomLeft';
}
$objWriter->writeAttribute('topLeftCell', $topLeftCell);
$objWriter->writeAttribute('activePane', $pane);
$objWriter->writeAttribute('state', 'frozen');
$objWriter->endElement();
if (($xSplit > 1) && ($ySplit > 1)) {
if (($xSplit > 0) && ($ySplit > 0)) {
// Write additional selections if more than two panes (ie both an X and a Y split)
$objWriter->startElement('selection');
$objWriter->writeAttribute('pane', 'topRight');

View File

@ -0,0 +1,37 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader;
use PhpOffice\PhpSpreadsheet\Reader\Xls as ReaderXls;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xls as WriterXls;
use PHPUnit_Framework_TestCase;
class XlsTest extends PHPUnit_Framework_TestCase
{
public function testFreezePane()
{
$filename = tempnam(File::sysGetTempDir(), 'phpspreadsheet');
$cellSplit = 'B2';
$topLeftCell = 'E5';
$spreadsheet = new Spreadsheet();
$active = $spreadsheet->getActiveSheet();
$active->freezePane($cellSplit, $topLeftCell);
$writer = new WriterXls($spreadsheet);
$writer->save($filename);
// Read written file
$reader = new ReaderXls();
$reloadedSpreadsheet = $reader->load($filename);
$reloadedActive = $reloadedSpreadsheet->getActiveSheet();
$actualCellSplit = $reloadedActive->getFreezePane();
$actualTopLeftCell = $reloadedActive->getTopLeftCell();
self::assertSame($cellSplit, $actualCellSplit, 'should be able to set freeze pane');
self::assertSame($topLeftCell, $actualTopLeftCell, 'should be able to set the top left cell');
}
}

View File

@ -2,7 +2,10 @@
namespace PhpOffice\PhpSpreadsheetTests\Reader;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as ReaderXlsx;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as WriterXlsx;
use PHPUnit\Framework\TestCase;
class XlsxTest extends TestCase
@ -13,7 +16,32 @@ class XlsxTest extends TestCase
public function testLoadXlsxWithoutCellReference()
{
$filename = './data/Reader/XLSX/without_cell_reference.xlsx';
$reader = new Xlsx();
$reader = new ReaderXlsx();
$reader->load($filename);
}
public function testFreezePane()
{
$filename = tempnam(File::sysGetTempDir(), 'phpspreadsheet');
$cellSplit = 'B2';
$topLeftCell = 'E5';
$spreadsheet = new Spreadsheet();
$active = $spreadsheet->getActiveSheet();
$active->freezePane($cellSplit, $topLeftCell);
$writer = new WriterXlsx($spreadsheet);
$writer->save($filename);
// Read written file
$reader = new ReaderXlsx();
$reloadedSpreadsheet = $reader->load($filename);
$reloadedActive = $reloadedSpreadsheet->getActiveSheet();
$actualCellSplit = $reloadedActive->getFreezePane();
$actualTopLeftCell = $reloadedActive->getTopLeftCell();
self::assertSame($cellSplit, $actualCellSplit, 'should be able to set freeze pane');
self::assertSame($topLeftCell, $actualTopLeftCell, 'should be able to set the top left cell');
}
}