From 7a2f5c4ccc3cd69234179488d0e1dd9e3f6ed7bc Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 16 Apr 2022 19:34:41 +0200 Subject: [PATCH] Ods Writer support for setting column width/row height (including Autosizing) --- CHANGELOG.md | 1 + phpstan-baseline.neon | 5 -- src/PhpSpreadsheet/Writer/Ods/Cell/Style.php | 62 +++++++++++++++++++ src/PhpSpreadsheet/Writer/Ods/Content.php | 51 ++++++++++++--- .../Writer/PreCalcTest.php | 2 +- tests/data/Writer/Ods/content-empty.xml | 1 - tests/data/Writer/Ods/content-with-data.xml | 2 - 7 files changed, 105 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3912c622..ceda96f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Added +- Ods Writer support for setting column width/row height (including the use of AutoSize) [Issue #2346](https://github.com/PHPOffice/PhpSpreadsheet/issues/2346) [PR #2753](https://github.com/PHPOffice/PhpSpreadsheet/pull/2753) - Introduced CellAddress, CellRange, RowRange and ColumnRange value objects that can be used as an alternative to a string value (e.g. `'C5'`, `'B2:D4'`, `'2:2'` or `'B:C'`) in appropriate contexts. - Implementation of the FILTER(), SORT(), SORTBY() and UNIQUE() Lookup/Reference (array) functions. - Implementation of the ISREF() Information function. diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 12329a72..c6d3dda9 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -4675,11 +4675,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Writer/Ods/Content.php - - - message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, int given\\.$#" - count: 4 - path: src/PhpSpreadsheet/Writer/Ods/Content.php - - message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, int\\<2, max\\> given\\.$#" count: 3 diff --git a/src/PhpSpreadsheet/Writer/Ods/Cell/Style.php b/src/PhpSpreadsheet/Writer/Ods/Cell/Style.php index f8aae20c..a3629bd6 100644 --- a/src/PhpSpreadsheet/Writer/Ods/Cell/Style.php +++ b/src/PhpSpreadsheet/Writer/Ods/Cell/Style.php @@ -2,15 +2,20 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Ods\Cell; +use PhpOffice\PhpSpreadsheet\Helper\Dimension; use PhpOffice\PhpSpreadsheet\Shared\XMLWriter; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Font; use PhpOffice\PhpSpreadsheet\Style\Style as CellStyle; +use PhpOffice\PhpSpreadsheet\Worksheet\ColumnDimension; +use PhpOffice\PhpSpreadsheet\Worksheet\RowDimension; class Style { public const CELL_STYLE_PREFIX = 'ce'; + public const COLUMN_STYLE_PREFIX = 'co'; + public const ROW_STYLE_PREFIX = 'ro'; private $writer; @@ -159,6 +164,63 @@ class Style $this->writer->endElement(); // Close style:text-properties } + protected function writeColumnProperties(ColumnDimension $columnDimension): void + { + $this->writer->startElement('style:table-column-properties'); + $this->writer->writeAttribute( + 'style:column-width', + round($columnDimension->getWidth(Dimension::UOM_CENTIMETERS), 3) . 'cm' + ); + $this->writer->writeAttribute('fo:break-before', 'auto'); + + // End + $this->writer->endElement(); // Close style:table-column-properties + } + + public function writeColumnStyles(ColumnDimension $columnDimension, int $sheetId): void + { + $this->writer->startElement('style:style'); + $this->writer->writeAttribute('style:family', 'table-column'); + $this->writer->writeAttribute( + 'style:name', + sprintf('%s_%d_%d', self::COLUMN_STYLE_PREFIX, $sheetId, $columnDimension->getColumnNumeric()) + ); + + $this->writeColumnProperties($columnDimension); + + // End + $this->writer->endElement(); // Close style:style + } + + protected function writeRowProperties(RowDimension $rowDimension): void + { + $this->writer->startElement('style:table-row-properties'); + $this->writer->writeAttribute( + 'style:row-height', + round($rowDimension->getRowHeight(Dimension::UOM_CENTIMETERS), 3) . 'cm' + ); + $this->writer->writeAttribute('style:use-optimal-row-height', 'true'); + $this->writer->writeAttribute('fo:break-before', 'auto'); + + // End + $this->writer->endElement(); // Close style:table-row-properties + } + + public function writeRowStyles(RowDimension $rowDimension, int $sheetId): void + { + $this->writer->startElement('style:style'); + $this->writer->writeAttribute('style:family', 'table-row'); + $this->writer->writeAttribute( + 'style:name', + sprintf('%s_%d_%d', self::ROW_STYLE_PREFIX, $sheetId, $rowDimension->getRowIndex()) + ); + + $this->writeRowProperties($rowDimension); + + // End + $this->writer->endElement(); // Close style:style + } + public function write(CellStyle $style): void { $this->writer->startElement('style:style'); diff --git a/src/PhpSpreadsheet/Writer/Ods/Content.php b/src/PhpSpreadsheet/Writer/Ods/Content.php index a589e549..2cb31b36 100644 --- a/src/PhpSpreadsheet/Writer/Ods/Content.php +++ b/src/PhpSpreadsheet/Writer/Ods/Content.php @@ -119,14 +119,21 @@ class Content extends WriterPart { $spreadsheet = $this->getParentWriter()->getSpreadsheet(); /** @var Spreadsheet $spreadsheet */ $sheetCount = $spreadsheet->getSheetCount(); - for ($i = 0; $i < $sheetCount; ++$i) { + for ($sheetIndex = 0; $sheetIndex < $sheetCount; ++$sheetIndex) { $objWriter->startElement('table:table'); - $objWriter->writeAttribute('table:name', $spreadsheet->getSheet($i)->getTitle()); + $objWriter->writeAttribute('table:name', $spreadsheet->getSheet($sheetIndex)->getTitle()); $objWriter->writeElement('office:forms'); - $objWriter->startElement('table:table-column'); - $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX); - $objWriter->endElement(); - $this->writeRows($objWriter, $spreadsheet->getSheet($i)); + foreach ($spreadsheet->getSheet($sheetIndex)->getColumnDimensions() as $columnDimension) { + $objWriter->startElement('table:table-column'); + $objWriter->writeAttribute( + 'table:style-name', + sprintf('%s_%d_%d', Style::COLUMN_STYLE_PREFIX, $sheetIndex, $columnDimension->getColumnNumeric()) + ); + $objWriter->writeAttribute('table:default-cell-style-name', 'ce0'); +// $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX); + $objWriter->endElement(); + } + $this->writeRows($objWriter, $spreadsheet->getSheet($sheetIndex), $sheetIndex); $objWriter->endElement(); } } @@ -134,7 +141,7 @@ class Content extends WriterPart /** * Write rows of the specified sheet. */ - private function writeRows(XMLWriter $objWriter, Worksheet $sheet): void + private function writeRows(XMLWriter $objWriter, Worksheet $sheet, int $sheetIndex): void { $numberRowsRepeated = self::NUMBER_ROWS_REPEATED_MAX; $span_row = 0; @@ -148,8 +155,14 @@ class Content extends WriterPart if ($span_row > 1) { $objWriter->writeAttribute('table:number-rows-repeated', $span_row); } + if ($sheet->getRowDimension($row->getRowIndex())->getRowHeight() > 0) { + $objWriter->writeAttribute( + 'table:style_name', + sprintf('%s_%d_%d', Style::ROW_STYLE_PREFIX, $sheetIndex, $row->getRowIndex()) + ); + } $objWriter->startElement('table:table-cell'); - $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX); + $objWriter->writeAttribute('table:number-columns-repeated', (string) self::NUMBER_COLS_REPEATED_MAX); $objWriter->endElement(); $objWriter->endElement(); $span_row = 0; @@ -275,6 +288,24 @@ class Content extends WriterPart private function writeXfStyles(XMLWriter $writer, Spreadsheet $spreadsheet): void { $styleWriter = new Style($writer); + + $sheetCount = $spreadsheet->getSheetCount(); + for ($i = 0; $i < $sheetCount; ++$i) { + $worksheet = $spreadsheet->getSheet($i); + $worksheet->calculateColumnWidths(); + foreach ($worksheet->getColumnDimensions() as $columnDimension) { + $styleWriter->writeColumnStyles($columnDimension, $i); + } + } + for ($i = 0; $i < $sheetCount; ++$i) { + $worksheet = $spreadsheet->getSheet($i); + foreach ($worksheet->getRowDimensions() as $rowDimension) { + if ($rowDimension->getRowHeight() > 0.0) { + $styleWriter->writeRowStyles($rowDimension, $i); + } + } + } + foreach ($spreadsheet->getCellXfCollection() as $style) { $styleWriter->write($style); } @@ -296,7 +327,7 @@ class Content extends WriterPart $columnSpan = Coordinate::columnIndexFromString($end[0]) - Coordinate::columnIndexFromString($start[0]) + 1; $rowSpan = ((int) $end[1]) - ((int) $start[1]) + 1; - $objWriter->writeAttribute('table:number-columns-spanned', $columnSpan); - $objWriter->writeAttribute('table:number-rows-spanned', $rowSpan); + $objWriter->writeAttribute('table:number-columns-spanned', (string) $columnSpan); + $objWriter->writeAttribute('table:number-rows-spanned', (string) $rowSpan); } } diff --git a/tests/PhpSpreadsheetTests/Writer/PreCalcTest.php b/tests/PhpSpreadsheetTests/Writer/PreCalcTest.php index 2db372c4..73374e21 100644 --- a/tests/PhpSpreadsheetTests/Writer/PreCalcTest.php +++ b/tests/PhpSpreadsheetTests/Writer/PreCalcTest.php @@ -64,7 +64,7 @@ class PreCalcTest extends AbstractFunctional } } - private const AUTOSIZE_TYPES = ['Xlsx', 'Xls', 'Html']; + private const AUTOSIZE_TYPES = ['Xlsx', 'Xls', 'Html', 'Ods']; private static function verifyA3B2(Calculation $calculation, string $title, ?bool $preCalc, string $type): void { diff --git a/tests/data/Writer/Ods/content-empty.xml b/tests/data/Writer/Ods/content-empty.xml index c9620060..867cfa3e 100644 --- a/tests/data/Writer/Ods/content-empty.xml +++ b/tests/data/Writer/Ods/content-empty.xml @@ -14,7 +14,6 @@ - diff --git a/tests/data/Writer/Ods/content-with-data.xml b/tests/data/Writer/Ods/content-with-data.xml index a707d197..fff47f65 100644 --- a/tests/data/Writer/Ods/content-with-data.xml +++ b/tests/data/Writer/Ods/content-with-data.xml @@ -64,7 +64,6 @@ - 1 @@ -110,7 +109,6 @@ - 2