Repository: PHPOffice/PhpSpreadsheet (php)

Issue:
Row breaks not working when print area is set starting from non-A column
This is:

```
- [X] a bug report
- [ ] 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?
After setting print area starting with a non-A column (e.g. B1:B2), calling `setBreak('B1', Worksheet::BREAK_ROW)` should be able to split B1 and B2 in two different pages.

### What is the current behavior?
B1 and B2 appears in the same page in print preview.

To have a correct row break for non-A column, `min="1" max="(count of print area columns)"` should be added to the `<brk>` element in xl/worksheets/*.xml. These two attributes can be referred to the XML standard.
https://docs.microsoft.com/en-us/openspecs/office_standards/ms-oe376/b32ae11b-dee7-4dcb-9b46-a0feb32ce94f

### What are the steps to reproduce?

Please provide a [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) of code that exhibits the issue without relying on an external Excel file or a web server:

```php
<?php

require __DIR__ . '/vendor/autoload.php';

// Create new Spreadsheet object
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();

// add code that show the issue here...
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValue('B1', 'First Page');
$sheet->setCellValue('B2', 'Second Page');

$sheet->getPageSetup()->setPrintArea('B1:B2');
$sheet->setBreak('B1', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW);

$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->save('hello_world.xlsx');
```

### Which versions of PhpSpreadsheet and PHP are affected?
PhpSpreadsheet 1.10.1
PHP 7.2


Pull request:
Print Area and Row Break
Fix #1275, which had been closed as stale, and is now reopened pending the implementation of this PR. If there is a page break inside a defined print area, Excel may not render the print correctly unless the xml `brk` tag contains a `max` attribute. Libre Office renders it correctly. This seems like a bug in Excel (https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/b32ae11b-dee7-4dcb-9b46-a0feb32ce94f states that Office ignores min and max). PR #3345 (issue #3143) already addressed this problem by allowing the user to explicitly specify a `max` property in the PageBreak object. This PR eliminates the need for the user to make use of that kludge, by adding `max` to the xml whenever a page break is specified on a sheet with a defined print area. Xlsx Reader will now ignore the `max` attribute for row breaks, since it is no longer needed; it already ignores it for column breaks. The user may still set the `max` property if desired, just in case the new treatment is not adequate (I have not found a case where that is true). Two existing unit tests are very marginally changed because of this PR.

This is:

- [x] a bugfix
- [ ] a new feature
- [ ] refactoring
- [ ] additional unit tests

Checklist:

- [x] Changes are covered by unit tests
  - [x] Changes are covered by existing unit tests
  - [x] New unit tests have been added
- [x] Code style is respected
- [x] Commit message explains **why** the change is made (see https://github.com/erlang/otp/wiki/Writing-good-commit-messages)
- [ ] CHANGELOG.md contains a short summary of the change and a link to the pull request if applicable
- [x] Documentation is updated as necessary

### Why this change is needed?

Provide an explanation of why this change is needed, with links to any Issues (if appropriate).
If this is a bugfix or a new feature, and there are no existing Issues, then please also create an issue that will make it easier to track progress with this PR.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6f090c217..0f13b3928 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
 
 ### Added
 
-- Nothing yet.
+- Xml Reader recognize indents. [Issue #4448](https://github.com/PHPOffice/PhpSpreadsheet/issues/4448) [PR #4449](https://github.com/PHPOffice/PhpSpreadsheet/pull/4449)
 
 ### Removed
 
@@ -29,7 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).
 
 ### Fixed
 
-- Nothing yet.
+- Micro-optimization for excelToDateTimeObject. [Issue #4438](https://github.com/PHPOffice/PhpSpreadsheet/issues/4438) [PR #4442](https://github.com/PHPOffice/PhpSpreadsheet/pull/4442)
+- Print Area and Row Break. [Issue #1275](https://github.com/PHPOffice/PhpSpreadsheet/issues/1275) [PR #4450](https://github.com/PHPOffice/PhpSpreadsheet/pull/4450)
 
 ## 2025-04-16 - 4.2.0
 
diff --git a/docs/topics/recipes.md b/docs/topics/recipes.md
index bdc86b982..8168abb24 100644
--- a/docs/topics/recipes.md
+++ b/docs/topics/recipes.md
@@ -836,15 +836,6 @@ 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 5e6cc88bb..ca6ae0b09 100644
--- a/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php
+++ b/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php
@@ -149,7 +149,7 @@ class PageSetup extends BaseParserClass
     private function rowBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
     {
         foreach ($xmlSheet->rowBreaks->brk as $brk) {
-            $rowBreakMax = isset($brk['max']) ? ((int) $brk['max']) : -1;
+            $rowBreakMax = /*isset($brk['max']) ? ((int) $brk['max']) :*/ -1;
             if ($brk['man']) {
                 $worksheet->setBreak("A{$brk['id']}", Worksheet::BREAK_ROW, $rowBreakMax);
             }
diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
index 336cfe60c..38d796c85 100644
--- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
+++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
@@ -1273,6 +1273,9 @@ class Worksheet extends WriterPart
                 $rowBreakMax = $break->getMaxColOrRow();
                 if ($rowBreakMax >= 0) {
                     $objWriter->writeAttribute('max', "$rowBreakMax");
+                } elseif ($worksheet->getPageSetup()->getPrintArea() !== '') {
+                    $maxCol = Coordinate::columnIndexFromString($worksheet->getHighestColumn());
+                    $objWriter->writeAttribute('max', "$maxCol");
                 }
                 $objWriter->endElement();
             }
@@ -1292,6 +1295,13 @@ class Worksheet extends WriterPart
                 $objWriter->startElement('brk');
                 $objWriter->writeAttribute('id', (string) ((int) $coords[0] - 1));
                 $objWriter->writeAttribute('man', '1');
+                $colBreakMax = $break->getMaxColOrRow();
+                if ($colBreakMax >= 0) {
+                    $objWriter->writeAttribute('max', "$colBreakMax");
+                } elseif ($worksheet->getPageSetup()->getPrintArea() !== '') {
+                    $maxRow = $worksheet->getHighestRow();
+                    $objWriter->writeAttribute('max', "$maxRow");
+                }
                 $objWriter->endElement();
             }
 


Test patch:
diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/RowBreakTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/RowBreakTest.php
index c98e1f528..5958a4bdc 100644
--- a/tests/PhpSpreadsheetTests/Reader/Xlsx/RowBreakTest.php
+++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/RowBreakTest.php
@@ -21,7 +21,7 @@ class RowBreakTest extends TestCase
         $writer = new XlsxWriter($spreadsheet);
         $writerWorksheet = new XlsxWriter\Worksheet($writer);
         $data = $writerWorksheet->writeWorksheet($sheet, []);
-        $expected = '<rowBreaks count="1" manualBreakCount="1"><brk id="25" man="1" max="16383"/></rowBreaks>';
+        $expected = '<rowBreaks count="1" manualBreakCount="1"><brk id="25" man="1" max="11"/></rowBreaks>';
         self::assertStringContainsString($expected, $data);
         $spreadsheet->disconnectWorksheets();
     }
@@ -51,6 +51,8 @@ class RowBreakTest extends TestCase
     {
         // This test does not specify max for setBreak,
         // and appears incorrect. Probable Excel bug.
+        // See issue #1275, which now has a fix.
+        // And I agree that the fix probably indicates an Excel bug.
         $spreadsheet = new Spreadsheet();
         $sheet = $spreadsheet->getActiveSheet();
         for ($row = 1; $row < 60; ++$row) {
@@ -64,7 +66,7 @@ class RowBreakTest extends TestCase
         $writer = new XlsxWriter($spreadsheet);
         $writerWorksheet = new XlsxWriter\Worksheet($writer);
         $data = $writerWorksheet->writeWorksheet($sheet, []);
-        $expected = '<rowBreaks count="1" manualBreakCount="1"><brk id="25" man="1"/></rowBreaks>';
+        $expected = '<rowBreaks count="1" manualBreakCount="1"><brk id="25" man="1" max="11"/></rowBreaks>';
         self::assertStringContainsString($expected, $data);
         $spreadsheet->disconnectWorksheets();
     }
diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/PageBreakTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/PageBreakTest.php
new file mode 100644
index 000000000..3a9b4a123
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/PageBreakTest.php
@@ -0,0 +1,85 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+use PHPUnit\Framework\TestCase;
+
+class PageBreakTest extends TestCase
+{
+    public function testRows(): void
+    {
+        $spreadsheet = new Spreadsheet();
+        $sheet = $spreadsheet->getActiveSheet();
+        $sheet->setCellValue('B1', 'First Page');
+        $sheet->setCellValue('B2', 'Second Page');
+
+        $sheet->getPageSetup()->setPrintArea('B1:B2');
+        $sheet->setBreak('B1', Worksheet::BREAK_ROW);
+        $sheet->getColumnDimension('B')->setAutoSize(true);
+
+        $writer = new Xlsx($spreadsheet);
+        $writerWorksheet = new Xlsx\Worksheet($writer);
+        $data = $writerWorksheet->writeWorksheet($sheet, []);
+        self::assertStringContainsString('<rowBreaks count="1" manualBreakCount="1"><brk id="1" man="1" max="2"/></rowBreaks>', $data);
+        $spreadsheet->disconnectWorksheets();
+    }
+
+    public function testRowsNoPrintArea(): void
+    {
+        $spreadsheet = new Spreadsheet();
+        $sheet = $spreadsheet->getActiveSheet();
+        $sheet->setCellValue('B1', 'First Page');
+        $sheet->setCellValue('B2', 'Second Page');
+
+        $sheet->setBreak('B1', Worksheet::BREAK_ROW);
+        $sheet->getColumnDimension('B')->setAutoSize(true);
+
+        $writer = new Xlsx($spreadsheet);
+        $writerWorksheet = new Xlsx\Worksheet($writer);
+        $data = $writerWorksheet->writeWorksheet($sheet, []);
+        self::assertStringContainsString('<rowBreaks count="1" manualBreakCount="1"><brk id="1" man="1"/></rowBreaks>', $data);
+        $spreadsheet->disconnectWorksheets();
+    }
+
+    public function testCols(): void
+    {
+        $spreadsheet = new Spreadsheet();
+        $sheet = $spreadsheet->getActiveSheet();
+        $sheet->setCellValue('B1', 'First Page');
+        $sheet->setCellValue('C1', 'Second Page');
+
+        $sheet->getPageSetup()->setPrintArea('B1:C1');
+        $sheet->setBreak('C1', Worksheet::BREAK_COLUMN);
+        $sheet->getColumnDimension('B')->setAutoSize(true);
+        $sheet->getColumnDimension('C')->setAutoSize(true);
+
+        $writer = new Xlsx($spreadsheet);
+        $writerWorksheet = new Xlsx\Worksheet($writer);
+        $data = $writerWorksheet->writeWorksheet($sheet, []);
+        self::assertStringContainsString('<colBreaks count="1" manualBreakCount="1"><brk id="2" man="1" max="1"/></colBreaks>', $data);
+        $spreadsheet->disconnectWorksheets();
+    }
+
+    public function testColsNoPrintArea(): void
+    {
+        $spreadsheet = new Spreadsheet();
+        $sheet = $spreadsheet->getActiveSheet();
+        $sheet->setCellValue('B1', 'First Page');
+        $sheet->setCellValue('C1', 'Second Page');
+
+        $sheet->setBreak('C1', Worksheet::BREAK_COLUMN);
+        $sheet->getColumnDimension('B')->setAutoSize(true);
+        $sheet->getColumnDimension('C')->setAutoSize(true);
+
+        $writer = new Xlsx($spreadsheet);
+        $writerWorksheet = new Xlsx\Worksheet($writer);
+        $data = $writerWorksheet->writeWorksheet($sheet, []);
+        self::assertStringContainsString('<colBreaks count="1" manualBreakCount="1"><brk id="2" man="1"/></colBreaks>', $data);
+        $spreadsheet->disconnectWorksheets();
+    }
+}