diff --git a/docs/_pages/documentation.md b/docs/_pages/documentation.md
index d63ca037..339962e3 100755
--- a/docs/_pages/documentation.md
+++ b/docs/_pages/documentation.md
@@ -50,6 +50,23 @@ $writer->setShouldCreateNewSheetsAutomatically(true); // default value
$writer->setShouldCreateNewSheetsAutomatically(false); // will stop writing new data when limit is reached
```
+### Sheet view (XLSX writer)
+
+Sheet view settings must be configured before any rows are added to the sheet.
+
+```php
+use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
+use Box\Spout\Writer\XLSX\Entity\SheetView;
+
+$sheetView = (new SheetView())
+ ->setFreezeRow(2) // First row will be fixed
+ ->setFreezeColumn('D') // Columns A to C will be fixed
+ ->setZoomScale(150); // And other options
+
+$writer = WriterEntityFactory::createXLSXWriter();
+$writer->getCurrentSheet()->setSheetView($sheetView);
+```
+
### Using a custom temporary folder
Processing XLSX and ODS files requires temporary files to be created. By default, {{ site.spout_html }} will use the system default temporary folder (as returned by `sys_get_temp_dir()`). It is possible to override this by explicitly setting it on the reader or writer:
diff --git a/src/Spout/Writer/Common/Entity/Sheet.php b/src/Spout/Writer/Common/Entity/Sheet.php
index aabdd91f..0666de36 100644
--- a/src/Spout/Writer/Common/Entity/Sheet.php
+++ b/src/Spout/Writer/Common/Entity/Sheet.php
@@ -3,6 +3,7 @@
namespace Box\Spout\Writer\Common\Entity;
use Box\Spout\Writer\Common\Manager\SheetManager;
+use Box\Spout\Writer\XLSX\Entity\SheetView;
/**
* Class Sheet
@@ -27,6 +28,9 @@ class Sheet
/** @var SheetManager Sheet manager */
private $sheetManager;
+ /** @var SheetView */
+ private $sheetView;
+
/**
* @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based)
* @param string $associatedWorkbookId ID of the sheet's associated workbook
@@ -108,4 +112,31 @@ public function setIsVisible($isVisible)
return $this;
}
+
+ /**
+ * @return SheetView|null
+ */
+ public function getSheetView(): ?SheetView
+ {
+ return $this->sheetView;
+ }
+
+ /**
+ * @param SheetView $sheetView
+ * @return $this
+ */
+ public function setSheetView(SheetView $sheetView)
+ {
+ $this->sheetView = $sheetView;
+
+ return $this;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasSheetView(): bool
+ {
+ return $this->sheetView instanceof SheetView;
+ }
}
diff --git a/src/Spout/Writer/Common/Entity/Worksheet.php b/src/Spout/Writer/Common/Entity/Worksheet.php
index 74c4976f..cd2eb680 100644
--- a/src/Spout/Writer/Common/Entity/Worksheet.php
+++ b/src/Spout/Writer/Common/Entity/Worksheet.php
@@ -23,6 +23,9 @@ class Worksheet
/** @var int Index of the last written row */
private $lastWrittenRowIndex;
+ /** @var bool */
+ private $hasStarted = false;
+
/**
* Worksheet constructor.
*
@@ -110,4 +113,20 @@ public function getId()
// sheet index is zero-based, while ID is 1-based
return $this->externalSheet->getIndex() + 1;
}
+
+ /**
+ * @return bool
+ */
+ public function hasStarted(): bool
+ {
+ return $this->hasStarted;
+ }
+
+ /**
+ * @param bool $hasStarted
+ */
+ public function setHasStarted(bool $hasStarted = true): void
+ {
+ $this->hasStarted = $hasStarted;
+ }
}
diff --git a/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php b/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php
index b5135555..982b0ae9 100644
--- a/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php
+++ b/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php
@@ -131,8 +131,6 @@ private function addNewSheet()
$worksheetFilePath = $this->getWorksheetFilePath($sheet);
$worksheet = $this->entityFactory->createWorksheet($worksheetFilePath, $sheet);
- $this->worksheetManager->startSheet($worksheet);
-
$worksheets[] = $worksheet;
$this->workbook->setWorksheets($worksheets);
@@ -223,8 +221,15 @@ public function addRowToCurrentWorksheet(Row $row)
if ($hasReachedMaxRows) {
// ... continue writing in a new sheet if option set
if ($this->optionsManager->getOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY)) {
+ $previousSheet = $currentWorksheet->getExternalSheet();
$currentWorksheet = $this->addNewSheetAndMakeItCurrent();
+ // Use the same sheet view if set
+ $sheet = $currentWorksheet->getExternalSheet();
+ if ($previousSheet->hasSheetView()) {
+ $sheet->setSheetView($previousSheet->getSheetView());
+ }
+
$this->addRowToWorksheet($currentWorksheet, $row);
} else {
// otherwise, do nothing as the data won't be written anyways
diff --git a/src/Spout/Writer/XLSX/Entity/SheetView.php b/src/Spout/Writer/XLSX/Entity/SheetView.php
new file mode 100644
index 00000000..3aad0842
--- /dev/null
+++ b/src/Spout/Writer/XLSX/Entity/SheetView.php
@@ -0,0 +1,295 @@
+showFormulas = $showFormulas;
+ return $this;
+ }
+
+ /**
+ * @param bool $showGridLines
+ * @return $this
+ */
+ public function setShowGridLines(bool $showGridLines): self
+ {
+ $this->showGridLines = $showGridLines;
+ return $this;
+ }
+
+ /**
+ * @param bool $showRowcolHeaders
+ * @return $this
+ */
+ public function setShowRowcolHeaders(bool $showRowcolHeaders): self
+ {
+ $this->showRowcolHeaders = $showRowcolHeaders;
+ return $this;
+ }
+
+ /**
+ * @param bool $showZeroes
+ * @return $this
+ */
+ public function setShowZeroes(bool $showZeroes): self
+ {
+ $this->showZeroes = $showZeroes;
+ return $this;
+ }
+
+ /**
+ * @param bool $rightToLeft
+ * @return $this
+ */
+ public function setRightToLeft(bool $rightToLeft): self
+ {
+ $this->rightToLeft = $rightToLeft;
+ return $this;
+ }
+
+ /**
+ * @param bool $tabSelected
+ * @return $this
+ */
+ public function setTabSelected(bool $tabSelected): self
+ {
+ $this->tabSelected = $tabSelected;
+ return $this;
+ }
+
+ /**
+ * @param bool $showOutlineSymbols
+ * @return $this
+ */
+ public function setShowOutlineSymbols(bool $showOutlineSymbols): self
+ {
+ $this->showOutlineSymbols = $showOutlineSymbols;
+ return $this;
+ }
+
+ /**
+ * @param bool $defaultGridColor
+ * @return $this
+ */
+ public function setDefaultGridColor(bool $defaultGridColor): self
+ {
+ $this->defaultGridColor = $defaultGridColor;
+ return $this;
+ }
+
+ /**
+ * @param string $view
+ * @return $this
+ */
+ public function setView(string $view): self
+ {
+ $this->view = $view;
+ return $this;
+ }
+
+ /**
+ * @param string $topLeftCell
+ * @return $this
+ */
+ public function setTopLeftCell(string $topLeftCell): self
+ {
+ $this->topLeftCell = $topLeftCell;
+ return $this;
+ }
+
+ /**
+ * @param int $colorId
+ * @return $this
+ */
+ public function setColorId(int $colorId): self
+ {
+ $this->colorId = $colorId;
+ return $this;
+ }
+
+ /**
+ * @param int $zoomScale
+ * @return $this
+ */
+ public function setZoomScale(int $zoomScale): self
+ {
+ $this->zoomScale = $zoomScale;
+ return $this;
+ }
+
+ /**
+ * @param int $zoomScaleNormal
+ * @return $this
+ */
+ public function setZoomScaleNormal(int $zoomScaleNormal): self
+ {
+ $this->zoomScaleNormal = $zoomScaleNormal;
+ return $this;
+ }
+
+ /**
+ * @param int $zoomScalePageLayoutView
+ * @return $this
+ */
+ public function setZoomScalePageLayoutView(int $zoomScalePageLayoutView): self
+ {
+ $this->zoomScalePageLayoutView = $zoomScalePageLayoutView;
+ return $this;
+ }
+
+ /**
+ * @param int $workbookViewId
+ * @return $this
+ */
+ public function setWorkbookViewId(int $workbookViewId): self
+ {
+ $this->workbookViewId = $workbookViewId;
+ return $this;
+ }
+
+ /**
+ * @param int $freezeRow Set to 2 to fix the first row
+ * @return $this
+ */
+ public function setFreezeRow(int $freezeRow): self
+ {
+ if ($freezeRow < 1) {
+ throw new InvalidArgumentException('Freeze row must be a positive integer', 1589543073);
+ }
+
+ $this->freezeRow = $freezeRow;
+ return $this;
+ }
+
+ /**
+ * @param string $freezeColumn Set to B to fix the first column
+ * @return $this
+ */
+ public function setFreezeColumn(string $freezeColumn): self
+ {
+ $this->freezeColumn = strtoupper($freezeColumn);
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getXml(): string
+ {
+ return 'getSheetViewAttributes() . '>' .
+ $this->getFreezeCellPaneXml() .
+ '';
+ }
+
+ /**
+ * @return string
+ */
+ protected function getSheetViewAttributes(): string
+ {
+ // Get class properties
+ $propertyValues = get_object_vars($this);
+ unset($propertyValues['freezeRow'], $propertyValues['freezeColumn']);
+
+ return $this->generateAttributes($propertyValues);
+ }
+
+ /**
+ * @return string
+ */
+ protected function getFreezeCellPaneXml(): string
+ {
+ if ($this->freezeRow < 2 && $this->freezeColumn === 'A') {
+ return '';
+ }
+
+ $columnIndex = CellHelper::getColumnIndexFromCellIndex($this->freezeColumn . '1');
+
+ return 'generateAttributes([
+ 'xSplit' => $columnIndex,
+ 'ySplit' => $this->freezeRow - 1,
+ 'topLeftCell' => $this->freezeColumn . $this->freezeRow,
+ 'activePane' => 'bottomRight',
+ 'state' => 'frozen',
+ ]) . '/>';
+ }
+
+ /**
+ * @param array $data with key containing the attribute name and value containing the attribute value
+ * @return string
+ */
+ protected function generateAttributes(array $data): string
+ {
+ // Create attribute for each key
+ $attributes = array_map(function ($key, $value) {
+ if (is_bool($value)) {
+ $value = $value ? 'true' : 'false';
+ }
+
+ return $key . '="' . $value . '"';
+ }, array_keys($data), $data);
+
+ // Append all attributes
+ return ' ' . implode(' ', $attributes);
+ }
+}
diff --git a/src/Spout/Writer/XLSX/Manager/WorksheetManager.php b/src/Spout/Writer/XLSX/Manager/WorksheetManager.php
index 741d0aa8..7005ea4f 100644
--- a/src/Spout/Writer/XLSX/Manager/WorksheetManager.php
+++ b/src/Spout/Writer/XLSX/Manager/WorksheetManager.php
@@ -17,6 +17,7 @@
use Box\Spout\Writer\Common\Manager\RowManager;
use Box\Spout\Writer\Common\Manager\Style\StyleMerger;
use Box\Spout\Writer\Common\Manager\WorksheetManagerInterface;
+use Box\Spout\Writer\XLSX\Entity\SheetView;
use Box\Spout\Writer\XLSX\Manager\Style\StyleManager;
/**
@@ -107,13 +108,34 @@ public function getSharedStringsManager()
*/
public function startSheet(Worksheet $worksheet)
{
+ // Only start once
+ if ($worksheet->hasStarted()) {
+ return;
+ }
+
$sheetFilePointer = \fopen($worksheet->getFilePath(), 'w');
$this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer);
$worksheet->setFilePointer($sheetFilePointer);
\fwrite($sheetFilePointer, self::SHEET_XML_FILE_HEADER);
+ $this->addSheetViews($sheetFilePointer, $worksheet);
\fwrite($sheetFilePointer, '');
+
+ $worksheet->setHasStarted();
+ }
+
+ /**
+ * @param resource $sheetFilePointer
+ * @param resourceWorksheet $worksheet
+ * @return void
+ */
+ private function addSheetViews($sheetFilePointer, Worksheet $worksheet): void
+ {
+ $sheet = $worksheet->getExternalSheet();
+ if ($sheet->hasSheetView()) {
+ \fwrite($sheetFilePointer, '' . $sheet->getSheetView()->getXml() . '');
+ }
}
/**
@@ -135,6 +157,9 @@ private function throwIfSheetFilePointerIsNotAvailable($sheetFilePointer)
*/
public function addRow(Worksheet $worksheet, Row $row)
{
+ // Start the sheet when the first row is added
+ $this->startSheet($worksheet);
+
if (!$this->rowManager->isEmpty($row)) {
$this->addNonEmptyRow($worksheet, $row);
}