Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix freeze pane for xlsx files #261

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion samples/Autofilter/10_Autofilter_selection_1.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
$spreadsheet->getActiveSheet()->getStyle('D2:D' . $row)->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_YYYYMMDD2);
$spreadsheet->getActiveSheet()->getStyle('E2:F' . $row)->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_CURRENCY_USD_SIMPLE);
$spreadsheet->getActiveSheet()->getColumnDimension('F')->setWidth(14);
$spreadsheet->getActiveSheet()->freezePane('A2');
$spreadsheet->getActiveSheet()->freezePane(0, 1);

// Set autofilter range
$helper->log('Set autofilter range');
Expand Down
2 changes: 1 addition & 1 deletion samples/Autofilter/10_Autofilter_selection_2.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
$spreadsheet->getActiveSheet()->getStyle('D2:D' . $row)->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_YYYYMMDD2);
$spreadsheet->getActiveSheet()->getStyle('E2:F' . $row)->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_CURRENCY_USD_SIMPLE);
$spreadsheet->getActiveSheet()->getColumnDimension('F')->setWidth(14);
$spreadsheet->getActiveSheet()->freezePane('A2');
$spreadsheet->getActiveSheet()->freezePane(0, 1);

// Set autofilter range
$helper->log('Set autofilter range');
Expand Down
2 changes: 1 addition & 1 deletion samples/Autofilter/10_Autofilter_selection_display.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
$spreadsheet->getActiveSheet()->getStyle('D2:D' . $row)->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_YYYYMMDD2);
$spreadsheet->getActiveSheet()->getStyle('E2:F' . $row)->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_CURRENCY_USD_SIMPLE);
$spreadsheet->getActiveSheet()->getColumnDimension('F')->setWidth(14);
$spreadsheet->getActiveSheet()->freezePane('A2');
$spreadsheet->getActiveSheet()->freezePane(0, 1);

// Set autofilter range
$helper->log('Set autofilter range');
Expand Down
2 changes: 1 addition & 1 deletion samples/templates/largeSpreadsheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

// Freeze panes
$helper->log('Freeze panes');
$spreadsheet->getActiveSheet()->freezePane('A2');
$spreadsheet->getActiveSheet()->freezePane(0, 1);

// Rows to repeat at top
$helper->log('Rows to repeat at top');
Expand Down
9 changes: 8 additions & 1 deletion src/PhpSpreadsheet/Reader/Xls.php
Original file line number Diff line number Diff line change
Expand Up @@ -4488,9 +4488,16 @@ private function readPane()
// 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(Cell::stringFromColumnIndex($px) . ($py + 1));
$topLeftCell = Cell::stringFromColumnIndex($colLeft) . ($rwTop + 1);
$this->phpSheet->freezePane($px, $py, $topLeftCell);
}
// unfrozen panes; split windows; not supported by PhpSpreadsheet core
}
Expand Down
25 changes: 13 additions & 12 deletions src/PhpSpreadsheet/Reader/Xlsx.php
Original file line number Diff line number Diff line change
Expand Up @@ -720,22 +720,23 @@ public function load($pFilename)
$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;
$xSplit = 0;
$ySplit = 0;
$topLeftCell = null;

if (isset($xmlSheet->sheetViews->sheetView->pane['xSplit'])) {
$xSplit = 1 + (int) ($xmlSheet->sheetViews->sheetView->pane['xSplit']);
}
if (isset($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']);
}
if (isset($xmlSheet->sheetViews->sheetView->pane['ySplit'])) {
$ySplit = (int) ($xmlSheet->sheetViews->sheetView->pane['ySplit']);
}

$docSheet->freezePaneByColumnAndRow($xSplit, $ySplit);
if (isset($xmlSheet->sheetViews->sheetView->pane['topLeftCell'])) {
$topLeftCell = (string) $xmlSheet->sheetViews->sheetView->pane['topLeftCell'];
}

$docSheet->freezePane($xSplit, $ySplit, $topLeftCell);
}

