Skip to content

Commit 11b055b

Browse files
Adrien CohenPowerKiKi
Adrien Cohen
authored andcommitted
Able to set the topLeftCell in freeze panes
Fixes #260 Closes #261
1 parent eb58563 commit 11b055b

File tree

9 files changed

+159
-51
lines changed

9 files changed

+159
-51
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
### Added
1111

1212
- Support to write merged cells in ODS format [#287](https://github.com/PHPOffice/PhpSpreadsheet/issues/287)
13+
- Able to set the `topLeftCell` in freeze panes [#261](https://github.com/PHPOffice/PhpSpreadsheet/pull/261)
1314

1415
### Changed
1516

src/PhpSpreadsheet/Reader/Xls.php

+9-1
Original file line numberDiff line numberDiff line change
@@ -4488,9 +4488,17 @@ private function readPane()
44884488
// offset: 2; size: 2; position of horizontal split
44894489
$py = self::getUInt2d($recordData, 2);
44904490

4491+
// offset: 4; size: 2; top most visible row in the bottom pane
4492+
$rwTop = self::getUInt2d($recordData, 4);
4493+
4494+
// offset: 6; size: 2; first visible left column in the right pane
4495+
$colLeft = self::getUInt2d($recordData, 6);
4496+
44914497
if ($this->frozen) {
44924498
// frozen panes
4493-
$this->phpSheet->freezePane(Coordinate::stringFromColumnIndex($px + 1) . ($py + 1));
4499+
$cell = Coordinate::stringFromColumnIndex($px + 1) . ($py + 1);
4500+
$topLeftCell = Coordinate::stringFromColumnIndex($colLeft + 1) . ($rwTop + 1);
4501+
$this->phpSheet->freezePane($cell, $topLeftCell);
44944502
}
44954503
// unfrozen panes; split windows; not supported by PhpSpreadsheet core
44964504
}

src/PhpSpreadsheet/Reader/Xlsx.php

+13-12
Original file line numberDiff line numberDiff line change
@@ -720,22 +720,23 @@ public function load($pFilename)
720720
$docSheet->setRightToLeft(self::boolean((string) $xmlSheet->sheetViews->sheetView['rightToLeft']));
721721
}
722722
if (isset($xmlSheet->sheetViews->sheetView->pane)) {
723-
if (isset($xmlSheet->sheetViews->sheetView->pane['topLeftCell'])) {
724-
$docSheet->freezePane((string) $xmlSheet->sheetViews->sheetView->pane['topLeftCell']);
725-
} else {
726-
$xSplit = 0;
727-
$ySplit = 0;
723+
$xSplit = 0;
724+
$ySplit = 0;
725+
$topLeftCell = null;
728726

729-
if (isset($xmlSheet->sheetViews->sheetView->pane['xSplit'])) {
730-
$xSplit = 1 + (int) ($xmlSheet->sheetViews->sheetView->pane['xSplit']);
731-
}
727+
if (isset($xmlSheet->sheetViews->sheetView->pane['xSplit'])) {
728+
$xSplit = (int) ($xmlSheet->sheetViews->sheetView->pane['xSplit']);
729+
}
732730

733-
if (isset($xmlSheet->sheetViews->sheetView->pane['ySplit'])) {
734-
$ySplit = 1 + (int) ($xmlSheet->sheetViews->sheetView->pane['ySplit']);
735-
}
731+
if (isset($xmlSheet->sheetViews->sheetView->pane['ySplit'])) {
732+
$ySplit = (int) ($xmlSheet->sheetViews->sheetView->pane['ySplit']);
733+
}
736734

737-
$docSheet->freezePaneByColumnAndRow($xSplit + 1, $ySplit);
735+
if (isset($xmlSheet->sheetViews->sheetView->pane['topLeftCell'])) {
736+
$topLeftCell = (string) $xmlSheet->sheetViews->sheetView->pane['topLeftCell'];
738737
}
738+
739+
$docSheet->freezePane(Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1), $topLeftCell);
739740
}
740741

