Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). Thia is a
- Minor improvements to Calculation coverage. [PR #4624](https://github.com/PHPOffice/PhpSpreadsheet/pull/4624)
- Conditional formatting in extLst. [Issue #4629](https://github.com/PHPOffice/PhpSpreadsheet/issues/4629) [PR #4633](https://github.com/PHPOffice/PhpSpreadsheet/pull/4633)
- Php8.5 deprecates use of null as array index. [PR #4634](https://github.com/PHPOffice/PhpSpreadsheet/pull/4634)
- Wrapped cells and default row height. [Issue #4584](https://github.com/PHPOffice/PhpSpreadsheet/issues/4584) [PR #4645](https://github.com/PHPOffice/PhpSpreadsheet/pull/4645)
- For Php8.5, replace one of our two uses of `__wakeup` with `__unserialize`, and eliminate the other. [PR #4639](https://github.com/PHPOffice/PhpSpreadsheet/pull/4639)
- Use prefix _xlfn for BASE function. [Issue #4638](https://github.com/PHPOffice/PhpSpreadsheet/issues/4638) [PR #4641](https://github.com/PHPOffice/PhpSpreadsheet/pull/4641)
- Additional support for union and intersection. [PR #4596](https://github.com/PHPOffice/PhpSpreadsheet/pull/4596)
Expand Down
9 changes: 8 additions & 1 deletion src/PhpSpreadsheet/Reader/Xls.php
Original file line number Diff line number Diff line change
Expand Up @@ -2647,7 +2647,14 @@ protected function readRow(): void
$useDefaultHeight = (0x8000 & self::getUInt2d($recordData, 6)) >> 15;

if (!$useDefaultHeight) {
$this->phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20);
if (
$this->phpSheet->getDefaultRowDimension()->getRowHeight() > 0
) {
$this->phpSheet->getRowDimension($r + 1)
->setCustomFormat(true, ($height === 255) ? -1 : ($height / 20));
} else {
$this->phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20);
}
}

// offset: 8; size: 2; not used
Expand Down
61 changes: 38 additions & 23 deletions src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,32 +50,40 @@ private function setColumnAttributes(string $columnAddress, array $columnAttribu
* Set Worksheet row attributes by attributes array passed.
*
* @param int $rowNumber 1, 2, 3, ... 99, ...
* @param array{xfIndex?: int, visible?: bool, collapsed?: bool, collapsed?: bool, outlineLevel?: int, rowHeight?: float} $rowAttributes array of attributes (indexes are attribute name, values are value)
* @param array{xfIndex?: int, visible?: bool, collapsed?: bool, collapsed?: bool, outlineLevel?: int, rowHeight?: float, customFormat?: bool, ht?: float} $rowAttributes array of attributes (indexes are attribute name, values are value)
* 'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'rowHeight', ... ?
*/
private function setRowAttributes(int $rowNumber, array $rowAttributes): void
{
if (isset($rowAttributes['xfIndex'])) {
$this->worksheet->getRowDimension($rowNumber)->setXfIndex($rowAttributes['xfIndex']);
$this->worksheet->getRowDimension($rowNumber)
->setXfIndex($rowAttributes['xfIndex']);
}
if (isset($rowAttributes['visible'])) {
$this->worksheet->getRowDimension($rowNumber)->setVisible($rowAttributes['visible']);
$this->worksheet->getRowDimension($rowNumber)
->setVisible($rowAttributes['visible']);
}
if (isset($rowAttributes['collapsed'])) {
$this->worksheet->getRowDimension($rowNumber)->setCollapsed($rowAttributes['collapsed']);
$this->worksheet->getRowDimension($rowNumber)
->setCollapsed($rowAttributes['collapsed']);
}
if (isset($rowAttributes['outlineLevel'])) {
$this->worksheet->getRowDimension($rowNumber)->setOutlineLevel($rowAttributes['outlineLevel']);
$this->worksheet->getRowDimension($rowNumber)
->setOutlineLevel($rowAttributes['outlineLevel']);
}
if (isset($rowAttributes['rowHeight'])) {
$this->worksheet->getRowDimension($rowNumber)->setRowHeight($rowAttributes['rowHeight']);
if (isset($rowAttributes['customFormat'], $rowAttributes['rowHeight'])) {
$this->worksheet->getRowDimension($rowNumber)
->setCustomFormat($rowAttributes['customFormat'], $rowAttributes['rowHeight']);
} elseif (isset($rowAttributes['rowHeight'])) {
$this->worksheet->getRowDimension($rowNumber)
->setRowHeight($rowAttributes['rowHeight']);
}
}

public function load(?IReadFilter $readFilter = null, bool $readDataOnly = false, bool $ignoreRowsWithNoCells = false): void
public function load(?IReadFilter $readFilter = null, bool $readDataOnly = false, bool $ignoreRowsWithNoCells = false): bool
{
if ($this->worksheetXml === null) {
return;
return false;
}
if ($readFilter !== null && $readFilter::class === DefaultReadFilter::class) {
$readFilter = null;
Expand Down Expand Up @@ -119,6 +127,8 @@ public function load(?IReadFilter $readFilter = null, bool $readDataOnly = false
}
}
}

return true;
}

/** @param mixed[] $rowsAttributes */
Expand Down Expand Up @@ -203,20 +213,25 @@ private function readRowAttributes(SimpleXMLElement $worksheetRow, bool $readDat
$row = $rowx->attributes();
if ($row !== null && (!$ignoreRowsWithNoCells || isset($rowx->c))) {
$rowIndex = (int) $row['r'];
if (isset($row['ht']) && !$readDataOnly) {
$rowAttributes[$rowIndex]['rowHeight'] = (float) $row['ht'];
}
if (isset($row['hidden']) && self::boolean($row['hidden'])) {
$rowAttributes[$rowIndex]['visible'] = false;
}
if (isset($row['collapsed']) && self::boolean($row['collapsed'])) {
$rowAttributes[$rowIndex]['collapsed'] = true;
}
if (isset($row['outlineLevel']) && (int) $row['outlineLevel'] > 0) {
$rowAttributes[$rowIndex]['outlineLevel'] = (int) $row['outlineLevel'];
}
if (isset($row['s']) && !$readDataOnly) {
$rowAttributes[$rowIndex]['xfIndex'] = (int) $row['s'];
if (!$readDataOnly) {
if (isset($row['ht'])) {
$rowAttributes[$rowIndex]['rowHeight'] = (float) $row['ht'];
}
if (isset($row['customFormat']) && self::boolean($row['customFormat'])) {
$rowAttributes[$rowIndex]['customFormat'] = true;
}
if (isset($row['hidden']) && self::boolean($row['hidden'])) {
$rowAttributes[$rowIndex]['visible'] = false;
}
if (isset($row['collapsed']) && self::boolean($row['collapsed'])) {
$rowAttributes[$rowIndex]['collapsed'] = true;
}
if (isset($row['outlineLevel']) && (int) $row['outlineLevel'] > 0) {
$rowAttributes[$rowIndex]['outlineLevel'] = (int) $row['outlineLevel'];
}
if (isset($row['s'])) {
$rowAttributes[$rowIndex]['xfIndex'] = (int) $row['s'];
}
}
if ($readFilterIsNotNull && empty($rowAttributes[$rowIndex])) {
$rowAttributes[$rowIndex]['exists'] = true;
Expand Down
41 changes: 18 additions & 23 deletions src/PhpSpreadsheet/Worksheet/RowDimension.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@

class RowDimension extends Dimension
{
/**
* Row index.
*/
private ?int $rowIndex;

/**
Expand All @@ -23,9 +20,9 @@ class RowDimension extends Dimension
*/
private bool $zeroHeight = false;

private bool $customFormat = false;

/**
* Create a new RowDimension.
*
* @param ?int $index Numeric row index
*/
public function __construct(?int $index = 0)
Expand All @@ -37,19 +34,11 @@ public function __construct(?int $index = 0)
parent::__construct(null);
}

/**
* Get Row Index.
*/
public function getRowIndex(): ?int
{
return $this->rowIndex;
}

/**
* Set Row Index.
*
* @return $this
*/
public function setRowIndex(int $index): static
{
$this->rowIndex = $index;
Expand All @@ -76,35 +65,41 @@ public function getRowHeight(?string $unitOfMeasure = null): float
* @param float $height in points. A value of -1 tells Excel to display this column in its default height.
* By default, this will be the passed argument value; but this method also accepts an optional unit of measure
* argument, and will convert the passed argument value to points from the specified UoM
*
* @return $this
*/
public function setRowHeight(float $height, ?string $unitOfMeasure = null): static
{
$this->height = ($unitOfMeasure === null || $height < 0)
? $height
: (new CssDimension("{$height}{$unitOfMeasure}"))->height();
$this->customFormat = false;

return $this;
}

/**
* Get ZeroHeight.
*/
public function getZeroHeight(): bool
{
return $this->zeroHeight;
}

/**
* Set ZeroHeight.
*
* @return $this
*/
public function setZeroHeight(bool $zeroHeight): static
{
$this->zeroHeight = $zeroHeight;

return $this;
}

public function getCustomFormat(): bool
{
return $this->customFormat;
}

public function setCustomFormat(bool $customFormat, ?float $height = -1): self
{
$this->customFormat = $customFormat;
if ($height !== null) {
$this->height = $height;
}

return $this;
}
}
15 changes: 15 additions & 0 deletions src/PhpSpreadsheet/Writer/Ods/Cell/Style.php
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,21 @@ public function writeRowStyles(RowDimension $rowDimension, int $sheetId): void
$this->writer->endElement(); // Close style:style
}

public function writeDefaultRowStyle(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', self::ROW_STYLE_PREFIX, $sheetId)
);

$this->writeRowProperties($rowDimension);

// End
$this->writer->endElement(); // Close style:style
}

public function writeTableStyle(Worksheet $worksheet, int $sheetId): void
{
$this->writer->startElement('style:style');
Expand Down
9 changes: 9 additions & 0 deletions src/PhpSpreadsheet/Writer/Ods/Content.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ private function writeRows(XMLWriter $objWriter, Worksheet $sheet, int $sheetInd
'table:style-name',
sprintf('%s_%d_%d', Style::ROW_STYLE_PREFIX, $sheetIndex, $row->getRowIndex())
);
} elseif ($sheet->getDefaultRowDimension()->getRowHeight() > 0.0 && !$sheet->getRowDimension($row->getRowIndex())->getCustomFormat()) {
$objWriter->writeAttribute(
'table:style-name',
sprintf('%s%d', Style::ROW_STYLE_PREFIX, $sheetIndex)
);
}
$this->writeCells($objWriter, $cellIterator);
$objWriter->endElement();
Expand Down Expand Up @@ -323,6 +328,10 @@ private function writeXfStyles(XMLWriter $writer, Spreadsheet $spreadsheet): voi
}
for ($i = 0; $i < $sheetCount; ++$i) {
$worksheet = $spreadsheet->getSheet($i);
$default = $worksheet->getDefaultRowDimension();
if ($default->getRowHeight() > 0.0) {
$styleWriter->writeDefaultRowStyle($default, $i);
}
foreach ($worksheet->getRowDimensions() as $rowDimension) {
if ($rowDimension->getRowHeight() > 0.0) {
$styleWriter->writeRowStyles($rowDimension, $i);
Expand Down
21 changes: 20 additions & 1 deletion src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -1398,6 +1398,16 @@ private function writeSheetData(XMLWriter $objWriter, PhpspreadsheetWorksheet $w
}
}

$customHeightNeeded = false;
if ($worksheet->getDefaultRowDimension()->getRowHeight() >= 0) {
foreach ($worksheet->getRowDimensions() as $rowDimension) {
if ($rowDimension->getCustomFormat()) {
$customHeightNeeded = true;

break;
}
}
}
$currentRow = 0;
$emptyDimension = new RowDimension();
while ($currentRow++ < $highestRow) {
Expand All @@ -1411,6 +1421,7 @@ private function writeSheetData(XMLWriter $objWriter, PhpspreadsheetWorksheet $w

if ($writeCurrentRow) {
// Start a new row
$customFormatWritten = false;
$objWriter->startElement('row');
$objWriter->writeAttribute('r', "$currentRow");
$objWriter->writeAttribute('spans', '1:' . $colCount);
Expand All @@ -1419,6 +1430,12 @@ private function writeSheetData(XMLWriter $objWriter, PhpspreadsheetWorksheet $w
if ($rowDimension->getRowHeight() >= 0) {
$objWriter->writeAttribute('customHeight', '1');
$objWriter->writeAttribute('ht', StringHelper::formatNumber($rowDimension->getRowHeight()));
} elseif ($rowDimension->getCustomFormat()) {
$objWriter->writeAttribute('customFormat', '1');
$customFormatWritten = true;
$objWriter->writeAttribute('ht', StringHelper::formatNumber($rowDimension->getRowHeight()));
} elseif ($customHeightNeeded) {
$objWriter->writeAttribute('customHeight', '1');
}

// Row visibility
Expand All @@ -1439,7 +1456,9 @@ private function writeSheetData(XMLWriter $objWriter, PhpspreadsheetWorksheet $w
// Style
if ($rowDimension->getXfIndex() !== null) {
$objWriter->writeAttribute('s', (string) $rowDimension->getXfIndex());
$objWriter->writeAttribute('customFormat', '1');
if (!$customFormatWritten) {
$objWriter->writeAttribute('customFormat', '1');
}
}

// Write cells
Expand Down
39 changes: 39 additions & 0 deletions tests/PhpSpreadsheetTests/Reader/Xlsx/OutlineTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;

use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
use PHPUnit\Framework\TestCase;

class OutlineTest extends TestCase
{
public function testOutline(): void
{
$filename = 'tests/data/Reader/XLSX/outline.xlsx';
$reader = new XlsxReader();
$spreadsheet = $reader->load($filename);
$sheet = $spreadsheet->getActiveSheet();
self::assertSame(0, $sheet->getRowDimension(1)->getOutlineLevel());
self::assertSame(2, $sheet->getRowDimension(2)->getOutlineLevel());
self::assertFalse($sheet->getRowDimension(2)->getCollapsed());
self::assertTrue($sheet->getRowDimension(2)->getVisible());
self::assertSame('=SUBTOTAL(9,B2:B4)', $sheet->getCell('B5')->getValue());
self::assertSame(2, $sheet->getRowDimension(6)->getOutlineLevel());
self::assertFalse($sheet->getRowDimension(6)->getCollapsed());
self::assertFalse($sheet->getRowDimension(6)->getVisible());
self::assertSame(1, $sheet->getRowDimension(8)->getOutlineLevel());
self::assertTrue($sheet->getRowDimension(8)->getCollapsed());
self::assertTrue($sheet->getRowDimension(8)->getVisible());
$fake = new XlsxReader\ColumnAndRowAttributes($sheet, null);
self::assertFalse($fake->load());
$writer = new XlsxWriter($spreadsheet);
$writerWorksheet = new XlsxWriter\Worksheet($writer);
$data = $writerWorksheet->writeWorksheet($sheet, []);
self::assertStringContainsString('<row r="7" spans="1:2" hidden="true" outlineLevel="2">', $data);
self::assertStringContainsString('<row r="8" spans="1:2" collapsed="true" outlineLevel="1">', $data);
$spreadsheet->disconnectWorksheets();
}
}
44 changes: 44 additions & 0 deletions tests/PhpSpreadsheetTests/Writer/Ods/Issue4584Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Writer\Ods;

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Ods as OdsWriter;
use PHPUnit\Framework\TestCase;

class Issue4584Test extends TestCase
{
public function testWriteRowDimensions(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getDefaultRowDimension()->setRowHeight(20);
$sheet->setCellValue('A1', 'hello there world 1');
$sheet->getStyle('A1')->getAlignment()->setWrapText(true);
$sheet->getRowDimension(1)->setCustomFormat(true);
$sheet->setCellValue('A2', 'hello there world 2');
$sheet->setCellValue('A4', 'hello there world 4');
$writer = new OdsWriter($spreadsheet);
$writerWorksheet = new OdsWriter\Content($writer);
$data = $writerWorksheet->write();
self::assertStringContainsString(
'<style:style style:family="table-row" style:name="ro0"><style:table-row-properties style:row-height="0.706cm" style:use-optimal-row-height="false" fo:break-before="auto"/></style:style>',
$data
);
self::assertStringContainsString(
'<table:table-row><table:table-cell table:style-name="ce1" office:value-type="string"><text:p>hello there world 1</text:p></table:table-cell></table:table-row>',
$data
);
self::assertStringContainsString(
'<table:table-row table:style-name="ro0"><table:table-cell table:style-name="ce0" office:value-type="string"><text:p>hello there world 2</text:p></table:table-cell></table:table-row>',
$data
);
self::assertStringContainsString(
'<table:table-row table:number-rows-repeated="1"/><table:table-row table:style-name="ro0"><table:table-cell table:style-name="ce0" office:value-type="string"><text:p>hello there world 4</text:p></table:table-cell></table:table-row>',
$data
);
$spreadsheet->disconnectWorksheets();
}
}
Loading