Skip to content

Commit 0d236eb

Browse files
committed
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.
1 parent 86cca12 commit 0d236eb

File tree

5 files changed

+100
-12
lines changed

5 files changed

+100
-12
lines changed

docs/topics/recipes.md

-9
Original file line numberDiff line numberDiff line change
@@ -836,15 +836,6 @@ row 10.
836836
$spreadsheet->getActiveSheet()->setBreak('A10', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW);
837837
```
838838

839-
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):
840-
841-
```php
842-
$spreadsheet->getActiveSheet()
843-
->setBreak('A10',
844-
\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW,
845-
\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW_MAX_COLUMN);
846-
```
847-
848839
The following line of code sets a print break on column D:
849840

850841
```php

src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ private function pageBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): v
149149
private function rowBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
150150
{
151151
foreach ($xmlSheet->rowBreaks->brk as $brk) {
152-
$rowBreakMax = isset($brk['max']) ? ((int) $brk['max']) : -1;
152+
$rowBreakMax = /*isset($brk['max']) ? ((int) $brk['max']) :*/ -1;
153153
if ($brk['man']) {
154154
$worksheet->setBreak("A{$brk['id']}", Worksheet::BREAK_ROW, $rowBreakMax);
155155
}

src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php

+10
Original file line numberDiff line numberDiff line change
@@ -1273,6 +1273,9 @@ private function writeBreaks(XMLWriter $objWriter, PhpspreadsheetWorksheet $work
12731273
$rowBreakMax = $break->getMaxColOrRow();
12741274
if ($rowBreakMax >= 0) {
12751275
$objWriter->writeAttribute('max', "$rowBreakMax");
1276+
} elseif ($worksheet->getPageSetup()->getPrintArea() !== '') {
1277+
$maxCol = Coordinate::columnIndexFromString($worksheet->getHighestColumn());
1278+
$objWriter->writeAttribute('max', "$maxCol");
12761279
}
12771280
$objWriter->endElement();
12781281
}
@@ -1292,6 +1295,13 @@ private function writeBreaks(XMLWriter $objWriter, PhpspreadsheetWorksheet $work
12921295
$objWriter->startElement('brk');
12931296
$objWriter->writeAttribute('id', (string) ((int) $coords[0] - 1));
12941297
$objWriter->writeAttribute('man', '1');
1298+
$colBreakMax = $break->getMaxColOrRow();
1299+
if ($colBreakMax >= 0) {
1300+
$objWriter->writeAttribute('max', "$colBreakMax");
1301+
} elseif ($worksheet->getPageSetup()->getPrintArea() !== '') {
1302+
$maxRow = $worksheet->getHighestRow();
1303+
$objWriter->writeAttribute('max', "$maxRow");
1304+
}
12951305
$objWriter->endElement();
12961306
}
12971307

tests/PhpSpreadsheetTests/Reader/Xlsx/RowBreakTest.php

+4-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public function testReadAndWriteRowBreak(): void
2121
$writer = new XlsxWriter($spreadsheet);
2222
$writerWorksheet = new XlsxWriter\Worksheet($writer);
2323
$data = $writerWorksheet->writeWorksheet($sheet, []);
24-
$expected = '<rowBreaks count="1" manualBreakCount="1"><brk id="25" man="1" max="16383"/></rowBreaks>';
24+
$expected = '<rowBreaks count="1" manualBreakCount="1"><brk id="25" man="1" max="11"/></rowBreaks>';
2525
self::assertStringContainsString($expected, $data);
2626
$spreadsheet->disconnectWorksheets();
2727
}
@@ -51,6 +51,8 @@ public function testWriteRowBreakInPrintAreaWithoutMax(): void
5151
{
5252
// This test does not specify max for setBreak,
5353
// and appears incorrect. Probable Excel bug.
54+
// See issue #1275, which now has a fix.
55+
// And I agree that the fix probably indicates an Excel bug.
5456
$spreadsheet = new Spreadsheet();
5557
$sheet = $spreadsheet->getActiveSheet();
5658
for ($row = 1; $row < 60; ++$row) {
@@ -64,7 +66,7 @@ public function testWriteRowBreakInPrintAreaWithoutMax(): void
6466
$writer = new XlsxWriter($spreadsheet);
6567
$writerWorksheet = new XlsxWriter\Worksheet($writer);
6668
$data = $writerWorksheet->writeWorksheet($sheet, []);
67-
$expected = '<rowBreaks count="1" manualBreakCount="1"><brk id="25" man="1"/></rowBreaks>';
69+
$expected = '<rowBreaks count="1" manualBreakCount="1"><brk id="25" man="1" max="11"/></rowBreaks>';
6870
self::assertStringContainsString($expected, $data);
6971
$spreadsheet->disconnectWorksheets();
7072
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
6+
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
9+
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
10+
use PHPUnit\Framework\TestCase;
11+
12+
class PageBreakTest extends TestCase
13+
{
14+
public function testRows(): void
15+
{
16+
$spreadsheet = new Spreadsheet();
17+
$sheet = $spreadsheet->getActiveSheet();
18+
$sheet->setCellValue('B1', 'First Page');
19+
$sheet->setCellValue('B2', 'Second Page');
20+
21+
$sheet->getPageSetup()->setPrintArea('B1:B2');
22+
$sheet->setBreak('B1', Worksheet::BREAK_ROW);
23+
$sheet->getColumnDimension('B')->setAutoSize(true);
24+
25+
$writer = new Xlsx($spreadsheet);
26+
$writerWorksheet = new Xlsx\Worksheet($writer);
27+
$data = $writerWorksheet->writeWorksheet($sheet, []);
28+
self::assertStringContainsString('<rowBreaks count="1" manualBreakCount="1"><brk id="1" man="1" max="2"/></rowBreaks>', $data);
29+
$spreadsheet->disconnectWorksheets();
30+
}
31+
32+
public function testRowsNoPrintArea(): void
33+
{
34+
$spreadsheet = new Spreadsheet();
35+
$sheet = $spreadsheet->getActiveSheet();
36+
$sheet->setCellValue('B1', 'First Page');
37+
$sheet->setCellValue('B2', 'Second Page');
38+
39+
$sheet->setBreak('B1', Worksheet::BREAK_ROW);
40+
$sheet->getColumnDimension('B')->setAutoSize(true);
41+
42+
$writer = new Xlsx($spreadsheet);
43+
$writerWorksheet = new Xlsx\Worksheet($writer);
44+
$data = $writerWorksheet->writeWorksheet($sheet, []);
45+
self::assertStringContainsString('<rowBreaks count="1" manualBreakCount="1"><brk id="1" man="1"/></rowBreaks>', $data);
46+
$spreadsheet->disconnectWorksheets();
47+
}
48+
49+
public function testCols(): void
50+
{
51+
$spreadsheet = new Spreadsheet();
52+
$sheet = $spreadsheet->getActiveSheet();
53+
$sheet->setCellValue('B1', 'First Page');
54+
$sheet->setCellValue('C1', 'Second Page');
55+
56+
$sheet->getPageSetup()->setPrintArea('B1:C1');
57+
$sheet->setBreak('C1', Worksheet::BREAK_COLUMN);
58+
$sheet->getColumnDimension('B')->setAutoSize(true);
59+
$sheet->getColumnDimension('C')->setAutoSize(true);
60+
61+
$writer = new Xlsx($spreadsheet);
62+
$writerWorksheet = new Xlsx\Worksheet($writer);
63+
$data = $writerWorksheet->writeWorksheet($sheet, []);
64+
self::assertStringContainsString('<colBreaks count="1" manualBreakCount="1"><brk id="2" man="1" max="1"/></colBreaks>', $data);
65+
$spreadsheet->disconnectWorksheets();
66+
}
67+
68+
public function testColsNoPrintArea(): void
69+
{
70+
$spreadsheet = new Spreadsheet();
71+
$sheet = $spreadsheet->getActiveSheet();
72+
$sheet->setCellValue('B1', 'First Page');
73+
$sheet->setCellValue('C1', 'Second Page');
74+
75+
$sheet->setBreak('C1', Worksheet::BREAK_COLUMN);
76+
$sheet->getColumnDimension('B')->setAutoSize(true);
77+
$sheet->getColumnDimension('C')->setAutoSize(true);
78+
79+
$writer = new Xlsx($spreadsheet);
80+
$writerWorksheet = new Xlsx\Worksheet($writer);
81+
$data = $writerWorksheet->writeWorksheet($sheet, []);
82+
self::assertStringContainsString('<colBreaks count="1" manualBreakCount="1"><brk id="2" man="1"/></colBreaks>', $data);
83+
$spreadsheet->disconnectWorksheets();
84+
}
85+
}

0 commit comments

Comments
 (0)