if (isset($xmlSheet->sheetViews->sheetView->selection)) {
Expand Down
14 changes: 12 additions & 2 deletions src/PhpSpreadsheet/ReferenceHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -583,8 +583,18 @@ public function insertNewBefore($pBefore, $pNumCols, $pNumRows, Worksheet $pShee
}

// Update worksheet: freeze pane
if ($pSheet->getFreezePane() != '') {
$pSheet->freezePane($this->updateCellReference($pSheet->getFreezePane(), $pBefore, $pNumCols, $pNumRows));
if ($pSheet->getFreezePane()) {
$splitCell = Cell::stringFromColumnIndex($pSheet->getColSplit()) . ($pSheet->getRowSplit() + 1);
$topLeftCell = $pSheet->getTopLeftCell();

$colSplit = $rowSplit = 0;
list($colSplit, $rowSplit) = Cell::coordinateFromString($this->updateCellReference($splitCell, $pBefore, $pNumCols, $pNumRows));

$colSplit = Cell::columnIndexFromString($colSplit) - 1;
$rowSplit = $rowSplit - 1;
$topLeftCell = $this->updateCellReference($topLeftCell, $pBefore, $pNumCols, $pNumRows);

$pSheet->freezePane($colSplit, $rowSplit, $topLeftCell);
}

// Page setup
Expand Down
105 changes: 78 additions & 27 deletions src/PhpSpreadsheet/Worksheet/Worksheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,32 @@ class Worksheet implements IComparable
private $autoFilter;

/**
* Freeze pane.
* Horizontal position of the split.
*
* @var int
*/
private $colSplit = 0;

/**
* Vertical position of the split.
*
* @var int
*/
private $rowSplit = 0;

/**
* Default position of the right bottom pane.
*
* @var string
*/
private $freezePane = '';
private $topLeftCell = '';

/**
* Pane frozen ?
*
* @var bool
*/
private $freezePane = false;

/**
* Show gridlines?
Expand Down Expand Up @@ -1962,9 +1983,9 @@ public function removeAutoFilter()
}

/**
* Get Freeze Pane.
* Freeze pane ?
*
* @return string
* @return bool
*/
public function getFreezePane()
{
Expand All @@ -1974,53 +1995,83 @@ public function getFreezePane()
/**
* 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)
* @param int $colSplit Horizontal position of the split
* @param int $rowSplit Vertical position of the split
* @param string $topLeftCell default position of the right bottom pane
*
* @throws Exception
*
* @return Worksheet
*/
public function freezePane($pCell)
public function freezePane($colSplit, $rowSplit, $topLeftCell = null)
{
// Uppercase coordinate
$pCell = strtoupper($pCell);
if (strpos($pCell, ':') === false && strpos($pCell, ',') === false) {
$this->freezePane = $pCell;
} else {
if (!isset($topLeftCell)) {
$topLeftCell = Cell::stringFromColumnIndex($colSplit) . ($rowSplit + 1);
}

if (!(strpos($topLeftCell, ':') === false && strpos($topLeftCell, ',') === false)) {
throw new Exception('Freeze pane can not be set on a range of cells.');
}

if (!is_int($colSplit) || !is_int($rowSplit)) {
throw new Exception('Split values should be integer to create freeze pane.');
}

// If colSplit and rowSplit are equal to zero the freeze pane is removed
if ($colSplit == 0 && $rowSplit == 0) {
$this->freezePane = false;

return $this;
}

$this->freezePane = true;

$this->colSplit = $colSplit;
$this->rowSplit = $rowSplit;

$this->topLeftCell = $topLeftCell;

return $this;
}

/**
* Freeze Pane by using numeric cell coordinates.
* Unfreeze Pane.
*
* @param int $pColumn Numeric column coordinate of the cell (A = 0)
* @param int $pRow Numeric row coordinate of the cell
* @return Worksheet
*/
public function unfreezePane()
{
return $this->freezePane(0, 0);
}

/**
* Get horizontal position of the split.
*
* @throws Exception
* @return int
*/
public function getColSplit()
{
return $this->colSplit;
}

/**
* Get vertical position of the split.
*
* @return Worksheet
* @return int
*/
public function freezePaneByColumnAndRow($pColumn, $pRow)
public function getRowSplit()
{
return $this->freezePane(Cell::stringFromColumnIndex($pColumn) . $pRow);
return $this->rowSplit;
}

/**
* Unfreeze Pane.
* Get the default position of the right bottom pane.
*
* @return Worksheet
* @return int
*/
public function unfreezePane()
public function getTopLeftCell()
{
return $this->freezePane('');
return $this->topLeftCell;
}

/**
Expand Down
11 changes: 7 additions & 4 deletions src/PhpSpreadsheet/Writer/Xls/Worksheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -1589,10 +1589,13 @@ private function writeRangeProtection()
private function writePanes()
{
$panes = [];
if ($freezePane = $this->phpSheet->getFreezePane()) {
list($column, $row) = Cell::coordinateFromString($freezePane);
$panes[0] = $row - 1;
$panes[1] = Cell::columnIndexFromString($column) - 1;
if ($this->phpSheet->getFreezePane()) {
$panes[0] = $this->phpSheet->getColSplit();
$panes[1] = $this->phpSheet->getRowSplit();
list($leftMostColumn, $topRow) = Cell::coordinateFromString($this->phpSheet->getTopLeftCell());
//Coordinates are zero-based in xls files
$panes[2] = ($topRow - 1);
$panes[3] = (Cell::columnIndexFromString($leftMostColumn) - 1);
} else {
// thaw panes
return;
Expand Down
24 changes: 11 additions & 13 deletions src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,31 +243,29 @@ private function writeSheetViews(XMLWriter $objWriter, PhpspreadsheetWorksheet $

// Pane
$pane = '';
$topLeftCell = $pSheet->getFreezePane();
if (($topLeftCell != '') && ($topLeftCell != 'A1')) {
$activeCell = $topLeftCell;
// Calculate freeze coordinates
$xSplit = $ySplit = 0;
if ($pSheet->getFreezePane()) {
$xSplit = $pSheet->getColSplit();
$ySplit = $pSheet->getRowSplit();

list($xSplit, $ySplit) = Cell::coordinateFromString($topLeftCell);
$xSplit = Cell::columnIndexFromString($xSplit);
$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');
Expand Down
40 changes: 40 additions & 0 deletions tests/PhpSpreadsheetTests/Reader/XlsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?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');

$colSplit = 1;
$rowSplit = 1;
$topLeftCell = 'B4';

$spreadsheet = new Spreadsheet();
$active = $spreadsheet->getActiveSheet();
$active->freezePane($colSplit, $rowSplit, $topLeftCell);

$writer = new WriterXls($spreadsheet);
$writer->save($filename);

// Read written file
$reader = new ReaderXls();
$reloadedSpreadsheet = $reader->load($filename);
$reloadedActive = $reloadedSpreadsheet->getActiveSheet();
$actualColSplit = $reloadedActive->getColSplit();
$actualRowSplit = $reloadedActive->getRowSplit();
$actualTopLeftCell = $reloadedActive->getTopLeftCell();

self::assertSame($colSplit, $actualColSplit, 'should be able to set horizontal split');
self::assertSame($rowSplit, $actualRowSplit, 'should be able to set vertical split');
self::assertSame($topLeftCell, $actualTopLeftCell, 'should be able to set the top left cell');
}
}
Loading