Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for autofitting the rows height #333

Closed
leandrogm opened this issue Jan 11, 2018 · 6 comments
Closed

Support for autofitting the rows height #333

leandrogm opened this issue Jan 11, 2018 · 6 comments
Labels

Comments

@leandrogm
Copy link

This is:

- [ ] a bug report
- [X] a feature request
- [X] **not** a usage question (ask them on https://stackoverflow.com/questions/tagged/phpspreadsheet or https://gitter.im/PHPOffice/PhpSpreadsheet)

What is the expected behavior?

The lib should be able to have a function that autofits the rows based on the largest cell. I couldn't find any place to do it.

After struggling with this issue I came up with a solution that I would like to share. That's why i'm creating this issue.

What is the current behavior?

I'm not sure if it only happens on Libre Office Calc but if I have a text that has been wrapped before using getAlignment()->setWrapText(true) I'm was not able to fit the rows based on the new height of the cells, like when we double click excel/calc rows.

Which versions of PhpSpreadsheet and PHP are affected?

"phpoffice/phpspreadsheet": "1.0.0-beta",
"php": ">=5.6.4"

leandrogm pushed a commit to leandrogm/PhpSpreadsheet that referenced this issue Jan 11, 2018
leandrogm pushed a commit to leandrogm/PhpSpreadsheet that referenced this issue Jan 11, 2018
@leandrogm leandrogm changed the title [feature request] - Autofit row height Support for autofitting the rows height Jan 23, 2018
@stale
Copy link

stale bot commented Mar 24, 2018

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
If this is still an issue for you, please try to help by debugging it further and sharing your results.
Thank you for your contributions.

@stale stale bot added the stale label Mar 24, 2018
@stale stale bot closed this as completed Mar 31, 2018
@ianef
Copy link

ianef commented May 2, 2018

Hi leandrodm. A good start but there are some errors in you code and it doesn't take into account merged cellls.

Here is a version of your method that can be placed into the client code and does not require your fork to implement the function.

Rather than use a row range, I've gone for performing the action on a single row but this could be adapted to your methodology easily. To get the row you need to do the following:

use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Row;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
...
    const ROW_PADDING = 5;
    const DEFAULT_CELL_WIDTH = 9.14;
    const DEFAULT_ROW_HEIGHT = 15;
...
            $row = new Row($myWorksheet, $myRowNum);
            $this->autofitRowHeight($row);

    /**
     * Auto-fit the row height based on the largest cell
     *
     * @param Row $start
     * @return Worksheet
     */
    public function autofitRowHeight(Row $row, $rowPadding = self::ROW_PADDING)
    {
        $ws = $row->getWorksheet();
        $cellIterator = $row->getCellIterator();
        $cellIterator->setIterateOnlyExistingCells(true);

        $maxCellLines = 1;
        /* @var $cell Cell */
        foreach ($cellIterator as $cell) {
            $cellLength = strlen($cell->getValue());
            $cellWidth = $ws->getColumnDimension($cell->getParent()->getCurrentColumn())->getWidth();
            // If no column width is set, set the default
            if($cellWidth === -1) {
                $ws->getColumnDimension($cell->getParent()->getCurrentColumn())->setWidth(self::DEFAULT_CELL_WIDTH);
                $cellWidth = $ws->getColumnDimension($cell->getParent()->getCurrentColumn())->getWidth();
            }
            // If the cell is in a merge range we need to determine the full width of the range
            if($cell->isInMergeRange()) {
                // We only need to do this for the master (first) cell in the range, the rest need to have a line height of 1
                if($cell->isMergeRangeValueCell()) {
                    $mergeRange = $cell->getMergeRange();
                    if($mergeRange) {
                        $mergeWidth = 0;
                        $mergeRefs = Coordinate::extractAllCellReferencesInRange($mergeRange);
                        foreach($mergeRefs as $cellRef) {
                            $mergeCell = $ws->getCell($cellRef);
                            $width = $ws->getColumnDimension($mergeCell->getParent()->getCurrentColumn())->getWidth();
                            if($width === -1) {
                                $ws->getColumnDimension($mergeCell->getParent()->getCurrentColumn())->setWidth(self::DEFAULT_CELL_WIDTH);
                                $width = $ws->getColumnDimension($mergeCell->getParent()->getCurrentColumn())->getWidth();
                            }
                            $mergeWidth += $width;
                        }
                        $cellWidth = $mergeWidth;
                    } else {
                        $cellWidth = 1;
                    }
                } else {
                    $cellWidth = 1;
                }
            }

            // Calculate the number of cell lines with a 10% additional margin
            $cellLines = ceil(($cellLength * 1.1) / $cellWidth);
            $maxCellLines = $cellLines > $maxCellLines ? $cellLines : $maxCellLines;
        }

        $rowDimension= $ws->getRowDimension($row->getRowIndex());
        $rowHeight = $rowDimension->getRowHeight();
        // If no row height is set, set the default
        if($rowHeight === -1) {
            $rowDimension->setRowHeight(self::DEFAULT_ROW_HEIGHT);
            $rowHeight = $rowDimension->getRowHeight();
        }

        $rowLines = $maxCellLines <= 0 ? 1 : $maxCellLines;

        $rowDimension->setRowHeight( ($rowHeight * $rowLines) + $rowPadding);

        return $ws;
    }

@gitrequests
Copy link

Hi leandrodm. A good start but there are some errors in you code and it doesn't take into account merged cellls.

Here is a version of your method that can be placed into the client code and does not require your fork to implement the function.

Rather than use a row range, I've gone for performing the action on a single row but this could be adapted to your methodology easily. To get the row you need to do the following:

use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Row;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
...
    const ROW_PADDING = 5;
    const DEFAULT_CELL_WIDTH = 9.14;
    const DEFAULT_ROW_HEIGHT = 15;
...
            $row = new Row($myWorksheet, $myRowNum);
            $this->autofitRowHeight($row);
    /**
     * Auto-fit the row height based on the largest cell
     *
     * @param Row $start
     * @return Worksheet
     */
    public function autofitRowHeight(Row $row, $rowPadding = self::ROW_PADDING)
    {
        $ws = $row->getWorksheet();
        $cellIterator = $row->getCellIterator();
        $cellIterator->setIterateOnlyExistingCells(true);

        $maxCellLines = 1;
        /* @var $cell Cell */
        foreach ($cellIterator as $cell) {
            $cellLength = strlen($cell->getValue());
            $cellWidth = $ws->getColumnDimension($cell->getParent()->getCurrentColumn())->getWidth();
            // If no column width is set, set the default
            if($cellWidth === -1) {
                $ws->getColumnDimension($cell->getParent()->getCurrentColumn())->setWidth(self::DEFAULT_CELL_WIDTH);
                $cellWidth = $ws->getColumnDimension($cell->getParent()->getCurrentColumn())->getWidth();
            }
            // If the cell is in a merge range we need to determine the full width of the range
            if($cell->isInMergeRange()) {
                // We only need to do this for the master (first) cell in the range, the rest need to have a line height of 1
                if($cell->isMergeRangeValueCell()) {
                    $mergeRange = $cell->getMergeRange();
                    if($mergeRange) {
                        $mergeWidth = 0;
                        $mergeRefs = Coordinate::extractAllCellReferencesInRange($mergeRange);
                        foreach($mergeRefs as $cellRef) {
                            $mergeCell = $ws->getCell($cellRef);
                            $width = $ws->getColumnDimension($mergeCell->getParent()->getCurrentColumn())->getWidth();
                            if($width === -1) {
                                $ws->getColumnDimension($mergeCell->getParent()->getCurrentColumn())->setWidth(self::DEFAULT_CELL_WIDTH);
                                $width = $ws->getColumnDimension($mergeCell->getParent()->getCurrentColumn())->getWidth();
                            }
                            $mergeWidth += $width;
                        }
                        $cellWidth = $mergeWidth;
                    } else {
                        $cellWidth = 1;
                    }
                } else {
                    $cellWidth = 1;
                }
            }

            // Calculate the number of cell lines with a 10% additional margin
            $cellLines = ceil(($cellLength * 1.1) / $cellWidth);
            $maxCellLines = $cellLines > $maxCellLines ? $cellLines : $maxCellLines;
        }

        $rowDimension= $ws->getRowDimension($row->getRowIndex());
        $rowHeight = $rowDimension->getRowHeight();
        // If no row height is set, set the default
        if($rowHeight === -1) {
            $rowDimension->setRowHeight(self::DEFAULT_ROW_HEIGHT);
            $rowHeight = $rowDimension->getRowHeight();
        }

        $rowLines = $maxCellLines <= 0 ? 1 : $maxCellLines;

        $rowDimension->setRowHeight( ($rowHeight * $rowLines) + $rowPadding);

        return $ws;
    }

Does this code work with line breaks?

@fbudin
Copy link

fbudin commented Jul 7, 2019

Hi,
I used a light version of this method to handle line breaks, but it assumes columns widths are corrects berforehand.
https://pastebin.com/ixTvfyBg

@Collie-IT
Copy link
Contributor

Collie-IT commented Jul 18, 2019

In the Code from @ianfoulds is an little issue on line 61 by using the generated template again. The $width will added at every use again and again.

      $rowDimension->setRowHeight( ($rowHeight * $rowLines) + $rowPadding);

Change the code to

 $rowDimension->setRowHeight( (DEFAULT_ROW_HEIGHT * $rowLines) + $rowPadding);

@schmidtl4
Copy link

I can't see that this has been addressed. Am I right? It's 2024.
I am trying to auto adjust the height of merged cells to the height necessary to accommodate all of the text set by a "setCellValue" method. Like this:
$sheet->setCellValue("E{$msgRow}", "a very very long message...." );
$sheet->getStyle("E{$msgRow}")->getFont()->setSize(10);
$range = "E{$msgRow}:J{$msgRow}";
$sheet->mergeCells($range);
$sheet->getStyle($range)->getAlignment()->setWrapText(true);

To force all rows in the spreadsheet to auto-adjust to the height of the text, I use:
$highestRow = $analysisSheet->getHighestRow();
for ($row = 1; $row <= $highestRow; $row++) {
$sheet->getRowDimension($row)->setRowHeight(-1);
}
But it doesn't have any effect on the merge cell range... What am I missing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

6 participants