From 298819c01d68e138bdc07b0f88a415dc2de65337 Mon Sep 17 00:00:00 2001 From: Eliurkis Diaz Date: Tue, 14 May 2019 14:57:22 -0400 Subject: [PATCH] Add the ability to add columns width (#1) --- composer.json | 29 ++++++-- src/Spout/Common/Escaper/ODS.php | 4 +- src/Spout/Common/Escaper/XLSX.php | 2 +- src/Spout/Writer/XLSX/Internal/Workbook.php | 29 ++++++-- src/Spout/Writer/XLSX/Internal/Worksheet.php | 73 +++++++++++++------- src/Spout/Writer/XLSX/Writer.php | 48 ++++++++++++- 6 files changed, 147 insertions(+), 38 deletions(-) diff --git a/composer.json b/composer.json index a15eb52..48afd5c 100644 --- a/composer.json +++ b/composer.json @@ -1,20 +1,40 @@ { - "name": "box/spout", + "name": "smart145/spout", "description": "PHP Library to read and write spreadsheet files (CSV, XLSX and ODS), in a fast and scalable way", "type": "library", - "keywords": ["php","read","write","csv","xlsx","ods","odf","open","office","excel","spreadsheet","scale","memory","stream","ooxml"], + "keywords": [ + "php", + "read", + "write", + "csv", + "xlsx", + "ods", + "odf", + "open", + "office", + "excel", + "spreadsheet", + "scale", + "memory", + "stream", + "ooxml" + ], "license": "Apache-2.0", - "homepage": "https://www.github.com/box/spout", + "homepage": "https://www.github.com/smart145/spout", "authors": [ { "name": "Adrien Loison", "email": "adrien@box.com" + }, + { + "name": "Dariel Ramos", + "email": "vdariel90@gmail.com" } ], "require": { "php": ">=5.4.0", "ext-zip": "*", - "ext-xmlreader" : "*" + "ext-xmlreader": "*" }, "require-dev": { "phpunit/phpunit": "^4.8.0" @@ -38,5 +58,4 @@ "php": "5.4.0" } } - } diff --git a/src/Spout/Common/Escaper/ODS.php b/src/Spout/Common/Escaper/ODS.php index 9de665b..4ffd766 100644 --- a/src/Spout/Common/Escaper/ODS.php +++ b/src/Spout/Common/Escaper/ODS.php @@ -26,13 +26,13 @@ class ODS implements EscaperInterface // 'ENT_DISALLOWED' ensures that invalid characters in the given document type are replaced. // Otherwise control characters like a vertical tab "\v" will make the XML document unreadable by the XML processor // @link https://github.com/box/spout/issues/329 - $replacedString = htmlspecialchars($string, ENT_NOQUOTES | ENT_DISALLOWED, 'UTF-8'); + $replacedString = htmlspecialchars($string, ENT_NOQUOTES | ENT_DISALLOWED); } else { // We are on hhvm or any other engine that does not support ENT_DISALLOWED. // // @NOTE: Using ENT_NOQUOTES as only XML entities ('<', '>', '&') need to be encoded. // Single and double quotes can be left as is. - $escapedString = htmlspecialchars($string, ENT_NOQUOTES, 'UTF-8'); + $escapedString = htmlspecialchars($string, ENT_NOQUOTES); // control characters values are from 0 to 1F (hex values) in the ASCII table // some characters should not be escaped though: "\t", "\r" and "\n". diff --git a/src/Spout/Common/Escaper/XLSX.php b/src/Spout/Common/Escaper/XLSX.php index 12b44d7..7c6c61b 100644 --- a/src/Spout/Common/Escaper/XLSX.php +++ b/src/Spout/Common/Escaper/XLSX.php @@ -44,7 +44,7 @@ class XLSX implements EscaperInterface $escapedString = $this->escapeControlCharacters($string); // @NOTE: Using ENT_NOQUOTES as only XML entities ('<', '>', '&') need to be encoded. // Single and double quotes can be left as is. - $escapedString = htmlspecialchars($escapedString, ENT_NOQUOTES, 'UTF-8'); + $escapedString = htmlspecialchars($escapedString, ENT_NOQUOTES); return $escapedString; } diff --git a/src/Spout/Writer/XLSX/Internal/Workbook.php b/src/Spout/Writer/XLSX/Internal/Workbook.php index bcdce7f..8df057b 100644 --- a/src/Spout/Writer/XLSX/Internal/Workbook.php +++ b/src/Spout/Writer/XLSX/Internal/Workbook.php @@ -3,10 +3,10 @@ namespace Box\Spout\Writer\XLSX\Internal; use Box\Spout\Writer\Common\Internal\AbstractWorkbook; +use Box\Spout\Writer\Common\Sheet; use Box\Spout\Writer\XLSX\Helper\FileSystemHelper; use Box\Spout\Writer\XLSX\Helper\SharedStringsHelper; use Box\Spout\Writer\XLSX\Helper\StyleHelper; -use Box\Spout\Writer\Common\Sheet; /** * Class Workbook @@ -35,6 +35,9 @@ class Workbook extends AbstractWorkbook /** @var \Box\Spout\Writer\XLSX\Helper\StyleHelper Helper to apply styles */ protected $styleHelper; + /** @var array contain column width information */ + protected $columnwidths = []; + /** * @param string $tempFolder * @param bool $shouldUseInlineStrings @@ -42,8 +45,13 @@ class Workbook extends AbstractWorkbook * @param \Box\Spout\Writer\Style\Style $defaultRowStyle * @throws \Box\Spout\Common\Exception\IOException If unable to create at least one of the base folders */ - public function __construct($tempFolder, $shouldUseInlineStrings, $shouldCreateNewSheetsAutomatically, $defaultRowStyle) - { + public function __construct( + $tempFolder, + $shouldUseInlineStrings, + $shouldCreateNewSheetsAutomatically, + $defaultRowStyle, + $columnwidths + ) { parent::__construct($shouldCreateNewSheetsAutomatically, $defaultRowStyle); $this->shouldUseInlineStrings = $shouldUseInlineStrings; @@ -56,6 +64,7 @@ class Workbook extends AbstractWorkbook // This helper will be shared by all sheets $xlFolder = $this->fileSystemHelper->getXlFolder(); $this->sharedStringsHelper = new SharedStringsHelper($xlFolder); + $this->columnwidths = $columnwidths; } /** @@ -86,12 +95,24 @@ class Workbook extends AbstractWorkbook $sheet = new Sheet($newSheetIndex, $this->internalId); $worksheetFilesFolder = $this->fileSystemHelper->getXlWorksheetsFolder(); - $worksheet = new Worksheet($sheet, $worksheetFilesFolder, $this->sharedStringsHelper, $this->styleHelper, $this->shouldUseInlineStrings); + $worksheet = new Worksheet($sheet, $worksheetFilesFolder, $this->sharedStringsHelper, $this->styleHelper, + $this->shouldUseInlineStrings, $this->columnwidths); $this->worksheets[] = $worksheet; return $worksheet; } + /** + * Set column width for sheet that will be created + * should only be called from the writer + * + * @param array $columnwidths + */ + public function _setColumnWidth($columnwidths) + { + $this->columnwidths = $columnwidths; + } + /** * Closes the workbook and all its associated sheets. * All the necessary files are written to disk and zipped together to create the XLSX file. diff --git a/src/Spout/Writer/XLSX/Internal/Worksheet.php b/src/Spout/Writer/XLSX/Internal/Worksheet.php index 0bd909d..392371d 100644 --- a/src/Spout/Writer/XLSX/Internal/Worksheet.php +++ b/src/Spout/Writer/XLSX/Internal/Worksheet.php @@ -65,8 +65,14 @@ EOD; * @param bool $shouldUseInlineStrings Whether inline or shared strings should be used * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing */ - public function __construct($externalSheet, $worksheetFilesFolder, $sharedStringsHelper, $styleHelper, $shouldUseInlineStrings) - { + public function __construct( + $externalSheet, + $worksheetFilesFolder, + $sharedStringsHelper, + $styleHelper, + $shouldUseInlineStrings, + $columnwidths + ) { $this->externalSheet = $externalSheet; $this->sharedStringsHelper = $sharedStringsHelper; $this->styleHelper = $styleHelper; @@ -76,8 +82,8 @@ EOD; $this->stringsEscaper = \Box\Spout\Common\Escaper\XLSX::getInstance(); $this->stringHelper = new StringHelper(); - $this->worksheetFilePath = $worksheetFilesFolder . '/' . strtolower($this->externalSheet->getName()) . '.xml'; - $this->startSheet(); + $this->worksheetFilePath = $worksheetFilesFolder.'/'.strtolower($this->externalSheet->getName()).'.xml'; + $this->startSheet($columnwidths); } /** @@ -86,12 +92,24 @@ EOD; * @return void * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing */ - protected function startSheet() + protected function startSheet($columnwidths = null) { $this->sheetFilePointer = fopen($this->worksheetFilePath, 'w'); $this->throwIfSheetFilePointerIsNotAvailable(); fwrite($this->sheetFilePointer, self::SHEET_XML_FILE_HEADER); + + if (!empty($columnwidths)) { + foreach ($columnwidths as $c) { + fwrite($this->sheetFilePointer, + '' + ); + } + } + fwrite($this->sheetFilePointer, ''); } @@ -162,6 +180,7 @@ EOD; protected function isEmptyRow($dataRow) { $numCells = count($dataRow); + // using "reset()" instead of "$dataRow[0]" because $dataRow can be an associative array return ($numCells === 1 && CellHelper::isEmpty(reset($dataRow))); } @@ -182,9 +201,9 @@ EOD; $rowIndex = $this->lastWrittenRowIndex + 1; $numCells = count($dataRow); - $rowXML = ''; + $rowXML = ''; - foreach($dataRow as $cellValue) { + foreach ($dataRow as $cellValue) { $rowXML .= $this->getCellXML($rowIndex, $cellNumber, $cellValue, $style->getId()); $cellNumber++; } @@ -210,25 +229,31 @@ EOD; protected function getCellXML($rowIndex, $cellNumber, $cellValue, $styleId) { $columnIndex = CellHelper::getCellIndexFromColumnIndex($cellNumber); - $cellXML = 'getCellXMLFragmentForNonEmptyString($cellValue); - } else if (CellHelper::isBoolean($cellValue)) { - $cellXML .= ' t="b">' . intval($cellValue) . ''; - } else if (CellHelper::isNumeric($cellValue)) { - $cellXML .= '>' . $cellValue . ''; - } else if (empty($cellValue)) { - if ($this->styleHelper->shouldApplyStyleOnEmptyCell($styleId)) { - $cellXML .= '/>'; - } else { - // don't write empty cells that do no need styling - // NOTE: not appending to $cellXML is the right behavior!! - $cellXML = ''; - } } else { - throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cellValue)); + if (CellHelper::isBoolean($cellValue)) { + $cellXML .= ' t="b">'.intval($cellValue).''; + } else { + if (CellHelper::isNumeric($cellValue)) { + $cellXML .= '>'.$cellValue.''; + } else { + if (empty($cellValue)) { + if ($this->styleHelper->shouldApplyStyleOnEmptyCell($styleId)) { + $cellXML .= '/>'; + } else { + // don't write empty cells that do no need styling + // NOTE: not appending to $cellXML is the right behavior!! + $cellXML = ''; + } + } else { + throw new InvalidArgumentException('Trying to add a value with an unsupported type: '.gettype($cellValue)); + } + } + } } return $cellXML; @@ -248,10 +273,10 @@ EOD; } if ($this->shouldUseInlineStrings) { - $cellXMLFragment = ' t="inlineStr">' . $this->stringsEscaper->escape($cellValue) . ''; + $cellXMLFragment = ' t="inlineStr">'.$this->stringsEscaper->escape($cellValue).''; } else { $sharedStringId = $this->sharedStringsHelper->writeString($cellValue); - $cellXMLFragment = ' t="s">' . $sharedStringId . ''; + $cellXMLFragment = ' t="s">'.$sharedStringId.''; } return $cellXMLFragment; diff --git a/src/Spout/Writer/XLSX/Writer.php b/src/Spout/Writer/XLSX/Writer.php index 965955a..b1deaba 100644 --- a/src/Spout/Writer/XLSX/Writer.php +++ b/src/Spout/Writer/XLSX/Writer.php @@ -30,6 +30,9 @@ class Writer extends AbstractMultiSheetsWriter /** @var Internal\Workbook The workbook for the XLSX file */ protected $book; + /** @var array contain column width information */ + protected $columnwidths = []; + /** * Sets a custom temporary folder for creating intermediate files/folders. * This must be set before opening the writer. @@ -44,6 +47,7 @@ class Writer extends AbstractMultiSheetsWriter $this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.'); $this->tempFolder = $tempFolder; + return $this; } @@ -61,6 +65,45 @@ class Writer extends AbstractMultiSheetsWriter $this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.'); $this->shouldUseInlineStrings = $shouldUseInlineStrings; + + return $this; + } + + /** + * Clear all column width specification + * @return Writer + */ + public function clearColumnWidth() + { + $this->columnwidths = []; + if ($this->book) { + $this->book->_setColumnWidth($this->columnwidths); + } + + return $this; + } + + /** + * Add a width definition for the next sheet that will be generated + * @param number $width column width + * @param number $min column position ( A=1 ) where this width should take effect + * @param number $max end of range where width take effect ( default to min ) + * @return Writer + */ + public function setColumnsWidth($width, $min, $max = null) + { + if ($max === null) { + $max = $min; + } + $this->columnwidths[] = [ + 'width' => $width, + 'min' => $min, + 'max' => $max, + ]; + if ($this->book) { + $this->book->_setColumnWidth($this->columnwidths); + } + return $this; } @@ -73,8 +116,9 @@ class Writer extends AbstractMultiSheetsWriter protected function openWriter() { if (!$this->book) { - $tempFolder = ($this->tempFolder) ? : sys_get_temp_dir(); - $this->book = new Workbook($tempFolder, $this->shouldUseInlineStrings, $this->shouldCreateNewSheetsAutomatically, $this->defaultRowStyle); + $tempFolder = ($this->tempFolder) ?: sys_get_temp_dir(); + $this->book = new Workbook($tempFolder, $this->shouldUseInlineStrings, + $this->shouldCreateNewSheetsAutomatically, $this->defaultRowStyle, $this->columnwidths); $this->book->addNewSheetAndMakeItCurrent(); } }