diff --git a/src/Spout/Writer/Common/Entity/Options.php b/src/Spout/Writer/Common/Entity/Options.php
index d7152bb..3918333 100644
--- a/src/Spout/Writer/Common/Entity/Options.php
+++ b/src/Spout/Writer/Common/Entity/Options.php
@@ -20,4 +20,9 @@ abstract class Options
// XLSX specific options
const SHOULD_USE_INLINE_STRINGS = 'shouldUseInlineStrings';
+
+ // Cell size options
+ const DEFAULT_COLUMN_WIDTH = 'defaultColumnWidth';
+ const DEFAULT_ROW_HEIGHT = 'defaultRowHeight';
+ const COLUMN_WIDTHS = 'columnWidthDefinition';
}
diff --git a/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php b/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php
index 7be5c6e..12c6523 100644
--- a/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php
+++ b/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php
@@ -103,7 +103,6 @@ abstract class WorkbookManagerAbstract implements WorkbookManagerInterface
* Creates a new sheet in the workbook and make it the current sheet.
* The writing will resume where it stopped (i.e. data won't be truncated).
*
- * @throws IOException If unable to open the sheet for writing
* @return Worksheet The created sheet
*/
public function addNewSheetAndMakeItCurrent()
@@ -117,8 +116,8 @@ abstract class WorkbookManagerAbstract implements WorkbookManagerInterface
/**
* Creates a new sheet in the workbook. The current sheet remains unchanged.
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to open the sheet for writing
* @return Worksheet The created sheet
+ * @throws IOException
*/
private function addNewSheet()
{
@@ -157,6 +156,16 @@ abstract class WorkbookManagerAbstract implements WorkbookManagerInterface
return $this->currentWorksheet;
}
+ /**
+ * Starts the current sheet and opens the file pointer
+ *
+ * @throws IOException
+ */
+ public function startCurrentSheet()
+ {
+ $this->worksheetManager->startSheet($this->getCurrentWorksheet());
+ }
+
/**
* Sets the given sheet as the current one. New data will be written to this sheet.
* The writing will resume where it stopped (i.e. data won't be truncated).
@@ -210,9 +219,10 @@ abstract class WorkbookManagerAbstract implements WorkbookManagerInterface
* with the creation of new worksheets if one worksheet has reached its maximum capicity.
*
* @param Row $row The row to be added
- * @throws IOException If trying to create a new sheet and unable to open the sheet for writing
- * @throws WriterException If unable to write data
+ *
* @return void
+ * @throws IOException If trying to create a new sheet and unable to open the sheet for writing
+ * @throws \Box\Spout\Common\Exception\InvalidArgumentException
*/
public function addRowToCurrentWorksheet(Row $row)
{
@@ -249,8 +259,10 @@ abstract class WorkbookManagerAbstract implements WorkbookManagerInterface
*
* @param Worksheet $worksheet Worksheet to write the row to
* @param Row $row The row to be added
- * @throws WriterException If unable to write data
+ *
* @return void
+ * @throws IOException
+ * @throws \Box\Spout\Common\Exception\InvalidArgumentException
*/
private function addRowToWorksheet(Worksheet $worksheet, Row $row)
{
@@ -276,6 +288,28 @@ abstract class WorkbookManagerAbstract implements WorkbookManagerInterface
}
}
+ /**
+ * @param float|null $width
+ */
+ public function setDefaultColumnWidth(float $width) {
+ $this->worksheetManager->setDefaultColumnWidth($width);
+ }
+
+ /**
+ * @param float|null $height
+ */
+ public function setDefaultRowHeight(float $height) {
+ $this->worksheetManager->setDefaultRowHeight($height);
+ }
+
+ /**
+ * @param float|null $width
+ * @param array $columns One or more columns with this width
+ */
+ public function setColumnWidth($width, ...$columns) {
+ $this->worksheetManager->setColumnWidth($width, ...$columns);
+ }
+
/**
* Closes the workbook and all its associated sheets.
* All the necessary files are written to disk and zipped together to create the final file.
diff --git a/src/Spout/Writer/Common/Manager/WorkbookManagerInterface.php b/src/Spout/Writer/Common/Manager/WorkbookManagerInterface.php
index aed304a..7bb469e 100644
--- a/src/Spout/Writer/Common/Manager/WorkbookManagerInterface.php
+++ b/src/Spout/Writer/Common/Manager/WorkbookManagerInterface.php
@@ -42,6 +42,21 @@ interface WorkbookManagerInterface
*/
public function getCurrentWorksheet();
+ /**
+ * Starts the current sheet and opens its file pointer
+ */
+ public function startCurrentSheet();
+
+ /**
+ * @param float $width
+ */
+ public function setDefaultColumnWidth(float $width);
+
+ /**
+ * @param float $height
+ */
+ public function setDefaultRowHeight(float $height);
+
/**
* Sets the given sheet as the current one. New data will be written to this sheet.
* The writing will resume where it stopped (i.e. data won't be truncated).
diff --git a/src/Spout/Writer/WriterMultiSheetsAbstract.php b/src/Spout/Writer/WriterMultiSheetsAbstract.php
index 8170b67..6148738 100644
--- a/src/Spout/Writer/WriterMultiSheetsAbstract.php
+++ b/src/Spout/Writer/WriterMultiSheetsAbstract.php
@@ -4,6 +4,7 @@ namespace Box\Spout\Writer;
use Box\Spout\Common\Creator\HelperFactory;
use Box\Spout\Common\Entity\Row;
+use Box\Spout\Common\Exception\IOException;
use Box\Spout\Common\Helper\GlobalFunctionsHelper;
use Box\Spout\Common\Manager\OptionsManagerInterface;
use Box\Spout\Writer\Common\Creator\ManagerFactoryInterface;
@@ -96,8 +97,9 @@ abstract class WriterMultiSheetsAbstract extends WriterAbstract
/**
* Creates a new sheet and make it the current sheet. The data will now be written to this sheet.
*
- * @throws WriterNotOpenedException If the writer has not been opened yet
* @return Sheet The created sheet
+ * @throws IOException
+ * @throws WriterNotOpenedException If the writer has not been opened yet
*/
public function addNewSheetAndMakeItCurrent()
{
@@ -135,6 +137,36 @@ abstract class WriterMultiSheetsAbstract extends WriterAbstract
$this->workbookManager->setCurrentSheet($sheet);
}
+ /**
+ * @param float $width
+ * @throws WriterNotOpenedException
+ */
+ public function setDefaultColumnWidth(float $width)
+ {
+ $this->throwIfWorkbookIsNotAvailable();
+ $this->workbookManager->setDefaultColumnWidth($width);
+ }
+
+ /**
+ * @param float $height
+ * @throws WriterNotOpenedException
+ */
+ public function setDefaultRowHeight(float $height)
+ {
+ $this->throwIfWorkbookIsNotAvailable();
+ $this->workbookManager->setDefaultRowHeight($height);
+ }
+
+ /**
+ * @param float|null $width
+ * @param array $columns One or more columns with this width
+ * @throws WriterNotOpenedException
+ */
+ public function setColumnWidth($width, ...$columns) {
+ $this->throwIfWorkbookIsNotAvailable();
+ $this->workbookManager->setColumnWidth($width, ...$columns);
+ }
+
/**
* Checks if the workbook has been created. Throws an exception if not created yet.
*
@@ -143,13 +175,15 @@ abstract class WriterMultiSheetsAbstract extends WriterAbstract
*/
protected function throwIfWorkbookIsNotAvailable()
{
- if (!$this->workbookManager->getWorkbook()) {
+ if (empty($this->workbookManager) || !$this->workbookManager->getWorkbook()) {
throw new WriterNotOpenedException('The writer must be opened before performing this action.');
}
}
/**
* {@inheritdoc}
+ *
+ * @throws Exception\WriterException
*/
protected function addRowToWriter(Row $row)
{
diff --git a/src/Spout/Writer/XLSX/Manager/OptionsManager.php b/src/Spout/Writer/XLSX/Manager/OptionsManager.php
index 53718bc..d83b8ae 100644
--- a/src/Spout/Writer/XLSX/Manager/OptionsManager.php
+++ b/src/Spout/Writer/XLSX/Manager/OptionsManager.php
@@ -39,6 +39,9 @@ class OptionsManager extends OptionsManagerAbstract
Options::DEFAULT_ROW_STYLE,
Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY,
Options::SHOULD_USE_INLINE_STRINGS,
+ Options::DEFAULT_COLUMN_WIDTH,
+ Options::DEFAULT_ROW_HEIGHT,
+ Options::COLUMN_WIDTHS,
];
}
diff --git a/src/Spout/Writer/XLSX/Manager/WorksheetManager.php b/src/Spout/Writer/XLSX/Manager/WorksheetManager.php
index b0e458f..f6ee831 100644
--- a/src/Spout/Writer/XLSX/Manager/WorksheetManager.php
+++ b/src/Spout/Writer/XLSX/Manager/WorksheetManager.php
@@ -62,6 +62,18 @@ EOD;
/** @var InternalEntityFactory Factory to create entities */
private $entityFactory;
+ /** @var float|null The default column width to use */
+ private $defaultColumnWidth;
+
+ /** @var float|null The default row height to use */
+ private $defaultRowHeight;
+
+ /** @var bool Whether rows have been written */
+ private $hasWrittenRows = false;
+
+ /** @var array Array of min-max-width arrays */
+ private $columnWidths;
+
/**
* WorksheetManager constructor.
*
@@ -85,6 +97,9 @@ EOD;
InternalEntityFactory $entityFactory
) {
$this->shouldUseInlineStrings = $optionsManager->getOption(Options::SHOULD_USE_INLINE_STRINGS);
+ $this->setDefaultColumnWidth($optionsManager->getOption(Options::DEFAULT_COLUMN_WIDTH));
+ $this->setDefaultRowHeight($optionsManager->getOption(Options::DEFAULT_ROW_HEIGHT));
+ $this->columnWidths = $optionsManager->getOption(Options::COLUMN_WIDTHS) ?? [];
$this->rowManager = $rowManager;
$this->styleManager = $styleManager;
$this->styleMerger = $styleMerger;
@@ -102,6 +117,39 @@ EOD;
return $this->sharedStringsManager;
}
+ /**
+ * @param float|null $width
+ */
+ public function setDefaultColumnWidth($width) {
+ $this->defaultColumnWidth = $width;
+ }
+
+ /**
+ * @param float|null $height
+ */
+ public function setDefaultRowHeight($height) {
+ $this->defaultRowHeight = $height;
+ }
+
+ /**
+ * @param float|null $width
+ * @param array $columns One or more columns with this width
+ */
+ public function setColumnWidth($width, ...$columns) {
+ // Gather sequences
+ $sequence = [];
+ foreach ($columns as $i) {
+ $sequenceLength = count($sequence);
+ $previousValue = $sequence[$sequenceLength - 1];
+ if ($sequenceLength > 0 && $i !== $previousValue + 1) {
+ $this->columnWidths[] = [$sequence[0], $previousValue, $width];
+ $sequence = [];
+ }
+ $sequence[] = $i;
+ }
+ $this->columnWidths[] = [$sequence[0], $sequence[count($sequence) - 1], $width];
+ }
+
/**
* {@inheritdoc}
*/
@@ -113,7 +161,6 @@ EOD;
$worksheet->setFilePointer($sheetFilePointer);
fwrite($sheetFilePointer, self::SHEET_XML_FILE_HEADER);
- fwrite($sheetFilePointer, '');
}
/**
@@ -153,6 +200,12 @@ EOD;
*/
private function addNonEmptyRow(Worksheet $worksheet, Row $row)
{
+ $sheetFilePointer = $worksheet->getFilePointer();
+ if (!$this->hasWrittenRows) {
+ fwrite($sheetFilePointer, $this->getXMLFragmentForDefaultCellSizing());
+ fwrite($sheetFilePointer, $this->getXMLFragmentForColumnWidths());
+ fwrite($sheetFilePointer, '');
+ }
$cellIndex = 0;
$rowStyle = $row->getStyle();
$rowIndex = $worksheet->getLastWrittenRowIndex() + 1;
@@ -167,10 +220,11 @@ EOD;
$rowXML .= '';
- $wasWriteSuccessful = fwrite($worksheet->getFilePointer(), $rowXML);
+ $wasWriteSuccessful = fwrite($sheetFilePointer, $rowXML);
if ($wasWriteSuccessful === false) {
throw new IOException("Unable to write data in {$worksheet->getFilePath()}");
}
+ $this->hasWrittenRows = true;
}
/**
@@ -256,6 +310,41 @@ EOD;
return $cellXMLFragment;
}
+ /**
+ * Construct column width references xml to inject into worksheet xml file
+ *
+ * @return string
+ */
+ public function getXMLFragmentForColumnWidths()
+ {
+ if (empty($this->columnWidths)) {
+ return '';
+ }
+ $xml = '';
+ foreach ($this->columnWidths as $entry) {
+ $xml .= '';
+ }
+ $xml .= '';
+ return $xml;
+ }
+
+ /**
+ * Constructs default row height and width xml to inject into worksheet xml file
+ *
+ * @return string
+ */
+ public function getXMLFragmentForDefaultCellSizing()
+ {
+ $rowHeightXml = empty($this->defaultRowHeight) ? '' : " defaultRowHeight=\"{$this->defaultRowHeight}\"";
+ $colWidthXml = empty($this->defaultColumnWidth) ? '' : " defaultColWidth=\"{$this->defaultColumnWidth}\"";
+ if (empty($colWidthXml) && empty($rowHeightXml)) {
+ return '';
+ }
+ // Ensure that the required defaultRowHeight is set
+ $rowHeightXml = empty($rowHeightXml) ? ' defaultRowHeight="0"' : $rowHeightXml;
+ return "";
+ }
+
/**
* {@inheritdoc}
*/
@@ -267,7 +356,9 @@ EOD;
return;
}
- fwrite($worksheetFilePointer, '');
+ if ($this->hasWrittenRows) {
+ fwrite($worksheetFilePointer, '');
+ }
fwrite($worksheetFilePointer, '');
fclose($worksheetFilePointer);
}
diff --git a/tests/Spout/Writer/XLSX/SheetTest.php b/tests/Spout/Writer/XLSX/SheetTest.php
index cebd72e..bf1bbb8 100644
--- a/tests/Spout/Writer/XLSX/SheetTest.php
+++ b/tests/Spout/Writer/XLSX/SheetTest.php
@@ -6,6 +6,7 @@ use Box\Spout\TestUsingResource;
use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
use Box\Spout\Writer\Common\Entity\Sheet;
use Box\Spout\Writer\Exception\InvalidSheetNameException;
+use Box\Spout\Writer\Exception\WriterNotOpenedException;
use Box\Spout\Writer\RowCreationHelper;
use PHPUnit\Framework\TestCase;
@@ -92,6 +93,124 @@ class SheetTest extends TestCase
$this->assertContains(' state="hidden"', $xmlContents, 'The sheet visibility should have been changed to "hidden"');
}
+ public function testThrowsIfWorkbookIsNotInitialized()
+ {
+ $this->expectException(WriterNotOpenedException::class);
+ $writer = WriterEntityFactory::createXLSXWriter();
+
+ $writer->addRow($this->createRowFromValues([]));
+ }
+
+ public function testThrowsWhenTryingToSetDefaultsBeforeWorkbookLoaded()
+ {
+ $this->expectException(WriterNotOpenedException::class);
+ $writer = WriterEntityFactory::createXLSXWriter();
+ $writer->setDefaultColumnWidth(10.0);
+ }
+
+ public function testWritesDefaultCellSizesIfSet()
+ {
+ $fileName = 'test_writes_default_cell_sizes_if_set.xlsx';
+ $this->createGeneratedFolderIfNeeded($fileName);
+ $resourcePath = $this->getGeneratedResourcePath($fileName);
+
+ $writer = WriterEntityFactory::createXLSXWriter();
+ $writer->openToFile($resourcePath);
+ $writer->setDefaultColumnWidth(10.0);
+ $writer->setDefaultRowHeight(10.0);
+ $writer->addRow($this->createRowFromValues(['xlsx--11', 'xlsx--12']));
+ $writer->close();
+
+ $pathToWorkbookFile = $resourcePath . '#xl/worksheets/sheet1.xml';
+ $xmlContents = file_get_contents('zip://' . $pathToWorkbookFile);
+
+ $this->assertContains('assertContains(' defaultColWidth="10', $xmlContents, 'No default column width found in sheet');
+ $this->assertContains(' defaultRowHeight="10', $xmlContents, 'No default row height found in sheet');
+ }
+
+ public function testWritesDefaultRequiredRowHeightIfOmitted()
+ {
+ $fileName = 'test_writes_default_required_row_height_if_omitted.xlsx';
+ $this->createGeneratedFolderIfNeeded($fileName);
+ $resourcePath = $this->getGeneratedResourcePath($fileName);
+
+ $writer = WriterEntityFactory::createXLSXWriter();
+ $writer->openToFile($resourcePath);
+ $writer->setDefaultColumnWidth(10.0);
+ $writer->addRow($this->createRowFromValues(['xlsx--11', 'xlsx--12']));
+ $writer->close();
+
+ $pathToWorkbookFile = $resourcePath . '#xl/worksheets/sheet1.xml';
+ $xmlContents = file_get_contents('zip://' . $pathToWorkbookFile);
+
+ $this->assertContains('assertContains(' defaultColWidth="10', $xmlContents, 'No default column width found in sheet');
+ $this->assertContains(' defaultRowHeight="0', $xmlContents, 'No default row height found in sheet');
+ }
+
+ public function testWritesColumnWidths()
+ {
+ $fileName = 'test_column_widths.xlsx';
+ $this->createGeneratedFolderIfNeeded($fileName);
+ $resourcePath = $this->getGeneratedResourcePath($fileName);
+
+ $writer = WriterEntityFactory::createXLSXWriter();
+ $writer->openToFile($resourcePath);
+ $writer->setColumnWidth(100.0, 1);
+ $writer->addRow($this->createRowFromValues(['xlsx--11', 'xlsx--12']));
+ $writer->close();
+
+ $pathToWorkbookFile = $resourcePath . '#xl/worksheets/sheet1.xml';
+ $xmlContents = file_get_contents('zip://' . $pathToWorkbookFile);
+
+ $this->assertContains('assertContains('createGeneratedFolderIfNeeded($fileName);
+ $resourcePath = $this->getGeneratedResourcePath($fileName);
+
+ $writer = WriterEntityFactory::createXLSXWriter();
+ $writer->openToFile($resourcePath);
+ $writer->setColumnWidth(100.0, 1, 2, 3);
+ $writer->addRow($this->createRowFromValues(['xlsx--11', 'xlsx--12', 'xlsx--13']));
+ $writer->close();
+
+ $pathToWorkbookFile = $resourcePath . '#xl/worksheets/sheet1.xml';
+ $xmlContents = file_get_contents('zip://' . $pathToWorkbookFile);
+
+ $this->assertContains('assertContains('createGeneratedFolderIfNeeded($fileName);
+ $resourcePath = $this->getGeneratedResourcePath($fileName);
+
+ $writer = WriterEntityFactory::createXLSXWriter();
+ $writer->openToFile($resourcePath);
+ $writer->setColumnWidth(50.0, 1, 3, 4, 6);
+ $writer->setColumnWidth(100.0, 2, 5);
+ $writer->addRow($this->createRowFromValues(['xlsx--11', 'xlsx--12', 'xlsx--13', 'xlsx--14', 'xlsx--15', 'xlsx--16']));
+ $writer->close();
+
+ $pathToWorkbookFile = $resourcePath . '#xl/worksheets/sheet1.xml';
+ $xmlContents = file_get_contents('zip://' . $pathToWorkbookFile);
+
+ $this->assertContains('assertContains('assertContains('assertContains('assertContains('assertContains('