Skip to content

Commit

Permalink
Merge pull request #3358 from PHPOffice/Issue-3356_AutoFit-for-Table-…
Browse files Browse the repository at this point in the history
…AutoFilter

Allow for Table AutoFilter dropdown icon for AutoFit column sizing
MarkBaker authored Feb 12, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 545fc89 + 1bcb86c commit ff5087c
Showing 6 changed files with 192 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).

- Calculation Engine doesn't evaluate Defined Name when default cell A1 is quote-prefixed [Issue #3335](https://github.com/PHPOffice/PhpSpreadsheet/issues/3335) [PR #3336](https://github.com/PHPOffice/PhpSpreadsheet/pull/3336)
- XLSX Writer - Array Formulas do not include function prefix [Issue #3337](https://github.com/PHPOffice/PhpSpreadsheet/issues/3337) [PR #3338](https://github.com/PHPOffice/PhpSpreadsheet/pull/3338)
- AutoSize Columns should allow for dropdown icon when AutoFilter is for a Table [Issue #3356](https://github.com/PHPOffice/PhpSpreadsheet/issues/3356) [PR #3358](https://github.com/PHPOffice/PhpSpreadsheet/pull/3358)


## 1.27.1 - 2023-02-08
8 changes: 4 additions & 4 deletions src/PhpSpreadsheet/Shared/Font.php
Original file line number Diff line number Diff line change
@@ -358,7 +358,7 @@ public static function calculateColumnWidth(
?FontStyle $defaultFont = null,
bool $filterAdjustment = false,
int $indentAdjustment = 0
): int {
): float {
// If it is rich text, use plain text
if ($cellText instanceof RichText) {
$cellText = $cellText->getPlainText();
@@ -412,13 +412,13 @@ public static function calculateColumnWidth(
$columnWidth = Drawing::pixelsToCellDimension((int) $columnWidth, $defaultFont ?? new FontStyle());

// Return
return (int) round($columnWidth, 6);
return round($columnWidth, 4);
}

/**
* Get GD text width in pixels for a string of text in a certain font at a certain rotation angle.
*/
public static function getTextWidthPixelsExact(string $text, FontStyle $font, int $rotation = 0): int
public static function getTextWidthPixelsExact(string $text, FontStyle $font, int $rotation = 0): float
{
// font size should really be supplied in pixels in GD2,
// but since GD2 seems to assume 72dpi, pixels and points are the same
@@ -437,7 +437,7 @@ public static function getTextWidthPixelsExact(string $text, FontStyle $font, in
$upperLeftCornerX = $textBox[6];

// Consider the rotation when calculating the width
return max($lowerRightCornerX - $upperLeftCornerX, $upperRightCornerX - $lowerLeftCornerX);
return round(max($lowerRightCornerX - $upperLeftCornerX, $upperRightCornerX - $lowerLeftCornerX), 4);
}

/**
51 changes: 51 additions & 0 deletions src/PhpSpreadsheet/Worksheet/AutoFit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace PhpOffice\PhpSpreadsheet\Worksheet;

use PhpOffice\PhpSpreadsheet\Cell\CellAddress;
use PhpOffice\PhpSpreadsheet\Cell\CellRange;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;

class AutoFit
{
protected Worksheet $worksheet;

public function __construct(Worksheet $worksheet)
{
$this->worksheet = $worksheet;
}

public function getAutoFilterIndentRanges(): array
{
$autoFilterIndentRanges = [];
$autoFilterIndentRanges[] = $this->getAutoFilterIndentRange($this->worksheet->getAutoFilter());

foreach ($this->worksheet->getTableCollection() as $table) {
/** @var Table $table */
if ($table->getShowHeaderRow() === true && $table->getAllowFilter() === true) {
$autoFilter = $table->getAutoFilter();
if ($autoFilter !== null) {
$autoFilterIndentRanges[] = $this->getAutoFilterIndentRange($autoFilter);
}
}
}

return array_filter($autoFilterIndentRanges);
}

private function getAutoFilterIndentRange(AutoFilter $autoFilter): ?string
{
$autoFilterRange = $autoFilter->getRange();
$autoFilterIndentRange = null;

if (!empty($autoFilterRange)) {
$autoFilterRangeBoundaries = Coordinate::rangeBoundaries($autoFilterRange);
$autoFilterIndentRange = (string) new CellRange(
CellAddress::fromColumnAndRow($autoFilterRangeBoundaries[0][0], $autoFilterRangeBoundaries[0][1]),
CellAddress::fromColumnAndRow($autoFilterRangeBoundaries[1][0], $autoFilterRangeBoundaries[0][1])
);
}

return $autoFilterIndentRange;
}
}
40 changes: 21 additions & 19 deletions src/PhpSpreadsheet/Worksheet/Worksheet.php
Original file line number Diff line number Diff line change
@@ -742,14 +742,7 @@ public function calculateColumnWidths()
}
}

