From 156fd29a44dc04adf8ad15fb5e3b57b0c40bd102 Mon Sep 17 00:00:00 2001 From: Adrien Loison Date: Mon, 31 Aug 2015 09:55:17 -0700 Subject: [PATCH] Improve ODS Writer Remove num-columns-repeated and num-rows-repeated as it does not seem to be required (LibreOffice does not add them). This greatly simplifies the writer and the XML output. Added some optional attributes to help LibreOffice with cell values caching ("calcext") --- .../Writer/ODS/Helper/FileSystemHelper.php | 6 +-- src/Spout/Writer/ODS/Helper/StyleHelper.php | 2 +- src/Spout/Writer/ODS/Internal/Worksheet.php | 50 ++++++------------- .../Spout/Writer/ODS/WriterWithStyleTest.php | 4 +- .../Spout/Writer/XLSX/WriterWithStyleTest.php | 4 +- 5 files changed, 23 insertions(+), 43 deletions(-) diff --git a/src/Spout/Writer/ODS/Helper/FileSystemHelper.php b/src/Spout/Writer/ODS/Helper/FileSystemHelper.php index 3270fc3..1bb1674 100644 --- a/src/Spout/Writer/ODS/Helper/FileSystemHelper.php +++ b/src/Spout/Writer/ODS/Helper/FileSystemHelper.php @@ -145,7 +145,7 @@ EOD; $metaXmlFileContents = << - + $appName $createdDate @@ -182,7 +182,7 @@ EOD; { $contentXmlFileContents = << - + EOD; @@ -203,7 +203,7 @@ EOD; foreach ($worksheets as $worksheet) { // write the "" node, with the final sheet's name - fwrite($contentXmlHandle, $worksheet->getTableRootNodeAsString() . PHP_EOL); + fwrite($contentXmlHandle, $worksheet->getTableElementStartAsString() . PHP_EOL); $worksheetFilePath = $worksheet->getWorksheetFilePath(); $this->copyFileContentsToTarget($worksheetFilePath, $contentXmlHandle); diff --git a/src/Spout/Writer/ODS/Helper/StyleHelper.php b/src/Spout/Writer/ODS/Helper/StyleHelper.php index 2752ad9..f5f9de8 100644 --- a/src/Spout/Writer/ODS/Helper/StyleHelper.php +++ b/src/Spout/Writer/ODS/Helper/StyleHelper.php @@ -46,7 +46,7 @@ class StyleHelper extends AbstractStyleHelper { $content = << - + EOD; diff --git a/src/Spout/Writer/ODS/Internal/Worksheet.php b/src/Spout/Writer/ODS/Internal/Worksheet.php index b2b1222..fe55382 100644 --- a/src/Spout/Writer/ODS/Internal/Worksheet.php +++ b/src/Spout/Writer/ODS/Internal/Worksheet.php @@ -18,13 +18,6 @@ use Box\Spout\Writer\Common\Sheet; */ class Worksheet implements WorksheetInterface { - /** - * @see https://wiki.openoffice.org/wiki/Documentation/FAQ/Calc/Miscellaneous/What's_the_maximum_number_of_rows_and_cells_for_a_spreadsheet_file%3f - * @see https://bz.apache.org/ooo/show_bug.cgi?id=30215 - */ - const MAX_NUM_ROWS_REPEATED = 1048576; - const MAX_NUM_COLUMNS_REPEATED = 1024; - /** @var \Box\Spout\Writer\Common\Sheet The "external" sheet */ protected $externalSheet; @@ -37,9 +30,12 @@ class Worksheet implements WorksheetInterface /** @var \Box\Spout\Common\Helper\StringHelper To help with string manipulation */ protected $stringHelper; - /** @var Resource Pointer to the sheet data file (e.g. xl/worksheets/sheet1.xml) */ + /** @var Resource Pointer to the temporary sheet data file (e.g. worksheets-temp/sheet1.xml) */ protected $sheetFilePointer; + /** @var int Maximum number of columns among all the written rows */ + protected $maxNumColumns = 1; + /** @var int Index of the last written row */ protected $lastWrittenRowIndex = 0; @@ -62,6 +58,8 @@ class Worksheet implements WorksheetInterface /** * Prepares the worksheet to accept data + * The XML file does not contain the "" node as it contains the sheet's name + * which may change during the execution of the program. It will be added at the end. * * @return void * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing @@ -70,11 +68,6 @@ class Worksheet implements WorksheetInterface { $this->sheetFilePointer = fopen($this->worksheetFilePath, 'w'); $this->throwIfSheetFilePointerIsNotAvailable(); - - // The XML file does not contain the "" node as it contains the sheet's name - // which may change during the execution of the program. It will be added at the end. - $content = ' ' . PHP_EOL; - fwrite($this->sheetFilePointer, $content); } /** @@ -103,12 +96,15 @@ class Worksheet implements WorksheetInterface * * @return string node as string */ - public function getTableRootNodeAsString() + public function getTableElementStartAsString() { $escapedSheetName = $this->stringsEscaper->escape($this->externalSheet->getName()); $tableStyleName = 'ta' . ($this->externalSheet->getIndex() + 1); - return ''; + $tableElement = '' . PHP_EOL; + $tableElement .= ' '; + + return $tableElement; } /** @@ -139,7 +135,7 @@ class Worksheet implements WorksheetInterface */ public function addRow($dataRow, $style) { - $numColumnsRepeated = self::MAX_NUM_COLUMNS_REPEATED; + $this->maxNumColumns = max($this->maxNumColumns, count($dataRow)); $styleIndex = ($style->getId() + 1); // 1-based $data = ' ' . PHP_EOL; @@ -148,7 +144,7 @@ class Worksheet implements WorksheetInterface $data .= ' ' . PHP_EOL; + $data .= ' office:value-type="boolean" calcext:value-type="boolean" office:value="' . $cellValue . '">' . PHP_EOL; $data .= ' ' . $cellValue . '' . PHP_EOL; $data .= ' ' . PHP_EOL; } else if (CellHelper::isNumeric($cellValue)) { - $data .= ' office:value-type="float" office:value="' . $cellValue . '">' . PHP_EOL; + $data .= ' office:value-type="float" calcext:value-type="float" office:value="' . $cellValue . '">' . PHP_EOL; $data .= ' ' . $cellValue . '' . PHP_EOL; $data .= ' ' . PHP_EOL; } else if (empty($cellValue)) { @@ -169,12 +165,6 @@ class Worksheet implements WorksheetInterface } else { throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cellValue)); } - - $numColumnsRepeated--; - } - - if ($numColumnsRepeated > 0) { - $data .= ' ' . PHP_EOL; } $data .= ' ' . PHP_EOL; @@ -195,16 +185,6 @@ class Worksheet implements WorksheetInterface */ public function close() { - $remainingRepeatedRows = self::MAX_NUM_ROWS_REPEATED - $this->lastWrittenRowIndex; - - if ($remainingRepeatedRows > 0) { - $data = ' ' . PHP_EOL; - $data .= ' ' . PHP_EOL; - $data .= ' ' . PHP_EOL; - - fwrite($this->sheetFilePointer, $data); - } - fclose($this->sheetFilePointer); } } diff --git a/tests/Spout/Writer/ODS/WriterWithStyleTest.php b/tests/Spout/Writer/ODS/WriterWithStyleTest.php index 1cfca7a..cc3fc50 100644 --- a/tests/Spout/Writer/ODS/WriterWithStyleTest.php +++ b/tests/Spout/Writer/ODS/WriterWithStyleTest.php @@ -114,7 +114,7 @@ class WriterWithStyleTest extends \PHPUnit_Framework_TestCase $style2 = (new StyleBuilder()) ->setFontSize(15) ->setFontColor(Color::RED) - ->setFontName('Font') + ->setFontName('Cambria') ->build(); $this->writeToODSFileWithMultipleStyles($dataRows, $fileName, [$style, $style2]); @@ -133,7 +133,7 @@ class WriterWithStyleTest extends \PHPUnit_Framework_TestCase $customFont2Element = $cellStyleElements[2]; $this->assertFirstChildHasAttributeEquals('15pt', $customFont2Element, 'text-properties', 'fo:font-size'); $this->assertFirstChildHasAttributeEquals('#' . Color::RED, $customFont2Element, 'text-properties', 'fo:color'); - $this->assertFirstChildHasAttributeEquals('Font', $customFont2Element, 'text-properties', 'style:font-name'); + $this->assertFirstChildHasAttributeEquals('Cambria', $customFont2Element, 'text-properties', 'style:font-name'); } /** diff --git a/tests/Spout/Writer/XLSX/WriterWithStyleTest.php b/tests/Spout/Writer/XLSX/WriterWithStyleTest.php index d8eeb1d..9a531b9 100644 --- a/tests/Spout/Writer/XLSX/WriterWithStyleTest.php +++ b/tests/Spout/Writer/XLSX/WriterWithStyleTest.php @@ -114,7 +114,7 @@ class WriterWithStyleTest extends \PHPUnit_Framework_TestCase $style2 = (new StyleBuilder()) ->setFontSize(15) ->setFontColor(Color::RED) - ->setFontName('Font') + ->setFontName('Cambria') ->build(); $this->writeToXLSXFileWithMultipleStyles($dataRows, $fileName, [$style, $style2]); @@ -148,7 +148,7 @@ class WriterWithStyleTest extends \PHPUnit_Framework_TestCase $this->assertChildrenNumEquals(3, $thirdFontElement, 'The font should only have 3 properties.'); $this->assertFirstChildHasAttributeEquals('15', $thirdFontElement, 'sz', 'val'); $this->assertFirstChildHasAttributeEquals(Color::toARGB(Color::RED), $thirdFontElement, 'color', 'rgb'); - $this->assertFirstChildHasAttributeEquals('Font', $thirdFontElement, 'name', 'val'); + $this->assertFirstChildHasAttributeEquals('Cambria', $thirdFontElement, 'name', 'val'); } /**