Skip to content

Commit b99dc60

Browse files
authored
Merge pull request #4598 from oleibman/issue2912
Better Handling of Print Area After Column/Row Removal
2 parents ad76cae + 87a78e1 commit b99dc60

File tree

3 files changed

+190
-6
lines changed

3 files changed

+190
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ and this project adheres to [Semantic Versioning](https://semver.org). Thia is a
3232
### Fixed
3333

3434
- Google-only formulas exported from Google Sheets. [Issue #1637](https://github.com/PHPOffice/PhpSpreadsheet/issues/1637) [PR #4579](https://github.com/PHPOffice/PhpSpreadsheet/pull/4579)
35+
- Maximum column width. [PR #4581](https://github.com/PHPOffice/PhpSpreadsheet/pull/4581)
36+
- PrintArea after row/column delete. [Issue #2912](https://github.com/PHPOffice/PhpSpreadsheet/issues/2912) [PR #4598](https://github.com/PHPOffice/PhpSpreadsheet/pull/4598)
3537

3638
## 2025-08-10 - 5.0.0
3739

src/PhpSpreadsheet/ReferenceHelper.php

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -558,12 +558,7 @@ public function insertNewBefore(
558558
$worksheet->freezePane($splitCell, $topLeftCell);
559559
}
560560

561-
// Page setup
562-
if ($worksheet->getPageSetup()->isPrintAreaSet()) {
563-
$worksheet->getPageSetup()->setPrintArea(
564-
$this->updateCellReference($worksheet->getPageSetup()->getPrintArea())
565-
);
566-
}
561+
$this->updatePrintAreas($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
567562

568563
// Update worksheet: drawings
569564
$aDrawings = $worksheet->getDrawingCollection();
@@ -589,6 +584,93 @@ public function insertNewBefore(
589584
$worksheet->garbageCollect();
590585
}
591586

587+
private function updatePrintAreas(Worksheet $worksheet, string $beforeCellAddress, int $numberOfColumns, int $numberOfRows): void
588+
{
589+
$pageSetup = $worksheet->getPageSetup();
590+
if (!$pageSetup->isPrintAreaSet()) {
591+
return;
592+
}
593+
$printAreas = explode(',', $pageSetup->getPrintArea());
594+
$newPrintAreas = [];
595+
foreach ($printAreas as $printArea) {
596+
$result = $this->updatePrintArea($printArea, $beforeCellAddress, $numberOfColumns, $numberOfRows);
597+
if ($result !== '') {
598+
$newPrintAreas[] = $result;
599+
}
600+
}
601+
$result = implode(',', $newPrintAreas);
602+
if ($result === '') {
603+
$pageSetup->clearPrintArea();
604+
} else {
605+
$pageSetup->setPrintArea($result);
606+
}
607+
}
608+
609+
private function updatePrintArea(string $printArea, string $beforeCellAddress, int $numberOfColumns, int $numberOfRows): string
610+
{
611+
$coordinates = Coordinate::indexesFromString($beforeCellAddress);
612+
if (preg_match('/^([A-Z]{1,3})(\d{1,7}):([A-Z]{1,3})(\d{1,7})$/i', $printArea, $matches) === 1) {
613+
$firstRow = (int) $matches[2];
614+
$lastRow = (int) $matches[4];
615+
$firstColumnString = $matches[1];
616+
$lastColumnString = $matches[3];
617+
if ($numberOfRows < 0) {
618+
$affectedRow = $coordinates[1] + $numberOfRows - 1;
619+
$lastAffectedRow = $coordinates[1] - 1;
620+
if ($affectedRow >= $firstRow && $affectedRow <= $lastRow) {
621+
$newLastRow = max($affectedRow, $lastRow + $numberOfRows);
622+
if ($newLastRow >= $firstRow) {
623+
return $matches[1] . $matches[2] . ':' . $matches[3] . $newLastRow;
624+
}
625+
626+
return '';
627+
}
628+
if ($lastAffectedRow >= $firstRow && $affectedRow <= $lastRow) {
629+
$newFirstRow = $affectedRow + 1;
630+
$newLastRow = $lastRow + $numberOfRows;
631+
if ($newFirstRow >= 1 && $newLastRow >= $newFirstRow) {
632+
return $matches[1] . $newFirstRow . ':' . $matches[3] . $newLastRow;
633+
}
634+
635+
return '';
636+
}
637+
}
638+
if ($numberOfColumns < 0) {
639+
$firstColumnInt = Coordinate::columnIndexFromString($firstColumnString);
640+
$lastColumnInt = Coordinate::columnIndexFromString($lastColumnString);
641+
$affectedColumn = $coordinates[0] + $numberOfColumns - 1;
642+
$lastAffectedColumn = $coordinates[0] - 1;
643+
if ($affectedColumn >= $firstColumnInt && $affectedColumn <= $lastColumnInt) {
644+
$newLastColumnInt = max($affectedColumn, $lastColumnInt + $numberOfColumns);
645+
if ($newLastColumnInt >= $firstColumnInt) {
646+
$newLastColumnString = Coordinate::stringFromColumnIndex($newLastColumnInt);
647+
648+
return $matches[1] . $matches[2] . ':' . $newLastColumnString . $matches[4];
649+
}
650+
651+
return '';
652+
}
653+
if ($affectedColumn < $firstColumnInt && $lastAffectedColumn > $lastColumnInt) {
654+
return '';
655+
}
656+
if ($lastAffectedColumn >= $firstColumnInt && $lastAffectedColumn <= $lastColumnInt) {
657+
$newFirstColumn = $affectedColumn + 1;
658+
$newLastColumn = $lastColumnInt + $numberOfColumns;
659+
if ($newFirstColumn >= 1 && $newLastColumn >= $newFirstColumn) {
660+
$firstString = Coordinate::stringFromColumnIndex($newFirstColumn);
661+
$lastString = Coordinate::stringFromColumnIndex($newLastColumn);
662+
663+
return $firstString . $matches[2] . ':' . $lastString . $matches[4];
664+
}
665+
666+
return '';
667+
}
668+
}
669+
}
670+
671+
return $this->updateCellReference($printArea);
672+
}
673+
592674
private static function matchSheetName(?string $match, string $worksheetName): bool
593675
{
594676
return $match === null || $match === '' || $match === "'\u{fffc}'" || $match === "'\u{fffb}'" || strcasecmp(trim($match, "'"), $worksheetName) === 0;
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
6+
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class PrintAreaTest extends TestCase
12+
{
13+
#[DataProvider('removeRowsProvider')]
14+
public function testRemoveRows(string $expected, int $rowNumber, int $numberOfRows): void
15+
{
16+
$spreadsheet = new Spreadsheet();
17+
$sheet = $spreadsheet->getActiveSheet();
18+
$sheet->getPageSetup()->setPrintArea('B5:M41');
19+
$sheet->removeRow($rowNumber, $numberOfRows);
20+
self::assertSame($expected, $sheet->getPageSetup()->getPrintArea());
21+
$spreadsheet->disconnectWorksheets();
22+
}
23+
24+
public static function removeRowsProvider(): array
25+
{
26+
return [
27+
'before beginning of printArea' => ['B3:M39', 3, 2],
28+
'creep into printArea' => ['B3:M37', 3, 4],
29+
'entirely within printArea' => ['B5:M36', 6, 5],
30+
'creep past end of printArea' => ['B5:M34', 35, 8],
31+
'after end of printArea' => ['B5:M41', 55, 8],
32+
'entire printArea' => ['', 5, 37],
33+
'entire printArea+' => ['', 4, 47],
34+
];
35+
}
36+
37+
#[DataProvider('removeColumnsProvider')]
38+
public function testRemoveColumns(string $expected, string $column, int $numberOfColumns): void
39+
{
40+
$spreadsheet = new Spreadsheet();
41+
$sheet = $spreadsheet->getActiveSheet();
42+
$sheet->getPageSetup()->setPrintArea('D5:M41');
43+
$sheet->removeColumn($column, $numberOfColumns);
44+
self::assertSame($expected, $sheet->getPageSetup()->getPrintArea());
45+
$spreadsheet->disconnectWorksheets();
46+
}
47+
48+
public static function removeColumnsProvider(): array
49+
{
50+
return [
51+
'before beginning of printArea' => ['B5:K41', 'B', 2],
52+
'creep into printArea' => ['B5:I41', 'B', 4],
53+
'entirely within printArea' => ['D5:I41', 'E', 4],
54+
'creep past end of printArea' => ['D5:K41', 'L', 3],
55+
'after end of printArea' => ['D5:M41', 'P', 8],
56+
'entire printArea' => ['', 'D', 10],
57+
'entire printArea+' => ['', 'C', 15],
58+
];
59+
}
60+
61+
#[DataProvider('addRowsProvider')]
62+
public function testAddRows(string $expected, int $rowNumber, int $numberOfRows): void
63+
{
64+
$spreadsheet = new Spreadsheet();
65+
$sheet = $spreadsheet->getActiveSheet();
66+
$sheet->getPageSetup()->setPrintArea('D5:M41');
67+
$sheet->insertNewRowBefore($rowNumber, $numberOfRows);
68+
self::assertSame($expected, $sheet->getPageSetup()->getPrintArea());
69+
$spreadsheet->disconnectWorksheets();
70+
}
71+
72+
public static function addRowsProvider(): array
73+
{
74+
return [
75+
'entirely within printArea' => ['D5:M44', 15, 3],
76+
'above printArea' => ['D9:M45', 3, 4],
77+
'below printArea' => ['D5:M41', 48, 4],
78+
];
79+
}
80+
81+
#[DataProvider('addColumnsProvider')]
82+
public function testAddColumns(string $expected, string $column, int $numberOfColumns): void
83+
{
84+
$spreadsheet = new Spreadsheet();
85+
$sheet = $spreadsheet->getActiveSheet();
86+
$sheet->getPageSetup()->setPrintArea('D5:M41');
87+
$sheet->insertNewColumnBefore($column, $numberOfColumns);
88+
self::assertSame($expected, $sheet->getPageSetup()->getPrintArea());
89+
$spreadsheet->disconnectWorksheets();
90+
}
91+
92+
public static function addColumnsProvider(): array
93+
{
94+
return [
95+
'entirely within printArea' => ['D5:P41', 'J', 3],
96+
'left of printArea' => ['H5:Q41', 'C', 4],
97+
'right of printArea' => ['D5:M41', 'Q', 4],
98+
];
99+
}
100+
}

0 commit comments

Comments
 (0)