741742
if (isset($xmlSheet->sheetViews->sheetView->selection)) {

src/PhpSpreadsheet/ReferenceHelper.php

+8-2
Original file line numberDiff line numberDiff line change
@@ -576,8 +576,14 @@ public function insertNewBefore($pBefore, $pNumCols, $pNumRows, Worksheet $pShee
576576
}
577577

578578
// Update worksheet: freeze pane
579-
if ($pSheet->getFreezePane() != '') {
580-
$pSheet->freezePane($this->updateCellReference($pSheet->getFreezePane(), $pBefore, $pNumCols, $pNumRows));
579+
if ($pSheet->getFreezePane()) {
580+
$splitCell = $pSheet->getFreezePane();
581+
$topLeftCell = $pSheet->getTopLeftCell();
582+
583+
$splitCell = $this->updateCellReference($splitCell, $pBefore, $pNumCols, $pNumRows);
584+
$topLeftCell = $this->updateCellReference($topLeftCell, $pBefore, $pNumCols, $pNumRows);
585+
586+
$pSheet->freezePane($splitCell, $topLeftCell);
581587
}
582588

583589
// Page setup

src/PhpSpreadsheet/Worksheet/Worksheet.php

+39-17
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,16 @@ class Worksheet implements IComparable
201201
/**
202202
* Freeze pane.
203203
*
204-
* @var string
204+
* @var null|string
205+
*/
206+
private $freezePane;
207+
208+
/**
209+
* Default position of the right bottom pane.
210+
*
211+
* @var null|string
205212
*/
206-
private $freezePane = '';
213+
private $topLeftCell;
207214

208215
/**
209216
* Show gridlines?
@@ -1975,27 +1982,33 @@ public function getFreezePane()
19751982
/**
19761983
* Freeze Pane.
19771984
*
1978-
* @param string $pCell Cell (i.e. A2)
1979-
* Examples:
1980-
* A2 will freeze the rows above cell A2 (i.e row 1)
1981-
* B1 will freeze the columns to the left of cell B1 (i.e column A)
1982-
* B2 will freeze the rows above and to the left of cell A2
1983-
* (i.e row 1 and column A)
1985+
* Examples:
1986+
*
1987+
* - A2 will freeze the rows above cell A2 (i.e row 1)
1988+
* - B1 will freeze the columns to the left of cell B1 (i.e column A)
1989+
* - B2 will freeze the rows above and to the left of cell A2 (i.e row 1 and column A)
1990+
*
1991+
* @param null|string $cell Position of the split
1992+
* @param null|string $topLeftCell default position of the right bottom pane
19841993
*
19851994
* @throws Exception
19861995
*
19871996
* @return Worksheet
19881997
*/
1989-
public function freezePane($pCell)
1998+
public function freezePane($cell, $topLeftCell = null)
19901999
{
1991-
// Uppercase coordinate
1992-
$pCell = strtoupper($pCell);
1993-
if (strpos($pCell, ':') === false && strpos($pCell, ',') === false) {
1994-
$this->freezePane = $pCell;
1995-
} else {
2000+
if (is_string($cell) && (strpos($cell, ':') !== false || strpos($cell, ',') !== false)) {
19962001
throw new Exception('Freeze pane can not be set on a range of cells.');
19972002
}
19982003

2004+
if ($cell !== null && $topLeftCell === null) {
2005+
$coordinate = Coordinate::coordinateFromString($cell);
2006+
$topLeftCell = $coordinate[0] . ($coordinate[1] + 1);
2007+
}
2008+
2009+
$this->freezePane = $cell;
2010+
$this->topLeftCell = $topLeftCell;
2011+
19992012
return $this;
20002013
}
20012014

@@ -2005,8 +2018,6 @@ public function freezePane($pCell)
20052018
* @param int $columnIndex Numeric column coordinate of the cell
20062019
* @param int $row Numeric row coordinate of the cell
20072020
*
2008-
* @throws Exception
2009-
*
20102021
* @return Worksheet
20112022
*/
20122023
public function freezePaneByColumnAndRow($columnIndex, $row)
@@ -2021,7 +2032,17 @@ public function freezePaneByColumnAndRow($columnIndex, $row)
20212032
*/
20222033
public function unfreezePane()
20232034
{
2024-
return $this->freezePane('');
2035+
return $this->freezePane(null);
2036+
}
2037+
2038+
/**
2039+
* Get the default position of the right bottom pane.
2040+
*
2041+
* @return int
2042+
*/
2043+
public function getTopLeftCell()
2044+
{
2045+
return $this->topLeftCell;
20252046
}
20262047

20272048
/**
@@ -2622,6 +2643,7 @@ public function toArray($nullValue = null, $calculateFormulas = true, $formatDat
26222643
// Identify the range that we need to extract from the worksheet
26232644
$maxCol = $this->getHighestColumn();
26242645
$maxRow = $this->getHighestRow();
2646+
26252647
// Return
26262648
return $this->rangeToArray('A1:' . $maxCol . $maxRow, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
26272649
}

src/PhpSpreadsheet/Writer/Xls/Worksheet.php

+9-4
Original file line numberDiff line numberDiff line change
@@ -1589,10 +1589,15 @@ private function writeRangeProtection()
15891589
private function writePanes()
15901590
{
15911591
$panes = [];
1592-
if ($freezePane = $this->phpSheet->getFreezePane()) {
1593-
list($column, $row) = Coordinate::coordinateFromString($freezePane);
1594-
$panes[0] = $row - 1;
1595-
$panes[1] = Coordinate::columnIndexFromString($column) - 1;
1592+
if ($this->phpSheet->getFreezePane()) {
1593+
list($column, $row) = Coordinate::coordinateFromString($this->phpSheet->getFreezePane());
1594+
$panes[0] = Coordinate::columnIndexFromString($column) - 1;
1595+
$panes[1] = $row - 1;
1596+
1597+
list($leftMostColumn, $topRow) = Coordinate::coordinateFromString($this->phpSheet->getTopLeftCell());
1598+
//Coordinates are zero-based in xls files
1599+
$panes[2] = $topRow - 1;
1600+
$panes[3] = Coordinate::columnIndexFromString($leftMostColumn) - 1;
15961601
} else {
15971602
// thaw panes
15981603
return;

src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php

+13-13
Original file line numberDiff line numberDiff line change
@@ -244,31 +244,31 @@ private function writeSheetViews(XMLWriter $objWriter, PhpspreadsheetWorksheet $
244244

245245
// Pane
246246
$pane = '';
247-
$topLeftCell = $pSheet->getFreezePane();
248-
if (($topLeftCell != '') && ($topLeftCell != 'A1')) {
249-
$activeCell = $topLeftCell;
250-
// Calculate freeze coordinates
251-
$xSplit = $ySplit = 0;
252-
253-
list($xSplit, $ySplit) = Coordinate::coordinateFromString($topLeftCell);
247+
if ($pSheet->getFreezePane()) {
248+
list($xSplit, $ySplit) = Coordinate::coordinateFromString($pSheet->getFreezePane());
254249
$xSplit = Coordinate::columnIndexFromString($xSplit);
250+
--$xSplit;
251+
--$ySplit;
252+
253+
$topLeftCell = $pSheet->getTopLeftCell();
254+
$activeCell = $topLeftCell;
255255

256256
// pane
257257
$pane = 'topRight';
258258
$objWriter->startElement('pane');
259-
if ($xSplit > 1) {
260-
$objWriter->writeAttribute('xSplit', $xSplit - 1);
259+
if ($xSplit > 0) {
260+
$objWriter->writeAttribute('xSplit', $xSplit);
261261
}
262-
if ($ySplit > 1) {
263-
$objWriter->writeAttribute('ySplit', $ySplit - 1);
264-
$pane = ($xSplit > 1) ? 'bottomRight' : 'bottomLeft';
262+
if ($ySplit > 0) {
263+
$objWriter->writeAttribute('ySplit', $ySplit);
264+
$pane = ($xSplit > 0) ? 'bottomRight' : 'bottomLeft';
265265
}
266266
$objWriter->writeAttribute('topLeftCell', $topLeftCell);
267267
$objWriter->writeAttribute('activePane', $pane);
268268
$objWriter->writeAttribute('state', 'frozen');
269269
$objWriter->endElement();
270270

271-
if (($xSplit > 1) && ($ySplit > 1)) {
271+
if (($xSplit > 0) && ($ySplit > 0)) {
272272
// Write additional selections if more than two panes (ie both an X and a Y split)
273273
$objWriter->startElement('selection');
274274
$objWriter->writeAttribute('pane', 'topRight');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheetTests\Reader;
4+
5+
use PhpOffice\PhpSpreadsheet\Reader\Xls as ReaderXls;
6+
use PhpOffice\PhpSpreadsheet\Shared\File;
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PhpOffice\PhpSpreadsheet\Writer\Xls as WriterXls;
9+
use PHPUnit_Framework_TestCase;
10+
11+
class XlsTest extends PHPUnit_Framework_TestCase
12+
{
13+
public function testFreezePane()
14+
{
15+
$filename = tempnam(File::sysGetTempDir(), 'phpspreadsheet');
16+
17+
$cellSplit = 'B2';
18+
$topLeftCell = 'E5';
19+
20+
$spreadsheet = new Spreadsheet();
21+
$active = $spreadsheet->getActiveSheet();
22+
$active->freezePane($cellSplit, $topLeftCell);
23+
24+
$writer = new WriterXls($spreadsheet);
25+
$writer->save($filename);
26+
27+
// Read written file
28+
$reader = new ReaderXls();
29+
$reloadedSpreadsheet = $reader->load($filename);
30+
$reloadedActive = $reloadedSpreadsheet->getActiveSheet();
31+
$actualCellSplit = $reloadedActive->getFreezePane();
32+
$actualTopLeftCell = $reloadedActive->getTopLeftCell();
33+
34+
self::assertSame($cellSplit, $actualCellSplit, 'should be able to set freeze pane');
35+
self::assertSame($topLeftCell, $actualTopLeftCell, 'should be able to set the top left cell');
36+
}
37+
}

tests/PhpSpreadsheetTests/Reader/XlsxTest.php

+30-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
namespace PhpOffice\PhpSpreadsheetTests\Reader;
44

5-
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
5+
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as ReaderXlsx;
6+
use PhpOffice\PhpSpreadsheet\Shared\File;
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as WriterXlsx;
69
use PHPUnit\Framework\TestCase;
710

811
class XlsxTest extends TestCase
@@ -13,7 +16,32 @@ class XlsxTest extends TestCase
1316
public function testLoadXlsxWithoutCellReference()
1417
{
1518
$filename = './data/Reader/XLSX/without_cell_reference.xlsx';
16-
$reader = new Xlsx();
19+
$reader = new ReaderXlsx();
1720
$reader->load($filename);
1821
}
22+
23+
public function testFreezePane()
24+
{
25+
$filename = tempnam(File::sysGetTempDir(), 'phpspreadsheet');
26+
27+
$cellSplit = 'B2';
28+
$topLeftCell = 'E5';
29+
30+
$spreadsheet = new Spreadsheet();
31+
$active = $spreadsheet->getActiveSheet();
32+
$active->freezePane($cellSplit, $topLeftCell);
33+
34+
$writer = new WriterXlsx($spreadsheet);
35+
$writer->save($filename);
36+
37+
// Read written file
38+
$reader = new ReaderXlsx();
39+
$reloadedSpreadsheet = $reader->load($filename);
40+
$reloadedActive = $reloadedSpreadsheet->getActiveSheet();
41+
$actualCellSplit = $reloadedActive->getFreezePane();
42+
$actualTopLeftCell = $reloadedActive->getTopLeftCell();
43+
44+
self::assertSame($cellSplit, $actualCellSplit, 'should be able to set freeze pane');
45+
self::assertSame($topLeftCell, $actualTopLeftCell, 'should be able to set the top left cell');
46+
}
1947
}

0 commit comments

Comments
 (0)