From 015afae32008e093ebcb27b93da7d95db244cece Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Mon, 23 Feb 2026 17:22:08 -0800 Subject: [PATCH] Ods Reader Style Support (Last) - Column Styles (Partial) Continuing the work of PR #4810 and PR #4813 and PR #4814. According to the spec for LibreOffice, the xml for both coluns and rows allow a `default-cell-style-name` attribute. However, my testing indicates that using that attribute with a row doesn't result in the results I would expect; furthermore, LibreOffice doesn't seem to use that attribute in association with a row (it does use it for columns). For rows, it fills the unpopulated rows in the cell with a repeated table cell with the appropriate style. These filler cells have been a source of problems for us, and I'm not willing to add them at this time (maybe in future). So, if a style is applied to a row in PhpSpreadsheet, it will be reflected in all cells in that row until `highestDataColumn`, but no further. Since LibreOffice does support adding a style to a column both in the UI and the Xml, it should be a little easier. But its support is imcomplete as well - when reading a spreadsheet, the UI will apply the style only to the rows that are defined in the xml. Again, this requires filler rows for the rows beyond `highestDataRow`, and, again, I am not willing to add those. However, it is important to process the column style when reading because, unlike the case with rows, the cells which are defined in the column may omit style data altogether if the style matches the column style. There has till now been no support for `ReadDataOnly` (default false) and `ReadEmptyCells` (default true) for Ods Reader. These are now added. ReadEmptyCells is especially important and it would be sensible to usually set it to false. The reason is that LibreOffice usually supplies a width for all the columns mentioned in its row fillers, and PhpSpreadsheet needs to create ColumnDimensions for each of those as a result, resulting in much larger xml than one would expect when the spreadsheet is saved. This arose when I was testing this change - it has always been the case, but nobody has raised an issue about it. We avoid this problem by ignoring the extra columns for ReadEmptyCells false. But this requires a little redundancy in the code. We need to define styles for an entire column before we define styles for the cells in the column, but we don't need to define column widths beforehand. So, if ReadEmptyCells is false, we need to process the columns for styles, then process the cell data (so that we know what `highDataColumn` is), then process the columns again but this time for widths (ignoring columns beyond highDataColumn). If ReadEmptyCells is true, we can process column styles and widths in a single pass. --- src/PhpSpreadsheet/Reader/Ods.php | 239 ++++++++++++++---- src/PhpSpreadsheet/Writer/Ods/Content.php | 1 - .../Reader/Ods/ColGroupTest.php | 69 +++++ .../Reader/Ods/CurrencyTest.php | 3 - .../Reader/Ods/FontStyleTest.php | 13 + .../Reader/Ods/Issue3721Test.php | 24 +- .../Reader/Ods/NestedTableRowTest.php | 2 + .../Reader/Ods/OdsInfoTest.php | 3 - .../Reader/Ods/OdsTest.php | 3 - tests/data/Reader/Ods/colgroup.ods | Bin 0 -> 9207 bytes tests/data/Reader/Ods/colheader.ods | Bin 0 -> 10239 bytes 11 files changed, 292 insertions(+), 65 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Ods/ColGroupTest.php create mode 100644 tests/data/Reader/Ods/colgroup.ods create mode 100644 tests/data/Reader/Ods/colheader.ods diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index 093892c87..e81663a93 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -298,6 +298,8 @@ class Ods extends BaseReader */ private array $allStyles; + private int $highestDataIndex; + /** * Loads PhpSpreadsheet from file into PhpSpreadsheet instance. */ @@ -335,8 +337,7 @@ class Ods extends BaseReader $styleNs = (string) $dom->lookupNamespaceUri('style'); $fontNs = (string) $dom->lookupNamespaceUri('fo'); - $defaultStyleSet = false; - $automaticStyle0 = $dom->getElementsByTagNameNS($officeNs, 'styles')->item(0); + $automaticStyle0 = $this->readDataOnly ? null : $dom->getElementsByTagNameNS($officeNs, 'styles')->item(0); $automaticStyles = ($automaticStyle0 === null) ? [] : $automaticStyle0->getElementsByTagNameNS($styleNs, 'default-style'); foreach ($automaticStyles as $automaticStyle) { $styleFamily = $automaticStyle->getAttributeNS($styleNs, 'family'); @@ -346,7 +347,6 @@ class Ods extends BaseReader $fonts = $this->getFontStyles($textProperty, $styleNs, $fontNs); } if (!empty($fonts)) { - $defaultStyleSet = true; $spreadsheet->getDefaultStyle() ->getFont() ->applyFromArray($fonts); @@ -368,7 +368,7 @@ class Ods extends BaseReader if ($styleName !== '') { if (!empty($fonts)) { $this->allStyles[$styleName]['font'] = $fonts; - if (!$defaultStyleSet && $styleName === 'Default') { + if ($styleName === 'Default') { $spreadsheet->getDefaultStyle() ->getFont() ->applyFromArray($fonts); @@ -405,7 +405,7 @@ class Ods extends BaseReader $autoFilterReader = new AutoFilter($spreadsheet, $tableNs); $definedNameReader = new DefinedNames($spreadsheet, $tableNs); $columnWidths = []; - $automaticStyle0 = $dom->getElementsByTagNameNS($officeNs, 'automatic-styles')->item(0); + $automaticStyle0 = $this->readDataOnly ? null : $dom->getElementsByTagNameNS($officeNs, 'automatic-styles')->item(0); $automaticStyles = ($automaticStyle0 === null) ? [] : $automaticStyle0->getElementsByTagNameNS($styleNs, 'style'); foreach ($automaticStyles as $automaticStyle) { $styleName = $automaticStyle->getAttributeNS($styleNs, 'name'); @@ -496,6 +496,7 @@ class Ods extends BaseReader // Go through every child of table element $rowID = 1; $tableColumnIndex = 1; + $this->highestDataIndex = 16384; foreach ($worksheetDataSet->childNodes as $childNode) { /** @var DOMElement $childNode */ @@ -533,37 +534,6 @@ class Ods extends BaseReader $spreadsheet ); - break; - case 'table-header-columns': - case 'table-columns': - $this->processTableHeaderColumns( - $childNode, - $tableNs, - $columnWidths, - $tableColumnIndex, - $spreadsheet - ); - - break; - case 'table-column-group': - $this->processTableColumnGroup( - $childNode, - $tableNs, - $columnWidths, - $tableColumnIndex, - $spreadsheet - ); - - break; - case 'table-column': - $this->processTableColumn( - $childNode, - $tableNs, - $columnWidths, - $tableColumnIndex, - $spreadsheet - ); - break; case 'table-row': $this->processTableRow( @@ -577,6 +547,43 @@ class Ods extends BaseReader $spreadsheet ); + break; + case 'table-header-columns': + case 'table-columns': + $this->processTableColumnHeader( + $childNode, + $tableNs, + $columnWidths, + $tableColumnIndex, + $spreadsheet, + $this->readEmptyCells, + true + ); + + break; + case 'table-column-group': + $this->processTableColumnGroup( + $childNode, + $tableNs, + $columnWidths, + $tableColumnIndex, + $spreadsheet, + $this->readEmptyCells, + true + ); + + break; + case 'table-column': + $this->processTableColumn( + $childNode, + $tableNs, + $columnWidths, + $tableColumnIndex, + $spreadsheet, + $this->readEmptyCells, + true + ); + break; } } @@ -593,10 +600,94 @@ class Ods extends BaseReader if ($this->createBlankSheetIfNoneRead && !$sheetCreated) { $spreadsheet->createSheet(); } + } + + foreach ($spreadsheets as $workbookData) { + /** @var DOMElement $workbookData */ + $tables = $workbookData->getElementsByTagNameNS($tableNs, 'table'); + + $worksheetID = 0; + foreach ($tables as $worksheetDataSet) { + /** @var DOMElement $worksheetDataSet */ + $worksheetName = $worksheetDataSet->getAttributeNS($tableNs, 'name'); + + // Check loadSheetsOnly + if ( + $this->loadSheetsOnly !== null + && $worksheetName + && !in_array($worksheetName, $this->loadSheetsOnly) + ) { + continue; + } + + // Create sheet + $spreadsheet->setActiveSheetIndex($worksheetID); + $highestDataColumn = $spreadsheet->getActiveSheet()->getHighestDataColumn(); + $this->highestDataIndex = Coordinate::columnIndexFromString($highestDataColumn); + + // Go through every child of table element processing column widths + $rowID = 1; + $tableColumnIndex = 1; + foreach ($worksheetDataSet->childNodes as $childNode) { + /** @var DOMElement $childNode */ + if (empty($columnWidths) || $this->readEmptyCells) { + break; + } + + // Filter elements which are not under the "table" ns + if ($childNode->namespaceURI != $tableNs) { + continue; + } + + $key = self::extractNodeName($childNode->nodeName); + + switch ($key) { + case 'table-header-columns': + case 'table-columns': + $this->processTableColumnHeader( + $childNode, + $tableNs, + $columnWidths, + $tableColumnIndex, + $spreadsheet, + true, + false + ); + + break; + case 'table-column-group': + $this->processTableColumnGroup( + $childNode, + $tableNs, + $columnWidths, + $tableColumnIndex, + $spreadsheet, + true, + false + ); + + break; + case 'table-column': + $this->processTableColumn( + $childNode, + $tableNs, + $columnWidths, + $tableColumnIndex, + $spreadsheet, + true, + false + ); + + break; + } + } + ++$worksheetID; + } $autoFilterReader->read($workbookData); $definedNameReader->read($workbookData); } + $spreadsheet->setActiveSheetIndex(0); if ($zip->locateName('settings.xml') !== false) { @@ -1091,12 +1182,14 @@ class Ods extends BaseReader /** * @param string[] $columnWidths */ - private function processTableHeaderColumns( + private function processTableColumnHeader( DOMElement $childNode, string $tableNs, array $columnWidths, int &$tableColumnIndex, - Spreadsheet $spreadsheet + Spreadsheet $spreadsheet, + bool $processWidths = true, + bool $processStyles = true ): void { foreach ($childNode->childNodes as $grandchildNode) { /** @var DOMElement $grandchildNode */ @@ -1108,7 +1201,9 @@ class Ods extends BaseReader $tableNs, $columnWidths, $tableColumnIndex, - $spreadsheet + $spreadsheet, + $processWidths, + $processStyles ); break; @@ -1124,7 +1219,9 @@ class Ods extends BaseReader string $tableNs, array $columnWidths, int &$tableColumnIndex, - Spreadsheet $spreadsheet + Spreadsheet $spreadsheet, + bool $processWidths = true, + bool $processStyles = true ): void { foreach ($childNode->childNodes as $grandchildNode) { /** @var DOMElement $grandchildNode */ @@ -1136,18 +1233,22 @@ class Ods extends BaseReader $tableNs, $columnWidths, $tableColumnIndex, - $spreadsheet + $spreadsheet, + $processWidths, + $processStyles ); break; case 'table-header-columns': case 'table-columns': - $this->processTableHeaderColumns( + $this->processTableColumnHeader( $grandchildNode, $tableNs, $columnWidths, $tableColumnIndex, - $spreadsheet + $spreadsheet, + $processWidths, + $processStyles ); break; @@ -1157,7 +1258,9 @@ class Ods extends BaseReader $tableNs, $columnWidths, $tableColumnIndex, - $spreadsheet + $spreadsheet, + $processWidths, + $processStyles ); break; @@ -1173,7 +1276,9 @@ class Ods extends BaseReader string $tableNs, array $columnWidths, int &$tableColumnIndex, - Spreadsheet $spreadsheet + Spreadsheet $spreadsheet, + bool $processWidths = true, + bool $processStyles = true ): void { if ($childNode->hasAttributeNS($tableNs, 'number-columns-repeated')) { $rowRepeats = (int) $childNode->getAttributeNS($tableNs, 'number-columns-repeated'); @@ -1181,15 +1286,41 @@ class Ods extends BaseReader $rowRepeats = 1; } $tableStyleName = $childNode->getAttributeNS($tableNs, 'style-name'); - if (isset($columnWidths[$tableStyleName])) { - $columnWidth = new HelperDimension($columnWidths[$tableStyleName]); - $tableColumnString = Coordinate::stringFromColumnIndex($tableColumnIndex); - for ($rowRepeats2 = $rowRepeats; $rowRepeats2 > 0; --$rowRepeats2) { - /** @var string $tableColumnString */ - $spreadsheet->getActiveSheet() - ->getColumnDimension($tableColumnString) - ->setWidth($columnWidth->toUnit('cm'), 'cm'); - StringHelper::stringIncrement($tableColumnString); + if ($processWidths) { + if (isset($columnWidths[$tableStyleName])) { + $columnWidth = new HelperDimension($columnWidths[$tableStyleName]); + $tableColumnIndex2 = $tableColumnIndex; + $tableColumnString = Coordinate::stringFromColumnIndex($tableColumnIndex2); + for ($rowRepeats2 = $rowRepeats; $rowRepeats2 > 0 && $tableColumnIndex2 <= 16384; --$rowRepeats2) { + if (!$this->readEmptyCells && $tableColumnIndex2 > $this->highestDataIndex) { + break; + } + $spreadsheet->getActiveSheet() + ->getColumnDimension($tableColumnString) + ->setWidth($columnWidth->toUnit('cm'), 'cm'); + StringHelper::stringIncrement( + $tableColumnString + ); + ++$tableColumnIndex2; + } + } + } + if ($processStyles) { + $defaultStyleName = $childNode->getAttributeNS($tableNs, 'default-cell-style-name'); + if ($defaultStyleName !== 'Default' && isset($this->allStyles[$defaultStyleName])) { + $tableColumnIndex2 = $tableColumnIndex; + $tableColumnString = Coordinate::stringFromColumnIndex($tableColumnIndex2); + for ($rowRepeats2 = $rowRepeats; $rowRepeats2 > 0 && $tableColumnIndex2 <= 16384; --$rowRepeats2) { + $spreadsheet->getActiveSheet() + ->getStyle($tableColumnString) + ->applyFromArray( + $this->allStyles[$defaultStyleName] + ); + StringHelper::stringIncrement( + $tableColumnString + ); + ++$tableColumnIndex2; + } } } $tableColumnIndex += $rowRepeats; diff --git a/src/PhpSpreadsheet/Writer/Ods/Content.php b/src/PhpSpreadsheet/Writer/Ods/Content.php index e6eebdcdf..e11bec4d1 100644 --- a/src/PhpSpreadsheet/Writer/Ods/Content.php +++ b/src/PhpSpreadsheet/Writer/Ods/Content.php @@ -142,7 +142,6 @@ class Content extends WriterPart 'table:style-name', sprintf('%s_%d_%d', Style::COLUMN_STYLE_PREFIX, $sheetIndex, $columnDimension->getColumnNumeric()) ); - $objWriter->writeAttribute('table:default-cell-style-name', 'ce0'); $objWriter->endElement(); } $this->writeRows($objWriter, $spreadsheet->getSheet($sheetIndex), $sheetIndex); diff --git a/tests/PhpSpreadsheetTests/Reader/Ods/ColGroupTest.php b/tests/PhpSpreadsheetTests/Reader/Ods/ColGroupTest.php new file mode 100644 index 000000000..c8d691ed7 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Ods/ColGroupTest.php @@ -0,0 +1,69 @@ +setReadEmptyCells(false); + $spreadsheet = $reader->load(self::ODS_COLGROUP_FILE); + self::assertSame('Arial', $spreadsheet->getDefaultStyle()->getFont()->getName()); + $sheet = $spreadsheet->getActiveSheet(); + $expected = [ + 'A' => 2.778, + 'B' => 2.99, + 'C' => 2.91, + 'D' => 2.858, + 'E' => 1.746, + 'F' => 1.746, + 'G' => 1.746, + 'H' => 1.746, + ]; + foreach ($expected as $col => $width) { + self::assertEqualsWithDelta( + $width, + $sheet->getColumnDimension($col) + ->getWidth(Dimension::UOM_CENTIMETERS), + 1.0E-3, + "mismatch for column $col" + ); + } + $spreadsheet->disconnectWorksheets(); + } + + public function testColHeader(): void + { + $reader = new OdsReader(); + $reader->setReadEmptyCells(false); + $spreadsheet = $reader->load(self::ODS_COLHEADER_FILE); + self::assertSame('Liberation Sans', $spreadsheet->getDefaultStyle()->getFont()->getName()); + $sheet = $spreadsheet->getActiveSheet(); + $expected = [ + 'A' => 2.858, + ]; + foreach ($expected as $col => $width) { + self::assertEqualsWithDelta( + $width, + $sheet->getColumnDimension($col) + ->getWidth(Dimension::UOM_CENTIMETERS), + 1.0E-3, + "mismatch for column $col" + ); + } + $spreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Ods/CurrencyTest.php b/tests/PhpSpreadsheetTests/Reader/Ods/CurrencyTest.php index 4d3fce708..f4fa27a4a 100644 --- a/tests/PhpSpreadsheetTests/Reader/Ods/CurrencyTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Ods/CurrencyTest.php @@ -7,9 +7,6 @@ namespace PhpOffice\PhpSpreadsheetTests\Reader\Ods; use PhpOffice\PhpSpreadsheet\Reader\Ods; use PHPUnit\Framework\TestCase; -/** - * @TODO The class doesn't read the bold/italic/underline properties (rich text) - */ class CurrencyTest extends TestCase { private const ODS_CURRENCY_FILE = 'tests/data/Reader/Ods/currency4.ods'; diff --git a/tests/PhpSpreadsheetTests/Reader/Ods/FontStyleTest.php b/tests/PhpSpreadsheetTests/Reader/Ods/FontStyleTest.php index c51d22f66..d7f50f1f1 100644 --- a/tests/PhpSpreadsheetTests/Reader/Ods/FontStyleTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Ods/FontStyleTest.php @@ -121,4 +121,17 @@ class FontStyleTest extends AbstractFunctional self::assertSame('Times New Roman', $sheet->getStyle('A1')->getFont()->getName()); $spreadsheet->disconnectWorksheets(); } + + public static function testDataOnly(): void + { + $reader = new OdsReader(); + $reader->setReadDataOnly(true); + $infile = 'tests/data/Reader/Ods/odsstyles5.ods'; + $spreadsheet = $reader->load($infile); + self::assertSame('Calibri', $spreadsheet->getDefaultStyle()->getFont()->getName(), 'ignore spreadsheet style, use PhpSpreadsheet default'); + $sheet = $spreadsheet->getActiveSheet(); + self::assertSame('Calibri', $sheet->getStyle('A1')->getFont()->getName()); + self::assertSame(13, $sheet->getCell('A1')->getValue()); + $spreadsheet->disconnectWorksheets(); + } } diff --git a/tests/PhpSpreadsheetTests/Reader/Ods/Issue3721Test.php b/tests/PhpSpreadsheetTests/Reader/Ods/Issue3721Test.php index 15765e637..a08670761 100644 --- a/tests/PhpSpreadsheetTests/Reader/Ods/Issue3721Test.php +++ b/tests/PhpSpreadsheetTests/Reader/Ods/Issue3721Test.php @@ -9,7 +9,7 @@ use PHPUnit\Framework\TestCase; class Issue3721Test extends TestCase { - public function testIssue2810(): void + public function xtestIssue2810ReadEmpty(): void { // Problems with getHighestDataColumn $filename = 'tests/data/Reader/Ods/issue.3721.ods'; @@ -29,4 +29,26 @@ class Issue3721Test extends TestCase self::assertSame('BL', $sheet->getHighestColumn()); $spreadsheet->disconnectWorksheets(); } + + public function testIssue2810NoReadEmpty(): void + { + // Problems with getHighestDataColumn + $filename = 'tests/data/Reader/Ods/issue.3721.ods'; + $reader = new Ods(); + $reader->setReadEmptyCells(false); + $spreadsheet = $reader->load($filename); + $sheet = $spreadsheet->getSheetByNameOrThrow('sheet with data'); + $origHigh = $sheet->getHighestDataColumn(); + self::assertSame('C', $origHigh); + $cells = []; + foreach ($sheet->getRowIterator() as $row) { + foreach ($row->getCellIterator(iterateOnlyExistingCells: true) as $cell) { + $cells[] = $cell->getCoordinate(); + } + } + self::assertSame(['A1', 'B1', 'C1', 'A2', 'B2', 'C2'], $cells); + self::assertSame('C', $sheet->getHighestDataColumn()); + self::assertSame('C', $sheet->getHighestColumn()); + $spreadsheet->disconnectWorksheets(); + } } diff --git a/tests/PhpSpreadsheetTests/Reader/Ods/NestedTableRowTest.php b/tests/PhpSpreadsheetTests/Reader/Ods/NestedTableRowTest.php index 45e0646b9..67e080832 100644 --- a/tests/PhpSpreadsheetTests/Reader/Ods/NestedTableRowTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Ods/NestedTableRowTest.php @@ -13,6 +13,7 @@ class NestedTableRowTest extends TestCase { $infile = 'tests/data/Reader/Ods/issue.4528.ods'; $reader = new OdsReader(); + $reader->setReadDataOnly(true)->setReadEmptyCells(false); $spreadsheet = $reader->load($infile); $sheet = $spreadsheet->getActiveSheet(); self::assertSame('Atterissage', $sheet->getCell('AS1')->getValue()); @@ -26,6 +27,7 @@ class NestedTableRowTest extends TestCase { $infile = 'tests/data/Reader/Ods/issue.2507.ods'; $reader = new OdsReader(); + $reader->setReadDataOnly(true)->setReadEmptyCells(false); $spreadsheet = $reader->load($infile); $sheet = $spreadsheet->getActiveSheet(); $values = $sheet->rangeToArray('B3:C7', null, false, false); diff --git a/tests/PhpSpreadsheetTests/Reader/Ods/OdsInfoTest.php b/tests/PhpSpreadsheetTests/Reader/Ods/OdsInfoTest.php index 1ba0847d7..d4cc6651b 100644 --- a/tests/PhpSpreadsheetTests/Reader/Ods/OdsInfoTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Ods/OdsInfoTest.php @@ -8,9 +8,6 @@ use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException; use PhpOffice\PhpSpreadsheet\Reader\Ods; use PHPUnit\Framework\TestCase; -/** - * @TODO The class doesn't read the bold/italic/underline properties (rich text) - */ class OdsInfoTest extends TestCase { public function testReadFileProperties(): void diff --git a/tests/PhpSpreadsheetTests/Reader/Ods/OdsTest.php b/tests/PhpSpreadsheetTests/Reader/Ods/OdsTest.php index 60b8d35a6..fe610afa5 100644 --- a/tests/PhpSpreadsheetTests/Reader/Ods/OdsTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Ods/OdsTest.php @@ -12,9 +12,6 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Font; use PHPUnit\Framework\TestCase; -/** - * @TODO The class doesn't read the bold/italic/underline properties (rich text) - */ class OdsTest extends TestCase { private const ODS_TEST_FILE = 'samples/templates/OOCalcTest.ods'; diff --git a/tests/data/Reader/Ods/colgroup.ods b/tests/data/Reader/Ods/colgroup.ods new file mode 100644 index 0000000000000000000000000000000000000000..7ac62c578927f62046deca0c96febc1535cd9648 GIT binary patch literal 9207 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