diff --git a/.gitignore b/.gitignore
index 62536649b..a4bddd509 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
/.idea
*.iml
-/tests/resources/generated
+/tests/**/resources/generated
/tests/coverage
/vendor
/composer.lock
diff --git a/src/Spout/Writer/Common/Entity/Options.php b/src/Spout/Writer/Common/Entity/Options.php
index d7152bb6c..391833345 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/Entity/Worksheet.php b/src/Spout/Writer/Common/Entity/Worksheet.php
index 74c4976f0..c7a098706 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 has the sheet data header been written */
+ private $sheetDataStarted = false;
+
/**
* Worksheet constructor.
*
@@ -36,6 +39,7 @@ public function __construct($worksheetFilePath, Sheet $externalSheet)
$this->externalSheet = $externalSheet;
$this->maxNumColumns = 0;
$this->lastWrittenRowIndex = 0;
+ $this->sheetDataStarted = false;
}
/**
@@ -110,4 +114,20 @@ public function getId()
// sheet index is zero-based, while ID is 1-based
return $this->externalSheet->getIndex() + 1;
}
+
+ /**
+ * @return bool
+ */
+ public function getSheetDataStarted()
+ {
+ return $this->sheetDataStarted;
+ }
+
+ /**
+ * @param bool $sheetDataStarted
+ */
+ public function setSheetDataStarted($sheetDataStarted)
+ {
+ $this->sheetDataStarted = $sheetDataStarted;
+ }
}
diff --git a/src/Spout/Writer/Common/Manager/ManagesCellSize.php b/src/Spout/Writer/Common/Manager/ManagesCellSize.php
new file mode 100644
index 000000000..8a517f0d9
--- /dev/null
+++ b/src/Spout/Writer/Common/Manager/ManagesCellSize.php
@@ -0,0 +1,63 @@
+defaultColumnWidth = $width;
+ }
+
+ /**
+ * @param float|null $height
+ */
+ public function setDefaultRowHeight($height)
+ {
+ $this->defaultRowHeight = $height;
+ }
+
+ /**
+ * @param float $width
+ * @param array $columns One or more columns with this width
+ */
+ public function setColumnWidth(float $width, ...$columns)
+ {
+ // Gather sequences
+ $sequence = [];
+ foreach ($columns as $i) {
+ $sequenceLength = count($sequence);
+ if ($sequenceLength > 0) {
+ $previousValue = $sequence[$sequenceLength - 1];
+ if ($i !== $previousValue + 1) {
+ $this->setColumnWidthForRange($width, $sequence[0], $previousValue);
+ $sequence = [];
+ }
+ }
+ $sequence[] = $i;
+ }
+ $this->setColumnWidthForRange($width, $sequence[0], $sequence[count($sequence) - 1]);
+ }
+
+ /**
+ * @param float $width The width to set
+ * @param int $start First column index of the range
+ * @param int $end Last column index of the range
+ */
+ public function setColumnWidthForRange(float $width, int $start, int $end)
+ {
+ $this->columnWidths[] = [$start, $end, $width];
+ }
+}
diff --git a/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php b/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php
index 653778c70..604de65ce 100644
--- a/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php
+++ b/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php
@@ -15,7 +15,6 @@
use Box\Spout\Writer\Common\Manager\Style\StyleManagerInterface;
use Box\Spout\Writer\Common\Manager\Style\StyleMerger;
use Box\Spout\Writer\Exception\SheetNotFoundException;
-use Box\Spout\Writer\Exception\WriterException;
/**
* Class WorkbookManagerAbstract
@@ -103,7 +102,6 @@ public function getWorkbook()
* 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,7 +115,7 @@ public function addNewSheetAndMakeItCurrent()
/**
* 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
+ * @throws IOException
* @return Worksheet The created sheet
*/
private function addNewSheet()
@@ -157,6 +155,16 @@ public function getCurrentWorksheet()
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,8 +218,9 @@ private function getWorksheetFromExternalSheet($sheet)
* 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
+ * @throws \Box\Spout\Common\Exception\InvalidArgumentException
* @return void
*/
public function addRowToCurrentWorksheet(Row $row)
@@ -249,7 +258,9 @@ private function hasCurrentWorksheetReachedMaxRows()
*
* @param Worksheet $worksheet Worksheet to write the row to
* @param Row $row The row to be added
- * @throws WriterException If unable to write data
+ *
+ * @throws IOException
+ * @throws \Box\Spout\Common\Exception\InvalidArgumentException
* @return void
*/
private function addRowToWorksheet(Worksheet $worksheet, Row $row)
@@ -276,6 +287,41 @@ private function applyDefaultRowStyle(Row $row)
}
}
+ /**
+ * @param float $width
+ */
+ public function setDefaultColumnWidth(float $width)
+ {
+ $this->worksheetManager->setDefaultColumnWidth($width);
+ }
+
+ /**
+ * @param float $height
+ */
+ public function setDefaultRowHeight(float $height)
+ {
+ $this->worksheetManager->setDefaultRowHeight($height);
+ }
+
+ /**
+ * @param float $width
+ * @param array $columns One or more columns with this width
+ */
+ public function setColumnWidth(float $width, ...$columns)
+ {
+ $this->worksheetManager->setColumnWidth($width, ...$columns);
+ }
+
+ /**
+ * @param float $width The width to set
+ * @param int $start First column index of the range
+ * @param int $end Last column index of the range
+ */
+ public function setColumnWidthForRange(float $width, int $start, int $end)
+ {
+ $this->worksheetManager->setColumnWidthForRange($width, $start, $end);
+ }
+
/**
* 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 aed304a02..7bb469eaf 100644
--- a/src/Spout/Writer/Common/Manager/WorkbookManagerInterface.php
+++ b/src/Spout/Writer/Common/Manager/WorkbookManagerInterface.php
@@ -42,6 +42,21 @@ public function getWorksheets();
*/
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/Common/Manager/WorksheetManagerInterface.php b/src/Spout/Writer/Common/Manager/WorksheetManagerInterface.php
index bb54ee643..bb6758a7f 100644
--- a/src/Spout/Writer/Common/Manager/WorksheetManagerInterface.php
+++ b/src/Spout/Writer/Common/Manager/WorksheetManagerInterface.php
@@ -11,6 +11,29 @@
*/
interface WorksheetManagerInterface
{
+ /**
+ * @param float|null $width
+ */
+ public function setDefaultColumnWidth($width);
+
+ /**
+ * @param float|null $height
+ */
+ public function setDefaultRowHeight($height);
+
+ /**
+ * @param float $width
+ * @param array $columns One or more columns with this width
+ */
+ public function setColumnWidth(float $width, ...$columns);
+
+ /**
+ * @param float $width The width to set
+ * @param int $start First column index of the range
+ * @param int $end Last column index of the range
+ */
+ public function setColumnWidthForRange(float $width, int $start, int $end);
+
/**
* Adds a row to the worksheet.
*
diff --git a/src/Spout/Writer/ODS/Creator/ManagerFactory.php b/src/Spout/Writer/ODS/Creator/ManagerFactory.php
index a5b77ee42..3605a37d7 100644
--- a/src/Spout/Writer/ODS/Creator/ManagerFactory.php
+++ b/src/Spout/Writer/ODS/Creator/ManagerFactory.php
@@ -93,7 +93,7 @@ private function createStyleManager(OptionsManagerInterface $optionsManager)
{
$styleRegistry = $this->createStyleRegistry($optionsManager);
- return new StyleManager($styleRegistry);
+ return new StyleManager($styleRegistry, $optionsManager);
}
/**
diff --git a/src/Spout/Writer/ODS/Manager/OptionsManager.php b/src/Spout/Writer/ODS/Manager/OptionsManager.php
index a6fb564ef..698108a9c 100644
--- a/src/Spout/Writer/ODS/Manager/OptionsManager.php
+++ b/src/Spout/Writer/ODS/Manager/OptionsManager.php
@@ -34,6 +34,9 @@ protected function getSupportedOptions()
Options::TEMP_FOLDER,
Options::DEFAULT_ROW_STYLE,
Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY,
+ Options::DEFAULT_COLUMN_WIDTH,
+ Options::DEFAULT_ROW_HEIGHT,
+ Options::COLUMN_WIDTHS,
];
}
diff --git a/src/Spout/Writer/ODS/Manager/Style/StyleManager.php b/src/Spout/Writer/ODS/Manager/Style/StyleManager.php
index 34f75c78d..93ed008ec 100644
--- a/src/Spout/Writer/ODS/Manager/Style/StyleManager.php
+++ b/src/Spout/Writer/ODS/Manager/Style/StyleManager.php
@@ -4,7 +4,10 @@
use Box\Spout\Common\Entity\Style\BorderPart;
use Box\Spout\Common\Entity\Style\CellAlignment;
+use Box\Spout\Common\Manager\OptionsManagerInterface;
+use Box\Spout\Writer\Common\Entity\Options;
use Box\Spout\Writer\Common\Entity\Worksheet;
+use Box\Spout\Writer\Common\Manager\ManagesCellSize;
use Box\Spout\Writer\ODS\Helper\BorderHelper;
/**
@@ -13,9 +16,22 @@
*/
class StyleManager extends \Box\Spout\Writer\Common\Manager\Style\StyleManager
{
+ use ManagesCellSize;
+
/** @var StyleRegistry */
protected $styleRegistry;
+ /**
+ * @param StyleRegistry $styleRegistry
+ */
+ public function __construct(StyleRegistry $styleRegistry, OptionsManagerInterface $optionsManager)
+ {
+ parent::__construct($styleRegistry);
+ $this->setDefaultColumnWidth($optionsManager->getOption(Options::DEFAULT_COLUMN_WIDTH));
+ $this->setDefaultRowHeight($optionsManager->getOption(Options::DEFAULT_ROW_HEIGHT));
+ $this->columnWidths = $optionsManager->getOption(Options::COLUMN_WIDTHS) ?? [];
+ }
+
/**
* Returns the content of the "styles.xml" file, given a list of styles.
*
@@ -162,12 +178,16 @@ public function getContentXmlAutomaticStylesSectionContent($worksheets)
$content .= $this->getStyleSectionContent($style);
}
- $content .= <<<'EOD'
-
-
+ $useOptimalRowHeight = empty($this->defaultRowHeight) ? 'true' : 'false';
+ $defaultRowHeight = empty($this->defaultRowHeight) ? '15pt' : "{$this->defaultRowHeight}pt";
+ $defaultColumnWidth = empty($this->defaultColumnWidth) ? '' : "style:column-width=\"{$this->defaultColumnWidth}pt\"";
+
+ $content .= <<
+
-
+
EOD;
@@ -182,6 +202,16 @@ public function getContentXmlAutomaticStylesSectionContent($worksheets)
EOD;
}
+ // Sort column widths since ODS cares about order
+ usort($this->columnWidths, function ($a, $b) {
+ if ($a[0] === $b[0]) {
+ return 0;
+ }
+
+ return ($a[0] < $b[0]) ? -1 : 1;
+ });
+ $content .= $this->getTableColumnStylesXMLContent();
+
$content .= '';
return $content;
@@ -313,9 +343,12 @@ private function getCellAlignmentSectionContent($style)
private function transformCellAlignment($cellAlignment)
{
switch ($cellAlignment) {
- case CellAlignment::LEFT: return 'start';
- case CellAlignment::RIGHT: return 'end';
- default: return $cellAlignment;
+ case CellAlignment::LEFT:
+ return 'start';
+ case CellAlignment::RIGHT:
+ return 'end';
+ default:
+ return $cellAlignment;
}
}
@@ -381,4 +414,42 @@ private function getBackgroundColorXMLContent($style)
{
return \sprintf(' fo:background-color="#%s" ', $style->getBackgroundColor());
}
+
+ public function getTableColumnStylesXMLContent() : string
+ {
+ if (empty($this->columnWidths)) {
+ return '';
+ }
+
+ $content = '';
+ foreach ($this->columnWidths as $styleIndex => $entry) {
+ $content .= <<
+
+
+EOD;
+ }
+
+ return $content;
+ }
+
+ public function getStyledTableColumnXMLContent(int $maxNumColumns) : string
+ {
+ if (empty($this->columnWidths)) {
+ return '';
+ }
+
+ $content = '';
+ foreach ($this->columnWidths as $styleIndex => $entry) {
+ $numCols = $entry[1] - $entry[0] + 1;
+ $content .= <<
+EOD;
+ }
+ // Note: This assumes the column widths are contiguous and default width is
+ // only applied to columns after the last custom column with a custom width
+ $content .= '';
+
+ return $content;
+ }
}
diff --git a/src/Spout/Writer/ODS/Manager/WorksheetManager.php b/src/Spout/Writer/ODS/Manager/WorksheetManager.php
index 7d7cb0ebb..64f6e6e60 100644
--- a/src/Spout/Writer/ODS/Manager/WorksheetManager.php
+++ b/src/Spout/Writer/ODS/Manager/WorksheetManager.php
@@ -95,7 +95,7 @@ public function getTableElementStartAsString(Worksheet $worksheet)
$tableStyleName = 'ta' . ($externalSheet->getIndex() + 1);
$tableElement = '';
- $tableElement .= '';
+ $tableElement .= $this->styleManager->getStyledTableColumnXMLContent($worksheet->getMaxNumColumns());
return $tableElement;
}
@@ -265,4 +265,39 @@ public function close(Worksheet $worksheet)
\fclose($worksheetFilePointer);
}
+
+ /**
+ * @param float|null $width
+ */
+ public function setDefaultColumnWidth($width)
+ {
+ $this->styleManager->setDefaultColumnWidth($width);
+ }
+
+ /**
+ * @param float|null $height
+ */
+ public function setDefaultRowHeight($height)
+ {
+ $this->styleManager->setDefaultRowHeight($height);
+ }
+
+ /**
+ * @param float $width
+ * @param array $columns One or more columns with this width
+ */
+ public function setColumnWidth(float $width, ...$columns)
+ {
+ $this->styleManager->setColumnWidth($width, ...$columns);
+ }
+
+ /**
+ * @param float $width The width to set
+ * @param int $start First column index of the range
+ * @param int $end Last column index of the range
+ */
+ public function setColumnWidthForRange(float $width, int $start, int $end)
+ {
+ $this->styleManager->setColumnWidthForRange($width, $start, $end);
+ }
}
diff --git a/src/Spout/Writer/WriterMultiSheetsAbstract.php b/src/Spout/Writer/WriterMultiSheetsAbstract.php
index 8170b679c..0161877e0 100644
--- a/src/Spout/Writer/WriterMultiSheetsAbstract.php
+++ b/src/Spout/Writer/WriterMultiSheetsAbstract.php
@@ -4,6 +4,7 @@
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;
@@ -56,7 +57,10 @@ public function setShouldCreateNewSheetsAutomatically($shouldCreateNewSheetsAuto
{
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
- $this->optionsManager->setOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, $shouldCreateNewSheetsAutomatically);
+ $this->optionsManager->setOption(
+ Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY,
+ $shouldCreateNewSheetsAutomatically
+ );
return $this;
}
@@ -96,6 +100,7 @@ public function getSheets()
/**
* Creates a new sheet and make it the current sheet. The data will now be written to this sheet.
*
+ * @throws IOException
* @throws WriterNotOpenedException If the writer has not been opened yet
* @return Sheet The created sheet
*/
@@ -125,8 +130,8 @@ public function getCurrentSheet()
* The writing will resume where it stopped (i.e. data won't be truncated).
*
* @param Sheet $sheet The sheet to set as current
- * @throws WriterNotOpenedException If the writer has not been opened yet
* @throws SheetNotFoundException If the given sheet does not exist in the workbook
+ * @throws WriterNotOpenedException If the writer has not been opened yet
* @return void
*/
public function setCurrentSheet($sheet)
@@ -135,6 +140,55 @@ public function setCurrentSheet($sheet)
$this->workbookManager->setCurrentSheet($sheet);
}
+ /**
+ * @param float $width
+ * @throws WriterAlreadyOpenedException
+ */
+ public function setDefaultColumnWidth(float $width)
+ {
+ $this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
+ $this->optionsManager->setOption(
+ Options::DEFAULT_COLUMN_WIDTH,
+ $width
+ );
+ }
+
+ /**
+ * @param float $height
+ * @throws WriterAlreadyOpenedException
+ */
+ public function setDefaultRowHeight(float $height)
+ {
+ $this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
+ $this->optionsManager->setOption(
+ Options::DEFAULT_ROW_HEIGHT,
+ $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);
+ }
+
+ /**
+ * @param float $width The width to set
+ * @param int $start First column index of the range
+ * @param int $end Last column index of the range
+ * @throws WriterNotOpenedException
+ */
+ public function setColumnWidthForRange(float $width, int $start, int $end)
+ {
+ $this->throwIfWorkbookIsNotAvailable();
+ $this->workbookManager->setColumnWidthForRange($width, $start, $end);
+ }
+
/**
* Checks if the workbook has been created. Throws an exception if not created yet.
*
@@ -143,13 +197,15 @@ public function setCurrentSheet($sheet)
*/
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 d3b5cd423..e6adedb8b 100644
--- a/src/Spout/Writer/XLSX/Manager/OptionsManager.php
+++ b/src/Spout/Writer/XLSX/Manager/OptionsManager.php
@@ -39,6 +39,9 @@ protected function getSupportedOptions()
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 61b93a176..6452cccb8 100644
--- a/src/Spout/Writer/XLSX/Manager/WorksheetManager.php
+++ b/src/Spout/Writer/XLSX/Manager/WorksheetManager.php
@@ -15,6 +15,7 @@
use Box\Spout\Writer\Common\Entity\Worksheet;
use Box\Spout\Writer\Common\Helper\CellHelper;
use Box\Spout\Writer\Common\Manager\RegisteredStyle;
+use Box\Spout\Writer\Common\Manager\ManagesCellSize;
use Box\Spout\Writer\Common\Manager\RowManager;
use Box\Spout\Writer\Common\Manager\Style\StyleMerger;
use Box\Spout\Writer\Common\Manager\WorksheetManagerInterface;
@@ -26,6 +27,8 @@
*/
class WorksheetManager implements WorksheetManagerInterface
{
+ use ManagesCellSize;
+
/**
* Maximum number of characters a cell can contain
* @see https://support.office.com/en-us/article/Excel-specifications-and-limits-16c69c74-3d6a-4aaf-ba35-e6eb276e8eaa [Excel 2007]
@@ -86,6 +89,9 @@ public function __construct(
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;
@@ -114,7 +120,23 @@ public function startSheet(Worksheet $worksheet)
$worksheet->setFilePointer($sheetFilePointer);
\fwrite($sheetFilePointer, self::SHEET_XML_FILE_HEADER);
- \fwrite($sheetFilePointer, '');
+ }
+
+ /**
+ * Writes the sheet data header
+ *
+ * @param Worksheet $worksheet The worksheet to add the row to
+ * @return void
+ */
+ private function ensureSheetDataStated(Worksheet $worksheet)
+ {
+ if (!$worksheet->getSheetDataStarted()) {
+ $worksheetFilePointer = $worksheet->getFilePointer();
+ \fwrite($worksheetFilePointer, $this->getXMLFragmentForDefaultCellSizing());
+ \fwrite($worksheetFilePointer, $this->getXMLFragmentForColumnWidths());
+ \fwrite($worksheetFilePointer, '');
+ $worksheet->setSheetDataStarted(true);
+ }
}
/**
@@ -148,17 +170,20 @@ public function addRow(Worksheet $worksheet, Row $row)
*
* @param Worksheet $worksheet The worksheet to add the row to
* @param Row $row The row to be written
- * @throws IOException If the data cannot be written
* @throws InvalidArgumentException If a cell value's type is not supported
+ * @throws IOException If the data cannot be written
* @return void
*/
private function addNonEmptyRow(Worksheet $worksheet, Row $row)
{
+ $this->ensureSheetDataStated($worksheet);
+ $sheetFilePointer = $worksheet->getFilePointer();
$rowStyle = $row->getStyle();
$rowIndexOneBased = $worksheet->getLastWrittenRowIndex() + 1;
$numCells = $row->getNumCells();
- $rowXML = '';
+ $hasCustomHeight = $this->defaultRowHeight > 0 ? '1' : '0';
+ $rowXML = "";
foreach ($row->getCells() as $columnIndexZeroBased => $cell) {
$registeredStyle = $this->applyStyleAndRegister($cell, $rowStyle);
@@ -171,7 +196,7 @@ private function addNonEmptyRow(Worksheet $worksheet, Row $row)
$rowXML .= '
';
- $wasWriteSuccessful = \fwrite($worksheet->getFilePointer(), $rowXML);
+ $wasWriteSuccessful = \fwrite($sheetFilePointer, $rowXML);
if ($wasWriteSuccessful === false) {
throw new IOException("Unable to write data in {$worksheet->getFilePath()}");
}
@@ -180,7 +205,7 @@ private function addNonEmptyRow(Worksheet $worksheet, Row $row)
/**
* Applies styles to the given style, merging the cell's style with its row's style
*
- * @param Cell $cell
+ * @param Cell $cell
* @param Style $rowStyle
*
* @throws InvalidArgumentException If the given value cannot be processed
@@ -221,10 +246,10 @@ private function applyStyleAndRegister(Cell $cell, Style $rowStyle) : Registered
/**
* Builds and returns xml for a single cell.
*
- * @param int $rowIndexOneBased
- * @param int $columnIndexZeroBased
+ * @param int $rowIndexOneBased
+ * @param int $columnIndexZeroBased
* @param Cell $cell
- * @param int $styleId
+ * @param int $styleId
*
* @throws InvalidArgumentException If the given value cannot be processed
* @return string
@@ -282,6 +307,43 @@ private function getCellXMLFragmentForNonEmptyString($cellValue)
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}
*/
@@ -292,7 +354,7 @@ public function close(Worksheet $worksheet)
if (!\is_resource($worksheetFilePointer)) {
return;
}
-
+ $this->ensureSheetDataStated($worksheet);
\fwrite($worksheetFilePointer, '
');
\fwrite($worksheetFilePointer, '');
\fclose($worksheetFilePointer);
diff --git a/tests/Spout/Writer/ODS/SheetTest.php b/tests/Spout/Writer/ODS/SheetTest.php
index 3bd219366..f94f63c5a 100644
--- a/tests/Spout/Writer/ODS/SheetTest.php
+++ b/tests/Spout/Writer/ODS/SheetTest.php
@@ -6,6 +6,7 @@
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;
@@ -82,7 +83,7 @@ public function testSetSheetNameShouldThrowWhenNameIsAlreadyUsed()
*/
public function testSetSheetVisibilityShouldCreateSheetHidden()
{
- $fileName = 'test_set_visibility_should_create_sheet_hidden.xlsx';
+ $fileName = 'test_set_visibility_should_create_sheet_hidden.ods';
$this->writeDataToHiddenSheet($fileName);
$resourcePath = $this->getGeneratedResourcePath($fileName);
@@ -92,12 +93,120 @@ public function testSetSheetVisibilityShouldCreateSheetHidden()
$this->assertStringContainsString(' table:display="false"', $xmlContents, 'The sheet visibility should have been changed to "hidden"');
}
- /**
- * @param string $fileName
- * @param string $sheetName
- * @return Sheet
- */
- private function writeDataAndReturnSheetWithCustomName($fileName, $sheetName)
+ public function testThrowsIfWorkbookIsNotInitialized()
+ {
+ $this->expectException(WriterNotOpenedException::class);
+ $writer = WriterEntityFactory::createODSWriter();
+
+ $writer->addRow($this->createRowFromValues([]));
+ }
+
+ public function testWritesDefaultCellSizesIfSet()
+ {
+ $fileName = 'test_writes_default_cell_sizes_if_set.ods';
+
+ $this->createGeneratedFolderIfNeeded($fileName);
+ $resourcePath = $this->getGeneratedResourcePath($fileName);
+
+ $writer = WriterEntityFactory::createODSWriter();
+ $writer->setDefaultColumnWidth(100.0);
+ $writer->setDefaultRowHeight(20.0);
+ $writer->openToFile($resourcePath);
+
+ $writer->addRow($this->createRowFromValues(['ods--11', 'ods--12']));
+ $writer->close();
+
+ $resourcePath = $this->getGeneratedResourcePath($fileName);
+ $pathToWorkbookFile = $resourcePath . '#content.xml';
+ $xmlContents = file_get_contents('zip://' . $pathToWorkbookFile);
+
+ $this->assertContains(' style:column-width="100pt"', $xmlContents, 'No default col width found in sheet');
+ $this->assertContains(' style:row-height="20pt"', $xmlContents, 'No default row height found in sheet');
+ $this->assertContains(' style:use-optimal-row-height="false', $xmlContents, 'No optimal row height override found in sheet');
+ }
+
+ public function testWritesColumnWidths()
+ {
+ $fileName = 'test_column_widths.ods';
+ $writer = $this->writerForFile($fileName);
+
+ $writer->setColumnWidth(100.0, 1);
+ $writer->addRow($this->createRowFromValues(['ods--11', 'ods--12']));
+ $writer->close();
+
+ $resourcePath = $this->getGeneratedResourcePath($fileName);
+ $pathToWorkbookFile = $resourcePath . '#content.xml';
+ $xmlContents = file_get_contents('zip://' . $pathToWorkbookFile);
+
+ $this->assertContains('', $xmlContents, 'No matching custom col style definition found in sheet');
+ $this->assertContains('', $xmlContents, 'No matching table-column-properties found in sheet');
+ $this->assertContains('table:style-name="co0"', $xmlContents, 'No matching table:style-name found in sheet');
+ $this->assertContains('table:number-columns-repeated="1"', $xmlContents, 'No matching table:number-columns-repeated count found in sheet');
+ }
+
+ public function testWritesMultipleColumnWidths()
+ {
+ $fileName = 'test_multiple_column_widths.ods';
+ $writer = $this->writerForFile($fileName);
+
+ $writer->setColumnWidth(100.0, 1, 2, 3);
+ $writer->addRow($this->createRowFromValues(['ods--11', 'ods--12', 'ods--13']));
+ $writer->close();
+
+ $resourcePath = $this->getGeneratedResourcePath($fileName);
+ $pathToWorkbookFile = $resourcePath . '#content.xml';
+ $xmlContents = file_get_contents('zip://' . $pathToWorkbookFile);
+
+ $this->assertContains('', $xmlContents, 'No matching custom col style definition found in sheet');
+ $this->assertContains('', $xmlContents, 'No matching table-column-properties found in sheet');
+ $this->assertContains('table:style-name="co0"', $xmlContents, 'No matching table:style-name found in sheet');
+ $this->assertContains('table:number-columns-repeated="3"', $xmlContents, 'No matching table:number-columns-repeated count found in sheet');
+ }
+
+ public function testWritesMultipleColumnWidthsInRanges()
+ {
+ $fileName = 'test_multiple_column_widths_in_ranges.ods';
+ $writer = $this->writerForFile($fileName);
+
+ $writer->setColumnWidth(50.0, 1, 3, 4, 6);
+ $writer->setColumnWidth(100.0, 2, 5);
+ $writer->addRow($this->createRowFromValues(['ods--11', 'ods--12', 'ods--13', 'ods--14', 'ods--15', 'ods--16']));
+ $writer->close();
+
+ $resourcePath = $this->getGeneratedResourcePath($fileName);
+ $pathToWorkbookFile = $resourcePath . '#content.xml';
+ $xmlContents = file_get_contents('zip://' . $pathToWorkbookFile);
+
+ $this->assertContains('', $xmlContents, 'No matching custom col style 0 definition found in sheet');
+ $this->assertContains('', $xmlContents, 'No matching custom col style 1 definition found in sheet');
+ $this->assertContains('', $xmlContents, 'No matching custom col style 2 definition found in sheet');
+ $this->assertContains('', $xmlContents, 'No matching custom col style 3 definition found in sheet');
+ $this->assertContains('', $xmlContents, 'No matching custom col style 4 definition found in sheet');
+ $this->assertContains('', $xmlContents, 'No matching table-column-properties found in sheet');
+ $this->assertContains('', $xmlContents, 'No matching table-column-properties found in sheet');
+ $this->assertContains('', $xmlContents, 'No matching table:number-columns-repeated count found in sheet');
+ }
+
+ public function testCanTakeColumnWidthsAsRange()
+ {
+ $fileName = 'test_column_widths_as_ranges.ods';
+ $writer = $this->writerForFile($fileName);
+
+ $writer->setColumnWidthForRange(150.0, 1, 3);
+ $writer->addRow($this->createRowFromValues(['ods--11', 'ods--12', 'ods--13']));
+ $writer->close();
+
+ $resourcePath = $this->getGeneratedResourcePath($fileName);
+ $pathToWorkbookFile = $resourcePath . '#content.xml';
+ $xmlContents = file_get_contents('zip://' . $pathToWorkbookFile);
+
+ $this->assertContains('', $xmlContents, 'No matching custom col style 0 definition found in sheet');
+ $this->assertContains('style:column-width="150pt"/>', $xmlContents, 'No matching table-column-properties found in sheet');
+ $this->assertContains('table:style-name="co0"', $xmlContents, 'No matching table:style-name found in sheet');
+ $this->assertContains('table:number-columns-repeated="3"', $xmlContents, 'No matching table:number-columns-repeated count found in sheet');
+ }
+
+ private function writerForFile($fileName)
{
$this->createGeneratedFolderIfNeeded($fileName);
$resourcePath = $this->getGeneratedResourcePath($fileName);
@@ -105,6 +214,18 @@ private function writeDataAndReturnSheetWithCustomName($fileName, $sheetName)
$writer = WriterEntityFactory::createODSWriter();
$writer->openToFile($resourcePath);
+ return $writer;
+ }
+
+ /**
+ * @param string $fileName
+ * @param string $sheetName
+ * @return void
+ */
+ private function writeDataAndReturnSheetWithCustomName($fileName, $sheetName)
+ {
+ $writer = $this->writerForFile($fileName);
+
$sheet = $writer->getCurrentSheet();
$sheet->setName($sheetName);
diff --git a/tests/Spout/Writer/XLSX/SheetTest.php b/tests/Spout/Writer/XLSX/SheetTest.php
index a2431619c..79033c208 100644
--- a/tests/Spout/Writer/XLSX/SheetTest.php
+++ b/tests/Spout/Writer/XLSX/SheetTest.php
@@ -6,6 +6,7 @@
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,138 @@ public function testSetSheetVisibilityShouldCreateSheetHidden()
$this->assertStringContainsString(' 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 testWritesDefaultCellSizesIfSet()
+ {
+ $fileName = 'test_writes_default_cell_sizes_if_set.xlsx';
+ $this->createGeneratedFolderIfNeeded($fileName);
+ $resourcePath = $this->getGeneratedResourcePath($fileName);
+
+ $writer = WriterEntityFactory::createXLSXWriter();
+ $writer->setDefaultColumnWidth(10.0);
+ $writer->setDefaultRowHeight(20.0);
+ $writer->openToFile($resourcePath);
+ $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="20', $xmlContents, 'No default row height found in sheet');
+ $this->assertContains(' customHeight="1"', $xmlContents, 'No row height override flag found in row');
+ }
+
+ public function testWritesDefaultRequiredRowHeightIfOmitted()
+ {
+ $fileName = 'test_writes_default_required_row_height_if_omitted.xlsx';
+ $this->createGeneratedFolderIfNeeded($fileName);
+ $resourcePath = $this->getGeneratedResourcePath($fileName);
+
+ $writer = WriterEntityFactory::createXLSXWriter();
+ $writer->setDefaultColumnWidth(10.0);
+ $writer->openToFile($resourcePath);
+
+ $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('createGeneratedFolderIfNeeded($fileName);
+ $resourcePath = $this->getGeneratedResourcePath($fileName);
+
+ $writer = WriterEntityFactory::createXLSXWriter();
+ $writer->openToFile($resourcePath);
+ $writer->setColumnWidthForRange(50.0, 1, 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('