diff --git a/docs/topics/recipes.md b/docs/topics/recipes.md
index f5563ef96a..691d081284 100644
--- a/docs/topics/recipes.md
+++ b/docs/topics/recipes.md
@@ -479,6 +479,15 @@ row 10.
$spreadsheet->getActiveSheet()->setBreak('A10', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW);
```
+If your print break is inside a defined print area, it may be necessary to add an extra parameter to specify the max column (and this probably won't hurt if the break is not inside a defined print area):
+
+```php
+$spreadsheet->getActiveSheet()
+ ->setBreak('A10',
+ \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW,
+ \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW_MAX_COLUMN);
+```
+
The following line of code sets a print break on column D:
```php
diff --git a/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php b/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php
index fb55cb2653..ab57548847 100644
--- a/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php
+++ b/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php
@@ -151,8 +151,9 @@ private function pageBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): v
private function rowBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
{
foreach ($xmlSheet->rowBreaks->brk as $brk) {
+ $rowBreakMax = isset($brk['max']) ? ((int) $brk['max']) : -1;
if ($brk['man']) {
- $worksheet->setBreak("A{$brk['id']}", Worksheet::BREAK_ROW);
+ $worksheet->setBreak("A{$brk['id']}", Worksheet::BREAK_ROW, $rowBreakMax);
}
}
}
diff --git a/src/PhpSpreadsheet/Worksheet/PageBreak.php b/src/PhpSpreadsheet/Worksheet/PageBreak.php
new file mode 100644
index 0000000000..743cc58d69
--- /dev/null
+++ b/src/PhpSpreadsheet/Worksheet/PageBreak.php
@@ -0,0 +1,58 @@
+breakType = $breakType;
+ $this->coordinate = $coordinate;
+ $this->maxColOrRow = $maxColOrRow;
+ }
+
+ public function getBreakType(): int
+ {
+ return $this->breakType;
+ }
+
+ public function getCoordinate(): string
+ {
+ return $this->coordinate;
+ }
+
+ public function getMaxColOrRow(): int
+ {
+ return $this->maxColOrRow;
+ }
+
+ public function getColumnInt(): int
+ {
+ return Coordinate::indexesFromString($this->coordinate)[0];
+ }
+
+ public function getRow(): int
+ {
+ return Coordinate::indexesFromString($this->coordinate)[1];
+ }
+
+ public function getColumnString(): string
+ {
+ return Coordinate::indexesFromString($this->coordinate)[2];
+ }
+}
diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php
index cd9b8ac884..27ef7735a2 100644
--- a/src/PhpSpreadsheet/Worksheet/Worksheet.php
+++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php
@@ -35,6 +35,8 @@ class Worksheet implements IComparable
public const BREAK_NONE = 0;
public const BREAK_ROW = 1;
public const BREAK_COLUMN = 2;
+ // Maximum column for row break
+ public const BREAK_ROW_MAX_COLUMN = 16383;
// Sheet state
public const SHEETSTATE_VISIBLE = 'visible';
@@ -188,11 +190,18 @@ class Worksheet implements IComparable
private $conditionalStylesCollection = [];
/**
- * Collection of breaks.
+ * Collection of row breaks.
*
- * @var int[]
+ * @var PageBreak[]
*/
- private $breaks = [];
+ private $rowBreaks = [];
+
+ /**
+ * Collection of column breaks.
+ *
+ * @var PageBreak[]
+ */
+ private $columnBreaks = [];
/**
* Collection of merged cell ranges.
@@ -1748,16 +1757,16 @@ public function duplicateConditionalStyle(array $styles, $range = '')
*
* @return $this
*/
- public function setBreak($coordinate, $break)
+ public function setBreak($coordinate, $break, int $max = -1)
{
$cellAddress = Functions::trimSheetFromCellReference(Validations::validateCellAddress($coordinate));
if ($break === self::BREAK_NONE) {
- if (isset($this->breaks[$cellAddress])) {
- unset($this->breaks[$cellAddress]);
- }
- } else {
- $this->breaks[$cellAddress] = $break;
+ unset($this->rowBreaks[$cellAddress], $this->columnBreaks[$cellAddress]);
+ } elseif ($break === self::BREAK_ROW) {
+ $this->rowBreaks[$cellAddress] = new PageBreak($break, $cellAddress, $max);
+ } elseif ($break === self::BREAK_COLUMN) {
+ $this->columnBreaks[$cellAddress] = new PageBreak($break, $cellAddress, $max);
}
return $this;
@@ -1789,7 +1798,35 @@ public function setBreakByColumnAndRow($columnIndex, $row, $break)
*/
public function getBreaks()
{
- return $this->breaks;
+ $breaks = [];
+ foreach ($this->rowBreaks as $break) {
+ $breaks[$break->getCoordinate()] = self::BREAK_ROW;
+ }
+ foreach ($this->columnBreaks as $break) {
+ $breaks[$break->getCoordinate()] = self::BREAK_COLUMN;
+ }
+
+ return $breaks;
+ }
+
+ /**
+ * Get row breaks.
+ *
+ * @return PageBreak[]
+ */
+ public function getRowBreaks()
+ {
+ return $this->rowBreaks;
+ }
+
+ /**
+ * Get row breaks.
+ *
+ * @return PageBreak[]
+ */
+ public function getColumnBreaks()
+ {
+ return $this->columnBreaks;
}
/**
diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php
index 309168f25b..c30bb30acf 100644
--- a/src/PhpSpreadsheet/Writer/Html.php
+++ b/src/PhpSpreadsheet/Writer/Html.php
@@ -1193,7 +1193,7 @@ private function generateRowStart(Worksheet $worksheet, $sheetIndex, $row)
{
$html = '';
if (count($worksheet->getBreaks()) > 0) {
- $breaks = $worksheet->getBreaks();
+ $breaks = $worksheet->getRowBreaks();
// check if a break is needed before this row
if (isset($breaks['A' . $row])) {
diff --git a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php
index ce6a84f206..79ab874c4b 100644
--- a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php
+++ b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php
@@ -2022,27 +2022,15 @@ private function writeBreaks(): void
$vbreaks = [];
$hbreaks = [];
- foreach ($this->phpSheet->getBreaks() as $cell => $breakType) {
+ foreach ($this->phpSheet->getRowBreaks() as $cell => $break) {
// Fetch coordinates
$coordinates = Coordinate::coordinateFromString($cell);
-
- // Decide what to do by the type of break
- switch ($breakType) {
- case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_COLUMN:
- // Add to list of vertical breaks
- $vbreaks[] = Coordinate::columnIndexFromString($coordinates[0]) - 1;
-
- break;
- case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW:
- // Add to list of horizontal breaks
- $hbreaks[] = $coordinates[1];
-
- break;
- case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_NONE:
- default:
- // Nothing to do
- break;
- }
+ $hbreaks[] = $coordinates[1];
+ }
+ foreach ($this->phpSheet->getColumnBreaks() as $cell => $break) {
+ // Fetch coordinates
+ $coordinates = Coordinate::indexesFromString($cell);
+ $vbreaks[] = $coordinates[0] - 1;
}
//horizontal page breaks
diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
index e864ddd175..f4904d2557 100644
--- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
+++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
@@ -1006,12 +1006,11 @@ private function writeBreaks(XMLWriter $objWriter, PhpspreadsheetWorksheet $work
// Get row and column breaks
$aRowBreaks = [];
$aColumnBreaks = [];
- foreach ($worksheet->getBreaks() as $cell => $breakType) {
- if ($breakType == PhpspreadsheetWorksheet::BREAK_ROW) {
- $aRowBreaks[] = $cell;
- } elseif ($breakType == PhpspreadsheetWorksheet::BREAK_COLUMN) {
- $aColumnBreaks[] = $cell;
- }
+ foreach ($worksheet->getRowBreaks() as $cell => $break) {
+ $aRowBreaks[$cell] = $break;
+ }
+ foreach ($worksheet->getColumnBreaks() as $cell => $break) {
+ $aColumnBreaks[$cell] = $break;
}
// rowBreaks
@@ -1020,12 +1019,16 @@ private function writeBreaks(XMLWriter $objWriter, PhpspreadsheetWorksheet $work
$objWriter->writeAttribute('count', (string) count($aRowBreaks));
$objWriter->writeAttribute('manualBreakCount', (string) count($aRowBreaks));
- foreach ($aRowBreaks as $cell) {
+ foreach ($aRowBreaks as $cell => $break) {
$coords = Coordinate::coordinateFromString($cell);
$objWriter->startElement('brk');
$objWriter->writeAttribute('id', $coords[1]);
$objWriter->writeAttribute('man', '1');
+ $rowBreakMax = $break->getMaxColOrRow();
+ if ($rowBreakMax >= 0) {
+ $objWriter->writeAttribute('max', "$rowBreakMax");
+ }
$objWriter->endElement();
}
@@ -1038,11 +1041,11 @@ private function writeBreaks(XMLWriter $objWriter, PhpspreadsheetWorksheet $work
$objWriter->writeAttribute('count', (string) count($aColumnBreaks));
$objWriter->writeAttribute('manualBreakCount', (string) count($aColumnBreaks));
- foreach ($aColumnBreaks as $cell) {
+ foreach ($aColumnBreaks as $cell => $break) {
$coords = Coordinate::coordinateFromString($cell);
$objWriter->startElement('brk');
- $objWriter->writeAttribute('id', (string) (Coordinate::columnIndexFromString($coords[0]) - 1));
+ $objWriter->writeAttribute('id', (string) ((int) $coords[0] - 1));
$objWriter->writeAttribute('man', '1');
$objWriter->endElement();
}
diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/RowBreakTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/RowBreakTest.php
new file mode 100644
index 0000000000..9bb75f6355
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/RowBreakTest.php
@@ -0,0 +1,69 @@
+load($file);
+ $sheet = $spreadsheet->getActiveSheet();
+ $writer = new XlsxWriter($spreadsheet);
+ $writerWorksheet = new XlsxWriter\Worksheet($writer);
+ $data = $writerWorksheet->writeWorksheet($sheet, []);
+ $expected = '';
+ self::assertStringContainsString($expected, $data);
+ $spreadsheet->disconnectWorksheets();
+ }
+
+ public function testWriteRowBreakInPrintAreaWithMax(): void
+ {
+ // This test specifies max for setBreak and appears correct.
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ for ($row = 1; $row < 60; ++$row) {
+ for ($column = 'A'; $column !== 'L'; ++$column) {
+ $cell = $column . $row;
+ $sheet->getCell($cell)->setValue($cell);
+ }
+ }
+ $sheet->getPageSetup()->setPrintArea('B2:J55');
+ $sheet->setBreak('A25', Worksheet::BREAK_ROW, Worksheet::BREAK_ROW_MAX_COLUMN);
+ $writer = new XlsxWriter($spreadsheet);
+ $writerWorksheet = new XlsxWriter\Worksheet($writer);
+ $data = $writerWorksheet->writeWorksheet($sheet, []);
+ $expected = '';
+ self::assertStringContainsString($expected, $data);
+ $spreadsheet->disconnectWorksheets();
+ }
+
+ public function testWriteRowBreakInPrintAreaWithoutMax(): void
+ {
+ // This test does not specify max for setBreak,
+ // and appears incorrect. Probable Excel bug.
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ for ($row = 1; $row < 60; ++$row) {
+ for ($column = 'A'; $column !== 'L'; ++$column) {
+ $cell = $column . $row;
+ $sheet->getCell($cell)->setValue($cell);
+ }
+ }
+ $sheet->getPageSetup()->setPrintArea('B2:J55');
+ $sheet->setBreak('A25', Worksheet::BREAK_ROW);
+ $writer = new XlsxWriter($spreadsheet);
+ $writerWorksheet = new XlsxWriter\Worksheet($writer);
+ $data = $writerWorksheet->writeWorksheet($sheet, []);
+ $expected = '';
+ self::assertStringContainsString($expected, $data);
+ $spreadsheet->disconnectWorksheets();
+ }
+}
diff --git a/tests/PhpSpreadsheetTests/Worksheet/PageBreakTest.php b/tests/PhpSpreadsheetTests/Worksheet/PageBreakTest.php
new file mode 100644
index 0000000000..cbeb96d68d
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/Worksheet/PageBreakTest.php
@@ -0,0 +1,148 @@
+getActiveSheet();
+ $sheet->setBreak('A20', Worksheet::BREAK_ROW);
+ $sheet->setBreak('A40', Worksheet::BREAK_ROW);
+ $sheet->setBreak('H1', Worksheet::BREAK_COLUMN);
+ $sheet->setBreak('X1', Worksheet::BREAK_COLUMN);
+ $breaks1 = $sheet->getBreaks();
+ self::assertSame(
+ [
+ 'A20' => Worksheet::BREAK_ROW,
+ 'A40' => Worksheet::BREAK_ROW,
+ 'H1' => Worksheet::BREAK_COLUMN,
+ 'X1' => Worksheet::BREAK_COLUMN,
+ ],
+ $breaks1
+ );
+ $sheet->setBreak('A40', Worksheet::BREAK_NONE);
+ $sheet->setBreak('H1', Worksheet::BREAK_NONE);
+ $sheet->setBreak('XX1', Worksheet::BREAK_NONE);
+ $breaks2 = $sheet->getBreaks();
+ self::assertSame(
+ [
+ 'A20' => Worksheet::BREAK_ROW,
+ 'X1' => Worksheet::BREAK_COLUMN,
+ ],
+ $breaks2
+ );
+ $spreadsheet->disconnectWorksheets();
+ }
+
+ public function testBreaksArray(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->setBreak([1, 20], Worksheet::BREAK_ROW);
+ $sheet->setBreak([1, 40], Worksheet::BREAK_ROW);
+ $sheet->setBreak([8, 1], Worksheet::BREAK_COLUMN);
+ $sheet->setBreak([24, 1], Worksheet::BREAK_COLUMN);
+ $breaks1 = $sheet->getBreaks();
+ self::assertSame(
+ [
+ 'A20' => Worksheet::BREAK_ROW,
+ 'A40' => Worksheet::BREAK_ROW,
+ 'H1' => Worksheet::BREAK_COLUMN,
+ 'X1' => Worksheet::BREAK_COLUMN,
+ ],
+ $breaks1
+ );
+ $sheet->setBreak([1, 40], Worksheet::BREAK_NONE);
+ $sheet->setBreak([8, 1], Worksheet::BREAK_NONE);
+ $sheet->setBreak([50, 1], Worksheet::BREAK_NONE);
+ $breaks2 = $sheet->getBreaks();
+ self::assertSame(
+ [
+ 'A20' => Worksheet::BREAK_ROW,
+ 'X1' => Worksheet::BREAK_COLUMN,
+ ],
+ $breaks2
+ );
+ $spreadsheet->disconnectWorksheets();
+ }
+
+ public function testBreaksCellAddress(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->setBreak(new CellAddress('A20'), Worksheet::BREAK_ROW, 16383);
+ $sheet->setBreak(new CellAddress('A40', $sheet), Worksheet::BREAK_ROW);
+ $sheet->setBreak(new CellAddress('H1'), Worksheet::BREAK_COLUMN);
+ $sheet->setBreak(new CellAddress('X1', $sheet), Worksheet::BREAK_COLUMN);
+ $breaks1 = $sheet->getBreaks();
+ self::assertSame(
+ [
+ 'A20' => Worksheet::BREAK_ROW,
+ 'A40' => Worksheet::BREAK_ROW,
+ 'H1' => Worksheet::BREAK_COLUMN,
+ 'X1' => Worksheet::BREAK_COLUMN,
+ ],
+ $breaks1
+ );
+ $sheet->setBreak(new CellAddress('A40'), Worksheet::BREAK_NONE);
+ $sheet->setBreak(new CellAddress('H1', $sheet), Worksheet::BREAK_NONE);
+ $sheet->setBreak(new CellAddress('XX1'), Worksheet::BREAK_NONE);
+ $breaks2 = $sheet->getBreaks();
+ self::assertSame(
+ [
+ 'A20' => Worksheet::BREAK_ROW,
+ 'X1' => Worksheet::BREAK_COLUMN,
+ ],
+ $breaks2
+ );
+ $spreadsheet->disconnectWorksheets();
+ }
+
+ public function testBreaksOtherMethods(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->setBreak('A20', Worksheet::BREAK_ROW, 16383);
+ $sheet->setBreak('A40', Worksheet::BREAK_ROW);
+ $sheet->setBreak('H1', Worksheet::BREAK_COLUMN);
+ $sheet->setBreak('X1', Worksheet::BREAK_COLUMN);
+
+ $rowBreaks = $sheet->getRowBreaks();
+ self::assertCount(2, $rowBreaks);
+ self::assertSame(Worksheet::BREAK_ROW, $rowBreaks['A20']->getBreakType());
+ self::assertSame('A20', $rowBreaks['A20']->getCoordinate());
+ self::assertSame(16383, $rowBreaks['A20']->getMaxColOrRow());
+ self::assertSame(1, $rowBreaks['A20']->getColumnInt());
+ self::assertSame('A', $rowBreaks['A20']->getColumnString());
+ self::assertSame(20, $rowBreaks['A20']->getRow());
+ self::assertSame(Worksheet::BREAK_ROW, $rowBreaks['A20']->getBreakType());
+ self::assertSame('A40', $rowBreaks['A40']->getCoordinate());
+ self::assertSame(-1, $rowBreaks['A40']->getMaxColOrRow());
+ self::assertSame(1, $rowBreaks['A40']->getColumnInt());
+ self::assertSame('A', $rowBreaks['A40']->getColumnString());
+ self::assertSame(40, $rowBreaks['A40']->getRow());
+ self::assertSame(Worksheet::BREAK_ROW, $rowBreaks['A40']->getBreakType());
+
+ $columnBreaks = $sheet->getColumnBreaks();
+ self::assertCount(2, $columnBreaks);
+ self::assertSame(Worksheet::BREAK_COLUMN, $columnBreaks['H1']->getBreakType());
+ self::assertSame('H1', $columnBreaks['H1']->getCoordinate());
+ self::assertSame(8, $columnBreaks['H1']->getColumnInt());
+ self::assertSame('H', $columnBreaks['H1']->getColumnString());
+ self::assertSame(1, $columnBreaks['H1']->getRow());
+ self::assertSame(Worksheet::BREAK_COLUMN, $columnBreaks['H1']->getBreakType());
+ self::assertSame('X1', $columnBreaks['X1']->getCoordinate());
+ self::assertSame(24, $columnBreaks['X1']->getColumnInt());
+ self::assertSame('X', $columnBreaks['X1']->getColumnString());
+ self::assertSame(1, $columnBreaks['X1']->getRow());
+ self::assertSame(Worksheet::BREAK_COLUMN, $columnBreaks['X1']->getBreakType());
+ $spreadsheet->disconnectWorksheets();
+ }
+}
diff --git a/tests/data/Reader/XLSX/issue.3143a.xlsx b/tests/data/Reader/XLSX/issue.3143a.xlsx
new file mode 100644
index 0000000000..d90eb29f16
Binary files /dev/null and b/tests/data/Reader/XLSX/issue.3143a.xlsx differ