Removing Columns/Rows Containing Merged Cells

Fix #282, which went stale over 7 years ago, and is now reopened. This is certainly related to issue #4379. If merged cells are in the midst of deleted rows/columns, the merge may continue to exist after the deletion, leading to various problems including spreadsheet corruption. The problem is not with either Reader or Writer.

This PR will automatically unmerge all merged cells whose first cell is in the delete range before performing the deletion. This will address the corruption problem. Something more sophisticated may be required, so I won't merge this PR for a while to give me a chance to think about it some more.
This commit is contained in:
oleibman
2025-05-09 20:58:06 -07:00
parent 3922d9ad00
commit 0245f6ba22
3 changed files with 73 additions and 0 deletions
@@ -2392,6 +2392,20 @@ class Worksheet
if ($row < 1) {
throw new Exception('Rows to be deleted should at least start from row 1.');
}
$startRow = $row;
$endRow = $startRow + $numberOfRows - 1;
$removeKeys = [];
foreach ($this->mergeCells as $key => $value) {
if (preg_match('/^[a-z]{1,3}(\d+)/i', $key, $matches) === 1) {
$startMergeInt = (int) $matches[1];
if ($startMergeInt >= $startRow && $startMergeInt <= $endRow) {
$removeKeys[] = $key;
}
}
}
foreach ($removeKeys as $key) {
unset($this->mergeCells[$key]);
}
$holdRowDimensions = $this->removeRowDimensions($row, $numberOfRows);
$highestRow = $this->getHighestDataRow();
@@ -2448,6 +2462,20 @@ class Worksheet
if (is_numeric($column)) {
throw new Exception('Column references should not be numeric.');
}
$startColumnInt = Coordinate::columnIndexFromString($column);
$endColumnInt = $startColumnInt + $numberOfColumns - 1;
$removeKeys = [];
foreach ($this->mergeCells as $key => $value) {
if (preg_match('/^[a-z]{1,3}/i', $key, $matches) === 1) {
$startMergeInt = Coordinate::columnIndexFromString($matches[0]);
if ($startMergeInt >= $startColumnInt && $startMergeInt <= $endColumnInt) {
$removeKeys[] = $key;
}
}
}
foreach ($removeKeys as $key) {
unset($this->mergeCells[$key]);
}
$highestColumn = $this->getHighestDataColumn();
$highestColumnIndex = Coordinate::columnIndexFromString($highestColumn);
@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PHPUnit\Framework\TestCase;
class MergeCellsDeletedTest extends TestCase
{
public function testDeletedColumns(): void
{
$infile = 'tests/data/Reader/XLSX/issue.282.xlsx';
$reader = new XlsxReader();
$spreadsheet = $reader->load($infile);
$sheet = $spreadsheet->getSheetByName('Sheet1');
$mergeCells = $sheet->getMergeCells();
self::assertSame(['B1:F1', 'G1:I1'], array_values($mergeCells));
// Want to delete column B,C,D,E,F
$sheet->removeColumnByIndex(2, 5);
$mergeCells2 = $sheet->getMergeCells();
self::assertSame(['B1:D1'], array_values($mergeCells2));
$spreadsheet->disconnectWorksheets();
}
public function testDeletedRows(): void
{
$infile = 'tests/data/Reader/XLSX/issue.282.xlsx';
$reader = new XlsxReader();
$spreadsheet = $reader->load($infile);
$sheet = $spreadsheet->getSheetByName('Sheet2');
$mergeCells = $sheet->getMergeCells();
self::assertSame(['A2:A6', 'A7:A9'], array_values($mergeCells));
// Want to delete rows 2 to 4
$sheet->removeRow(2, 3);
$mergeCells2 = $sheet->getMergeCells();
self::assertSame(['A4:A6'], array_values($mergeCells2));
$spreadsheet->disconnectWorksheets();
}
}
Binary file not shown.