diff --git a/src/Spout/Reader/Helper/XLSX/SharedStringsHelper.php b/src/Spout/Reader/Helper/XLSX/SharedStringsHelper.php index b1a239c..89f0004 100644 --- a/src/Spout/Reader/Helper/XLSX/SharedStringsHelper.php +++ b/src/Spout/Reader/Helper/XLSX/SharedStringsHelper.php @@ -31,6 +31,9 @@ class SharedStringsHelper */ const MAX_NUM_STRINGS_PER_TEMP_FILE = 10000; + /** Value to use to escape the line feed character ("\n") */ + const ESCAPED_LINE_FEED_CHARACTER = '_x000A_'; + /** @var string Path of the XLSX file being read */ protected $filePath; @@ -80,7 +83,6 @@ class SharedStringsHelper * Please note that SimpleXML does not provide such a functionality but since it is faster * and more handy to parse few XML nodes, it is used in combination with XMLReader for that purpose. * - * @param string $filePath * @return void * @throws \Box\Spout\Common\Exception\IOException If sharedStrings.xml can't be read */ @@ -120,7 +122,12 @@ class SharedStringsHelper } $unescapedTextValue = $escaper->unescape($textValue); - $this->writeSharedStringToTempFile($unescapedTextValue, $sharedStringIndex); + + // The shared string retrieval logic expects each cell data to be on one line only + // Encoding the line feed character allows to preserve this assumption + $lineFeedEncodedTextValue = $this->escapeLineFeed($unescapedTextValue); + + $this->writeSharedStringToTempFile($lineFeedEncodedTextValue, $sharedStringIndex); $sharedStringIndex++; @@ -246,7 +253,8 @@ class SharedStringsHelper $sharedString = null; if (array_key_exists($indexInFile, $this->inMemoryTempFileContents)) { - $sharedString = $this->inMemoryTempFileContents[$indexInFile]; + $escapedSharedString = $this->inMemoryTempFileContents[$indexInFile]; + $sharedString = $this->unescapeLineFeed($escapedSharedString); } if (!$sharedString) { @@ -256,6 +264,28 @@ class SharedStringsHelper return rtrim($sharedString, PHP_EOL); } + /** + * Escapes the line feed character (\n) + * + * @param string $unescapedString + * @return string + */ + private function escapeLineFeed($unescapedString) + { + return str_replace("\n", self::ESCAPED_LINE_FEED_CHARACTER, $unescapedString); + } + + /** + * Unescapes the line feed character (\n) + * + * @param string $escapedString + * @return string + */ + private function unescapeLineFeed($escapedString) + { + return str_replace(self::ESCAPED_LINE_FEED_CHARACTER, "\n", $escapedString); + } + /** * Deletes the created temporary folder and all its contents * diff --git a/src/Spout/Writer/Internal/XLSX/Workbook.php b/src/Spout/Writer/Internal/XLSX/Workbook.php index 3f8cca4..e6ca0db 100644 --- a/src/Spout/Writer/Internal/XLSX/Workbook.php +++ b/src/Spout/Writer/Internal/XLSX/Workbook.php @@ -5,7 +5,6 @@ namespace Box\Spout\Writer\Internal\XLSX; use Box\Spout\Writer\Exception\SheetNotFoundException; use Box\Spout\Writer\Helper\XLSX\FileSystemHelper; use Box\Spout\Writer\Helper\XLSX\SharedStringsHelper; -use Box\Spout\Writer\Helper\XLSX\ZipHelper; use Box\Spout\Writer\Sheet; /** diff --git a/src/Spout/Writer/Internal/XLSX/Worksheet.php b/src/Spout/Writer/Internal/XLSX/Worksheet.php index 326c41b..4a13dea 100644 --- a/src/Spout/Writer/Internal/XLSX/Worksheet.php +++ b/src/Spout/Writer/Internal/XLSX/Worksheet.php @@ -42,7 +42,8 @@ EOD; /** * @param \Box\Spout\Writer\Sheet $externalSheet The associated "external" sheet - * @param string $tempFolder Temporary folder where the files to create the XLSX will be stored + * @param string $worksheetFilesFolder Temporary folder where the files to create the XLSX will be stored + * @param \Box\Spout\Writer\Helper\XLSX\SharedStringsHelper $sharedStringsHelper Helper for shared strings * @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 */ diff --git a/tests/Spout/Reader/Helper/XLSX/SharedStringsHelperTest.php b/tests/Spout/Reader/Helper/XLSX/SharedStringsHelperTest.php index 110c04d..c868863 100644 --- a/tests/Spout/Reader/Helper/XLSX/SharedStringsHelperTest.php +++ b/tests/Spout/Reader/Helper/XLSX/SharedStringsHelperTest.php @@ -46,7 +46,7 @@ class SharedStringsHelperTest extends \PHPUnit_Framework_TestCase $this->assertEquals(1, count($filesInTempFolder), 'One temp file should have been created in the temp folder.'); $tempFileContents = file_get_contents($filesInTempFolder[0]); - $tempFileContentsPerLine = explode("\n", $tempFileContents); + $tempFileContentsPerLine = explode(PHP_EOL, $tempFileContents); $this->assertEquals('s1--A1', $tempFileContentsPerLine[0]); $this->assertEquals('s1--E5', $tempFileContentsPerLine[24]); @@ -96,4 +96,23 @@ class SharedStringsHelperTest extends \PHPUnit_Framework_TestCase $sharedString = $this->sharedStringsHelper->getStringAtIndex(24); $this->assertEquals('s1--E5', $sharedString); } + + /** + * @return void + */ + public function testGetStringAtIndexShouldWorkWithMultilineStrings() + { + $resourcePath = $this->getResourcePath('one_sheet_with_shared_multiline_strings.xlsx'); + $sharedStringsHelper = new SharedStringsHelper($resourcePath); + + $sharedStringsHelper->extractSharedStrings(); + + $sharedString = $sharedStringsHelper->getStringAtIndex(0); + $this->assertEquals("s1\nA1", $sharedString); + + $sharedString = $sharedStringsHelper->getStringAtIndex(24); + $this->assertEquals("s1\nE5", $sharedString); + + $sharedStringsHelper->cleanup(); + } } diff --git a/tests/Spout/Writer/CSVTest.php b/tests/Spout/Writer/CSVTest.php index 0d8478b..bfbc71f 100644 --- a/tests/Spout/Writer/CSVTest.php +++ b/tests/Spout/Writer/CSVTest.php @@ -135,6 +135,6 @@ class CSVTest extends \PHPUnit_Framework_TestCase private function trimWrittenContent($writtenContent) { // remove line feeds and UTF-8 BOM - return trim($writtenContent, "\n" . CSV::UTF8_BOM); + return trim($writtenContent, PHP_EOL . CSV::UTF8_BOM); } } diff --git a/tests/resources/xlsx/one_sheet_with_shared_multiline_strings.xlsx b/tests/resources/xlsx/one_sheet_with_shared_multiline_strings.xlsx new file mode 100644 index 0000000..bfe9e8b Binary files /dev/null and b/tests/resources/xlsx/one_sheet_with_shared_multiline_strings.xlsx differ