From c8ddcf5441a6a7ac6798a43a30247d4d74d5e2bc Mon Sep 17 00:00:00 2001 From: Adrien Loison Date: Thu, 13 Aug 2015 23:03:26 -0700 Subject: [PATCH] Set wrap text style when multiline string encountered Fixes #10 If a cell contains a multiline string, "wrap text" style option should automatically be set. --- README.md | 2 +- src/Spout/Common/Escaper/XLSX.php | 3 ++ src/Spout/Writer/XLSX/Helper/StyleHelper.php | 45 +++++++++++++++++++ src/Spout/Writer/XLSX/Internal/Workbook.php | 11 ++--- .../Writer/XLSX/Helper/StyleHelperTest.php | 30 +++++++++++++ .../Spout/Writer/XLSX/WriterWithStyleTest.php | 19 ++++++++ 6 files changed, 102 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5887e70..d9ea18e 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ $style = (new StyleBuilder()) ->setFontSize(15) ->setShouldWrapText() ->build(); - + $writer = WriterFactory::create(Type::XLSX); $writer->openToFile($filePath); diff --git a/src/Spout/Common/Escaper/XLSX.php b/src/Spout/Common/Escaper/XLSX.php index 7bdac43..6f5bd1f 100644 --- a/src/Spout/Common/Escaper/XLSX.php +++ b/src/Spout/Common/Escaper/XLSX.php @@ -13,6 +13,9 @@ class XLSX implements EscaperInterface /** @var string[] Control characters to be escaped */ protected $controlCharactersEscapingMap; + /** + * + */ public function __construct() { $this->controlCharactersEscapingMap = $this->getControlCharactersEscapingMap(); diff --git a/src/Spout/Writer/XLSX/Helper/StyleHelper.php b/src/Spout/Writer/XLSX/Helper/StyleHelper.php index 55af351..e986683 100644 --- a/src/Spout/Writer/XLSX/Helper/StyleHelper.php +++ b/src/Spout/Writer/XLSX/Helper/StyleHelper.php @@ -71,6 +71,51 @@ class StyleHelper return $this->styleIdToStyleMappingTable[$styleId]; } + /** + * Apply additional styles if the given row needs it. + * Typically, set "wrap text" if a cell contains a new line. + * + * @param \Box\Spout\Writer\Style\Style $style The original style + * @param array $dataRow The row the style will be applied to + * @return \Box\Spout\Writer\Style\Style The updated style + */ + public function applyExtraStylesIfNeeded($style, $dataRow) + { + $updatedStyle = $this->applyWrapTextIfCellContainsNewLine($style, $dataRow); + + return $updatedStyle; + } + + /** + * Set the "wrap text" option if a cell of the given row contains a new line. + * + * @NOTE: There is a bug on the Mac version of Excel (2011 and below) where new lines + * are ignored even when the "wrap text" option is set. This only occurs with + * inline strings (shared strings do work fine). + * A workaround would be to encode "\n" as "_x000D_" but it does not work + * on the Windows version of Excel... + * + * @param \Box\Spout\Writer\Style\Style $style The original style + * @param array $dataRow The row the style will be applied to + * @return \Box\Spout\Writer\Style\Style The eventually updated style + */ + protected function applyWrapTextIfCellContainsNewLine($style, $dataRow) + { + // if the "wrap text" option is already set, no-op + if ($style->shouldWrapText()) { + return $style; + } + + foreach ($dataRow as $cell) { + if (is_string($cell) && strpos($cell, "\n") !== false) { + $style->setShouldWrapText(); + break; + } + } + + return $style; + } + /** * Returns the content of the "styles.xml" file, given a list of styles. * @return string diff --git a/src/Spout/Writer/XLSX/Internal/Workbook.php b/src/Spout/Writer/XLSX/Internal/Workbook.php index cc707b0..e1c1f08 100644 --- a/src/Spout/Writer/XLSX/Internal/Workbook.php +++ b/src/Spout/Writer/XLSX/Internal/Workbook.php @@ -44,11 +44,6 @@ class Workbook /** @var Worksheet The worksheet where data will be written to */ protected $currentWorksheet; - protected $styles = []; - - - - /** * @param string $tempFolder * @param bool $shouldUseInlineStrings @@ -192,13 +187,15 @@ class Workbook if ($this->shouldCreateNewSheetsAutomatically) { $currentWorksheet = $this->addNewSheetAndMakeItCurrent(); - $registeredStyle = $this->styleHelper->registerStyle($style); + $updatedStyle = $this->styleHelper->applyExtraStylesIfNeeded($style, $dataRow); + $registeredStyle = $this->styleHelper->registerStyle($updatedStyle); $currentWorksheet->addRow($dataRow, $registeredStyle); } else { // otherwise, do nothing as the data won't be read anyways } } else { - $registeredStyle = $this->styleHelper->registerStyle($style); + $updatedStyle = $this->styleHelper->applyExtraStylesIfNeeded($style, $dataRow); + $registeredStyle = $this->styleHelper->registerStyle($updatedStyle); $currentWorksheet->addRow($dataRow, $registeredStyle); } } diff --git a/tests/Spout/Writer/XLSX/Helper/StyleHelperTest.php b/tests/Spout/Writer/XLSX/Helper/StyleHelperTest.php index 13d1575..333c1c2 100644 --- a/tests/Spout/Writer/XLSX/Helper/StyleHelperTest.php +++ b/tests/Spout/Writer/XLSX/Helper/StyleHelperTest.php @@ -56,4 +56,34 @@ class StyleHelperTest extends \PHPUnit_Framework_TestCase $this->assertEquals(1, $registeredStyle1->getId()); $this->assertEquals(1, $registeredStyle2->getId()); } + + /** + * @return void + */ + public function testApplyExtraStylesIfNeededShouldApplyWrapTextIfCellContainsNewLine() + { + $style = clone $this->defaultStyle; + $styleHelper = new StyleHelper($this->defaultStyle); + + $this->assertFalse($style->shouldWrapText()); + + $updatedStyle = $styleHelper->applyExtraStylesIfNeeded($style, [12, 'single line', "multi\nlines", null]); + + $this->assertTrue($updatedStyle->shouldWrapText()); + } + + /** + * @return void + */ + public function testApplyExtraStylesIfNeededShouldDoNothingIfWrapTextAlreadyApplied() + { + $style = (new StyleBuilder())->setShouldWrapText()->build(); + $styleHelper = new StyleHelper($this->defaultStyle); + + $this->assertTrue($style->shouldWrapText()); + + $updatedStyle = $styleHelper->applyExtraStylesIfNeeded($style, ["multi\nlines"]); + + $this->assertTrue($updatedStyle->shouldWrapText()); + } } diff --git a/tests/Spout/Writer/XLSX/WriterWithStyleTest.php b/tests/Spout/Writer/XLSX/WriterWithStyleTest.php index 882e6d6..b450061 100644 --- a/tests/Spout/Writer/XLSX/WriterWithStyleTest.php +++ b/tests/Spout/Writer/XLSX/WriterWithStyleTest.php @@ -206,6 +206,25 @@ class WriterWithStyleTest extends \PHPUnit_Framework_TestCase $this->assertEquals('1', $cellDomElements[1]->getAttribute('s')); } + /** + * @return void + */ + public function testAddRowWithStyleShouldApplyWrapTextIfCellContainsNewLine() + { + $fileName = 'test_add_row_with_style_should_apply_wrap_text_if_new_lines.xlsx'; + $dataRows = [ + ["xlsx--11\nxlsx--11"], + ['xlsx--21'], + ]; + + $this->writeToXLSXFile($dataRows, $fileName, $this->defaultStyle); + + $cellXfsDomElement = $this->getXmlSectionFromStylesXmlFile($fileName, 'cellXfs'); + $xfElement = $cellXfsDomElement->getElementsByTagName('xf')->item(1); + $this->assertEquals(1, $xfElement->getAttribute('applyAlignment')); + $this->assertFirstChildHasAttributeEquals('1', $xfElement, 'alignment', 'wrapText'); + } + /** * @param array $allRows * @param string $fileName