$autoFilterRange = $autoFilterFirstRowRange = $this->autoFilter->getRange();
if (!empty($autoFilterRange)) {
$autoFilterRangeBoundaries = Coordinate::rangeBoundaries($autoFilterRange);
$autoFilterFirstRowRange = (string) new CellRange(
CellAddress::fromColumnAndRow($autoFilterRangeBoundaries[0][0], $autoFilterRangeBoundaries[0][1]),
CellAddress::fromColumnAndRow($autoFilterRangeBoundaries[1][0], $autoFilterRangeBoundaries[0][1])
);
}
$autoFilterIndentRanges = (new AutoFit($this))->getAutoFilterIndentRanges();

// loop through all cells in the worksheet
foreach ($this->getCoordinates(false) as $coordinate) {
@@ -776,8 +769,14 @@ public function calculateColumnWidths()
// Determine if we need to make an adjustment for the first row in an AutoFilter range that
// has a column filter dropdown
$filterAdjustment = false;
if (!empty($autoFilterRange) && $cell->isInRange($autoFilterFirstRowRange)) {
$filterAdjustment = true;
if (!empty($autoFilterIndentRanges)) {
foreach ($autoFilterIndentRanges as $autoFilterFirstRowRange) {
if ($cell->isInRange($autoFilterFirstRowRange)) {
$filterAdjustment = true;

break;
}
}
}

$indentAdjustment = $cell->getStyle()->getAlignment()->getIndent();
@@ -792,15 +791,18 @@ public function calculateColumnWidths()

if ($cellValue !== null && $cellValue !== '') {
$autoSizes[$this->cellCollection->getCurrentColumn()] = max(
(float) $autoSizes[$this->cellCollection->getCurrentColumn()],
(float) Shared\Font::calculateColumnWidth(
$this->getParentOrThrow()->getCellXfByIndex($cell->getXfIndex())->getFont(),
$cellValue,
(int) $this->getParentOrThrow()->getCellXfByIndex($cell->getXfIndex())
->getAlignment()->getTextRotation(),
$this->getParentOrThrow()->getDefaultStyle()->getFont(),
$filterAdjustment,
$indentAdjustment
$autoSizes[$this->cellCollection->getCurrentColumn()],
round(
Shared\Font::calculateColumnWidth(
$this->getParentOrThrow()->getCellXfByIndex($cell->getXfIndex())->getFont(),
$cellValue,
(int) $this->getParentOrThrow()->getCellXfByIndex($cell->getXfIndex())
->getAlignment()->getTextRotation(),
$this->getParentOrThrow()->getDefaultStyle()->getFont(),
$filterAdjustment,
$indentAdjustment
),
3
)
);
}
20 changes: 10 additions & 10 deletions tests/PhpSpreadsheetTests/Shared/FontTest.php
Original file line number Diff line number Diff line change
@@ -106,7 +106,7 @@ public function testVerdanaRotation(): void
* @dataProvider providerCalculateApproximateColumnWidth
*/
public function testCalculateApproximateColumnWidth(
int $expectedWidth,
float $expectedWidth,
StyleFont $font,
string $text,
int $rotation,
@@ -121,15 +121,15 @@ public function testCalculateApproximateColumnWidth(
public function providerCalculateApproximateColumnWidth(): array
{
return [
[13, new StyleFont(), 'Hello World', 0, new StyleFont(), false, 0],
[16, new StyleFont(), 'Hello World', 0, new StyleFont(), true, 0],
[16, new StyleFont(), 'Hello World', 0, new StyleFont(), false, 1],
[18, new StyleFont(), 'Hello World', 0, new StyleFont(), false, 2],
[20, new StyleFont(), 'Hello World', 0, new StyleFont(), false, 3],
[6, new StyleFont(), "Hello\nWorld", 0, new StyleFont(), false, 0],
[9, new StyleFont(), "Hello\nWorld", 0, new StyleFont(), true, 0],
[17, new StyleFont(), 'PhpSpreadsheet', 0, new StyleFont(), false, 0],
[19, new StyleFont(), 'PhpSpreadsheet', 0, new StyleFont(), false, 1],
[13.9966, new StyleFont(), 'Hello World', 0, new StyleFont(), false, 0],
[16.2817, new StyleFont(), 'Hello World', 0, new StyleFont(), true, 0],
[16.2817, new StyleFont(), 'Hello World', 0, new StyleFont(), false, 1],
[18.7097, new StyleFont(), 'Hello World', 0, new StyleFont(), false, 2],
[20.9949, new StyleFont(), 'Hello World', 0, new StyleFont(), false, 3],
[6.9983, new StyleFont(), "Hello\nWorld", 0, new StyleFont(), false, 0],
[9.2834, new StyleFont(), "Hello\nWorld", 0, new StyleFont(), true, 0],
[17.5671, new StyleFont(), 'PhpSpreadsheet', 0, new StyleFont(), false, 0],
[19.8523, new StyleFont(), 'PhpSpreadsheet', 0, new StyleFont(), false, 1],
];
}
}
105 changes: 105 additions & 0 deletions tests/PhpSpreadsheetTests/Worksheet/AutoSizeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

namespace PhpOffice\PhpSpreadsheetTests\Worksheet;

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
use PhpOffice\PhpSpreadsheet\Worksheet\Table\TableStyle;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;

class AutoSizeTest extends TestCase
{
protected Spreadsheet $spreadsheet;

protected Worksheet $worksheet;

protected function setUp(): void
{
parent::setUp();

$spreadsheet = new Spreadsheet();
$this->worksheet = $spreadsheet->getActiveSheet();

$this->worksheet->setCellValue('A1', 'YEAR')
->setCellValue('B1', 'QUARTER')
->setCellValue('C1', 'COUNTRY')
->setCellValue('D1', 'SALES');
$dataArray = [
['10', 'Q1', 'United States', 790],
['10', 'Q2', 'United States', 730],
['10', 'Q3', 'United States', 860],
['10', 'Q4', 'United States', 850],
];

$this->worksheet->fromArray($dataArray, null, 'A2');

$toColumn = $this->worksheet->getHighestColumn();
++$toColumn;
for ($i = 'A'; $i !== $toColumn; ++$i) {
$this->worksheet->getColumnDimension($i)->setAutoSize(true);
}
}

protected function setTable(): Table
{
$table = new Table('A1:D5', 'Sales_Data');
$tableStyle = new TableStyle();
$tableStyle->setTheme(TableStyle::TABLE_STYLE_MEDIUM2);
$table->setStyle($tableStyle);
$this->worksheet->addTable($table);

return $table;
}

protected function readColumnSizes(): array
{
$columnSizes = [];
$toColumn = $this->worksheet->getHighestColumn();
++$toColumn;
for ($column = 'A'; $column !== $toColumn; ++$column) {
$columnSizes[$column] = $this->worksheet->getColumnDimension($column)->getWidth();
}

return $columnSizes;
}

public function testStandardAutoSize(): void
{
$this->worksheet->calculateColumnWidths();
$columnSizes = $this->readColumnSizes();

self::assertSame(['A' => 5.856, 'B' => 9.283, 'C' => 16.425, 'D' => 6.998], $columnSizes);
}

public function testAutoFilterAutoSize(): void
{
$this->worksheet->setAutoFilter('A1:D5');

$this->worksheet->calculateColumnWidths();
$columnSizes = $this->readColumnSizes();

self::assertSame(['A' => 8.141, 'B' => 11.569, 'C' => 16.425, 'D' => 9.283], $columnSizes);
}

public function testTableWithAutoFilterAutoSize(): void
{
$this->setTable();

$this->worksheet->calculateColumnWidths();
$columnSizes = $this->readColumnSizes();

self::assertSame(['A' => 8.141, 'B' => 11.569, 'C' => 16.425, 'D' => 9.283], $columnSizes);
}

public function testTableWithoutHiddenHeadersAutoSize(): void
{
$table = $this->setTable();
$table->setShowHeaderRow(false);

$this->worksheet->calculateColumnWidths();
$columnSizes = $this->readColumnSizes();

self::assertSame(['A' => 5.856, 'B' => 9.283, 'C' => 16.425, 'D' => 6.998], $columnSizes);
}
}

0 comments on commit ff5087c

Please sign in to comment.