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 ### Added
- Support to write merged cells in ODS format [#287](https://github.com/PHPOffice/PhpSpreadsheet/issues/287) - 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 ### Changed

View File

@ -4488,9 +4488,17 @@ class Xls extends BaseReader
// offset: 2; size: 2; position of horizontal split // offset: 2; size: 2; position of horizontal split
$py = self::getUInt2d($recordData, 2); $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) { if ($this->frozen) {
// frozen panes // 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 // 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'])); $docSheet->setRightToLeft(self::boolean((string) $xmlSheet->sheetViews->sheetView['rightToLeft']));
} }
if (isset($xmlSheet->sheetViews->sheetView->pane)) { if (isset($xmlSheet->sheetViews->sheetView->pane)) {
if (isset($xmlSheet->sheetViews->sheetView->pane['topLeftCell'])) {
$docSheet->freezePane((string) $xmlSheet->sheetViews->sheetView->pane['topLeftCell']);
} else {
$xSplit = 0; $xSplit = 0;
$ySplit = 0; $ySplit = 0;
$topLeftCell = null;
if (isset($xmlSheet->sheetViews->sheetView->pane['xSplit'])) { 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'])) { 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)) { if (isset($xmlSheet->sheetViews->sheetView->selection)) {

View File

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

View File

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

View File

@ -1589,10 +1589,15 @@ class Worksheet extends BIFFwriter
private function writePanes() private function writePanes()
{ {
$panes = []; $panes = [];
if ($freezePane = $this->phpSheet->getFreezePane()) { if ($this->phpSheet->getFreezePane()) {
list($column, $row) = Coordinate::coordinateFromString($freezePane); list($column, $row) = Coordinate::coordinateFromString($this->phpSheet->getFreezePane());
$panes[0] = $row - 1; $panes[0] = Coordinate::columnIndexFromString($column) - 1;
$panes[1] = 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 { } else {
// thaw panes // thaw panes
return; return;

View File

@ -244,31 +244,31 @@ class Worksheet extends WriterPart
// Pane // Pane
$pane = ''; $pane = '';
$topLeftCell = $pSheet->getFreezePane(); if ($pSheet->getFreezePane()) {
if (($topLeftCell != '') && ($topLeftCell != 'A1')) { list($xSplit, $ySplit) = Coordinate::coordinateFromString($pSheet->getFreezePane());
$activeCell = $topLeftCell;
// Calculate freeze coordinates
$xSplit = $ySplit = 0;
list($xSplit, $ySplit) = Coordinate::coordinateFromString($topLeftCell);
$xSplit = Coordinate::columnIndexFromString($xSplit); $xSplit = Coordinate::columnIndexFromString($xSplit);
--$xSplit;
--$ySplit;
$topLeftCell = $pSheet->getTopLeftCell();
$activeCell = $topLeftCell;
// pane // pane
$pane = 'topRight'; $pane = 'topRight';
$objWriter->startElement('pane'); $objWriter->startElement('pane');
if ($xSplit > 1) { if ($xSplit > 0) {
$objWriter->writeAttribute('xSplit', $xSplit - 1); $objWriter->writeAttribute('xSplit', $xSplit);
} }
if ($ySplit > 1) { if ($ySplit > 0) {
$objWriter->writeAttribute('ySplit', $ySplit - 1); $objWriter->writeAttribute('ySplit', $ySplit);
$pane = ($xSplit > 1) ? 'bottomRight' : 'bottomLeft'; $pane = ($xSplit > 0) ? 'bottomRight' : 'bottomLeft';
} }
$objWriter->writeAttribute('topLeftCell', $topLeftCell); $objWriter->writeAttribute('topLeftCell', $topLeftCell);
$objWriter->writeAttribute('activePane', $pane); $objWriter->writeAttribute('activePane', $pane);
$objWriter->writeAttribute('state', 'frozen'); $objWriter->writeAttribute('state', 'frozen');
$objWriter->endElement(); $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) // Write additional selections if more than two panes (ie both an X and a Y split)
$objWriter->startElement('selection'); $objWriter->startElement('selection');
$objWriter->writeAttribute('pane', 'topRight'); $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; 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; use PHPUnit\Framework\TestCase;
class XlsxTest extends TestCase class XlsxTest extends TestCase
@ -13,7 +16,32 @@ class XlsxTest extends TestCase
public function testLoadXlsxWithoutCellReference() public function testLoadXlsxWithoutCellReference()
{ {
$filename = './data/Reader/XLSX/without_cell_reference.xlsx'; $filename = './data/Reader/XLSX/without_cell_reference.xlsx';
$reader = new Xlsx(); $reader = new ReaderXlsx();
$reader->load($filename); $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');
}
} }