Skip to content

Commit 0245f6b

Browse files
committed
Removing Columns/Rows Containing Merged Cells
Fix PHPOffice#282, which went stale over 7 years ago, and is now reopened. This is certainly related to issue PHPOffice#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.
1 parent 3922d9a commit 0245f6b

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed

src/PhpSpreadsheet/Worksheet/Worksheet.php

+28
Original file line numberDiff line numberDiff line change
@@ -2392,6 +2392,20 @@ public function removeRow(int $row, int $numberOfRows = 1): static
23922392
if ($row < 1) {
23932393
throw new Exception('Rows to be deleted should at least start from row 1.');
23942394
}
2395+
$startRow = $row;
2396+
$endRow = $startRow + $numberOfRows - 1;
2397+
$removeKeys = [];
2398+
foreach ($this->mergeCells as $key => $value) {
2399+
if (preg_match('/^[a-z]{1,3}(\d+)/i', $key, $matches) === 1) {
2400+
$startMergeInt = (int) $matches[1];
2401+
if ($startMergeInt >= $startRow && $startMergeInt <= $endRow) {
2402+
$removeKeys[] = $key;
2403+
}
2404+
}
2405+
}
2406+
foreach ($removeKeys as $key) {
2407+
unset($this->mergeCells[$key]);
2408+
}
23952409

23962410
$holdRowDimensions = $this->removeRowDimensions($row, $numberOfRows);
23972411
$highestRow = $this->getHighestDataRow();
@@ -2448,6 +2462,20 @@ public function removeColumn(string $column, int $numberOfColumns = 1): static
24482462
if (is_numeric($column)) {
24492463
throw new Exception('Column references should not be numeric.');
24502464
}
2465+
$startColumnInt = Coordinate::columnIndexFromString($column);
2466+
$endColumnInt = $startColumnInt + $numberOfColumns - 1;
2467+
$removeKeys = [];
2468+
foreach ($this->mergeCells as $key => $value) {
2469+
if (preg_match('/^[a-z]{1,3}/i', $key, $matches) === 1) {
2470+
$startMergeInt = Coordinate::columnIndexFromString($matches[0]);
2471+
if ($startMergeInt >= $startColumnInt && $startMergeInt <= $endColumnInt) {
2472+
$removeKeys[] = $key;
2473+
}
2474+
}
2475+
}
2476+
foreach ($removeKeys as $key) {
2477+
unset($this->mergeCells[$key]);
2478+
}
24512479

24522480
$highestColumn = $this->getHighestDataColumn();
24532481
$highestColumnIndex = Coordinate::columnIndexFromString($highestColumn);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
6+
7+
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
8+
use PHPUnit\Framework\TestCase;
9+
10+
class MergeCellsDeletedTest extends TestCase
11+
{
12+
public function testDeletedColumns(): void
13+
{
14+
$infile = 'tests/data/Reader/XLSX/issue.282.xlsx';
15+
$reader = new XlsxReader();
16+
$spreadsheet = $reader->load($infile);
17+
$sheet = $spreadsheet->getSheetByName('Sheet1');
18+
19+
$mergeCells = $sheet->getMergeCells();
20+
self::assertSame(['B1:F1', 'G1:I1'], array_values($mergeCells));
21+
22+
// Want to delete column B,C,D,E,F
23+
$sheet->removeColumnByIndex(2, 5);
24+
$mergeCells2 = $sheet->getMergeCells();
25+
self::assertSame(['B1:D1'], array_values($mergeCells2));
26+
$spreadsheet->disconnectWorksheets();
27+
}
28+
29+
public function testDeletedRows(): void
30+
{
31+
$infile = 'tests/data/Reader/XLSX/issue.282.xlsx';
32+
$reader = new XlsxReader();
33+
$spreadsheet = $reader->load($infile);
34+
$sheet = $spreadsheet->getSheetByName('Sheet2');
35+
36+
$mergeCells = $sheet->getMergeCells();
37+
self::assertSame(['A2:A6', 'A7:A9'], array_values($mergeCells));
38+
39+
// Want to delete rows 2 to 4
40+
$sheet->removeRow(2, 3);
41+
$mergeCells2 = $sheet->getMergeCells();
42+
self::assertSame(['A4:A6'], array_values($mergeCells2));
43+
$spreadsheet->disconnectWorksheets();
44+
}
45+
}

tests/data/Reader/XLSX/issue.282.xlsx

10.9 KB
Binary file not shown.

0 commit comments

Comments
 (0)