Skip to content

Commit fde46cf

Browse files
authored
Merge pull request #4320 from oleibman/issue4319
Mpdf and Tcpdf Hidden Columns and Merged Cells
2 parents f33a440 + 12b1690 commit fde46cf

File tree

6 files changed

+557
-2
lines changed

6 files changed

+557
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
2727
### Fixed
2828

2929
- Ods Reader Sheet Names with Period. [Issue #4311](https://github.com/PHPOffice/PhpSpreadsheet/issues/4311) [PR #4313](https://github.com/PHPOffice/PhpSpreadsheet/pull/4313)
30+
- Mpdf and Tcpdf Hidden Columns and Merged Cells. [Issue #4319](https://github.com/PHPOffice/PhpSpreadsheet/issues/4319) [PR #4320](https://github.com/PHPOffice/PhpSpreadsheet/pull/4320)
3031

3132
## 2025-01-11 - 3.8.0
3233

src/PhpSpreadsheet/Writer/Html.php

+14-2
Original file line numberDiff line numberDiff line change
@@ -1565,7 +1565,7 @@ private function generateRowWriteCell(
15651565
/**
15661566
* Generate row.
15671567
*
1568-
* @param array $values Array containing cells in a row
1568+
* @param array<int, mixed> $values Array containing cells in a row
15691569
* @param int $row Row number (0-based)
15701570
* @param string $cellType eg: 'td'
15711571
*/
@@ -1577,7 +1577,19 @@ private function generateRow(Worksheet $worksheet, array $values, int $row, stri
15771577

15781578
// Write cells
15791579
$colNum = 0;
1580-
foreach ($values as $cellAddress) {
1580+
$tcpdfInited = false;
1581+
foreach ($values as $key => $cellAddress) {
1582+
if ($this instanceof Pdf\Mpdf) {
1583+
$colNum = $key - 1;
1584+
} elseif ($this instanceof Pdf\Tcpdf) {
1585+
// It appears that Tcpdf requires first cell in tr.
1586+
$colNum = $key - 1;
1587+
if (!$tcpdfInited && $key !== 1) {
1588+
$tempspan = ($colNum > 1) ? " colspan='$colNum'" : '';
1589+
$html .= "<td$tempspan></td>\n";
1590+
}
1591+
$tcpdfInited = true;
1592+
}
15811593
[$cell, $cssClass, $coordinate] = $this->generateRowCellCss($worksheet, $cellAddress, $row, $colNum);
15821594

15831595
// Cell Data
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Writer\Dompdf;
6+
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PhpOffice\PhpSpreadsheet\Style\Alignment;
9+
use PhpOffice\PhpSpreadsheet\Style\Border;
10+
use PhpOffice\PhpSpreadsheet\Style\Color;
11+
use PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf;
12+
use PHPUnit\Framework\TestCase;
13+
14+
class HideMergeTest extends TestCase
15+
{
16+
public function testHideWithMerge(): void
17+
{
18+
$spreadsheet = new Spreadsheet();
19+
$worksheet = $spreadsheet->getActiveSheet();
20+
// just some labels for better visualisation of the problem
21+
$worksheet->setCellValue('A1', 'A');
22+
$worksheet->setCellValue('B1', 'B');
23+
$worksheet->setCellValue('C1', 'C');
24+
// setting the row height to better visualize the problem
25+
for ($i = 1; $i <= 10; ++$i) {
26+
$worksheet->getRowDimension($i)->setRowHeight(17);
27+
}
28+
// Headline - merged over two cells AND two rows
29+
$worksheet->mergeCells('B2:C3');
30+
$worksheet->setCellValue('B2', 'Hello World Headline');
31+
$worksheet->getStyle('B2:C3')->getFont()->setBold(true);
32+
$worksheet->getStyle('B2:C3')
33+
->getAlignment()
34+
->setHorizontal(Alignment::HORIZONTAL_CENTER);
35+
$worksheet->getStyle('B2:C3')
36+
->getBorders()
37+
->getAllBorders()
38+
->setBorderStyle(Border::BORDER_THIN)
39+
->setColor(new Color(Color::COLOR_BLACK));
40+
41+
// Content 1 - merge over two rows
42+
$worksheet->mergeCells('B4:B5');
43+
$worksheet->mergeCells('C4:C5');
44+
$worksheet->setCellValue('B4', 'Label 1');
45+
$worksheet->setCellValue('C4', 'Text 1');
46+
$worksheet->getStyle('B4:B5')->getFont()->setBold(true);
47+
$worksheet->getStyle('B4:C5')
48+
->getAlignment()
49+
->setVertical(Alignment::VERTICAL_CENTER);
50+
$worksheet->getStyle('B4:B5')
51+
->getBorders()
52+
->getAllBorders()
53+
->setBorderStyle(Border::BORDER_THIN)
54+
->setColor(new Color(Color::COLOR_BLACK));
55+
$worksheet->getStyle('C4:C5')
56+
->getBorders()
57+
->getAllBorders()
58+
->setBorderStyle(Border::BORDER_THIN)
59+
->setColor(new Color(Color::COLOR_BLACK));
60+
61+
// Content 2 - merge over two rows
62+
$worksheet->mergeCells('B6:B7');
63+
$worksheet->mergeCells('C6:C7');
64+
$worksheet->setCellValue('B6', 'Label 2');
65+
$worksheet->setCellValue('C6', 'Text 2');
66+
$worksheet->getStyle('B6:B7')->getFont()->setBold(true);
67+
$worksheet->getStyle('B6:C7')
68+
->getAlignment()
69+
->setVertical(Alignment::VERTICAL_CENTER);
70+
$worksheet->getStyle('B6:B7')
71+
->getBorders()
72+
->getAllBorders()
73+
->setBorderStyle(Border::BORDER_THIN)
74+
->setColor(new Color(Color::COLOR_BLACK));
75+
$worksheet->getStyle('C6:C7')
76+
->getBorders()
77+
->getAllBorders()
78+
->setBorderStyle(Border::BORDER_THIN)
79+
->setColor(new Color(Color::COLOR_BLACK));
80+
81+
// This is where the error was introduced (!!!)
82+
$worksheet->getColumnDimension('A')->setVisible(false);
83+
$Dompdf = new Dompdf($spreadsheet);
84+
$html = $Dompdf->generateHtmlAll();
85+
$html = preg_replace('/^\\s+/m', '', $html) ?? $html;
86+
$html = preg_replace('/[\\n\\r]/', '', $html) ?? $html;
87+
self::assertStringContainsString(
88+
'table.sheet0 .column0 { display:none }',
89+
$html
90+
);
91+
self::assertStringContainsString(
92+
'<tr class="row0">'
93+
. '<td class="column0 style0 s" style="width:42pt; height:17pt">A</td>'
94+
. '<td class="column1 style0 s" style="width:42pt; height:17pt">B</td>'
95+
. '<td class="column2 style0 s" style="width:42pt; height:17pt">C</td>'
96+
. '</tr>',
97+
$html
98+
);
99+
self::assertStringContainsString(
100+
'<tr class="row1">'
101+
. '<td class="column0 style0" style="width:42pt; height:17pt">&nbsp;</td>'
102+
. '<td class="column1 style1 s style1" style="width:84pt; height:17pt" colspan="2" rowspan="2">Hello World Headline</td>'
103+
. '</tr>',
104+
$html
105+
);
106+
self::assertStringContainsString(
107+
'<tr class="row2">'
108+
. '<td class="column0 style0" style="width:42pt; height:17pt">&nbsp;</td>'
109+
. '</tr>',
110+
$html
111+
);
112+
self::assertStringContainsString(
113+
'<tr class="row3">'
114+
. '<td class="column0 style0" style="width:42pt; height:17pt">&nbsp;</td>'
115+
. '<td class="column1 style2 s style2" style="width:42pt; height:17pt" rowspan="2">Label 1</td>'
116+
. '<td class="column2 style3 s style3" style="width:42pt; height:17pt" rowspan="2">Text 1</td>'
117+
. '</tr>',
118+
$html
119+
);
120+
self::assertStringContainsString(
121+
'<tr class="row4">'
122+
. '<td class="column0 style0" style="width:42pt; height:17pt">&nbsp;</td>'
123+
. '</tr>',
124+
$html
125+
);
126+
self::assertStringContainsString(
127+
'<tr class="row5">'
128+
. '<td class="column0 style0" style="width:42pt; height:17pt">&nbsp;</td>'
129+
. '<td class="column1 style2 s style2" style="width:42pt; height:17pt" rowspan="2">Label 2</td>'
130+
. '<td class="column2 style3 s style3" style="width:42pt; height:17pt" rowspan="2">Text 2</td>'
131+
. '</tr>',
132+
$html
133+
);
134+
self::assertStringContainsString(
135+
'<tr class="row6">'
136+
. '<td class="column0 style0" style="width:42pt; height:17pt">&nbsp;</td>'
137+
. '</tr>',
138+
$html
139+
);
140+
$spreadsheet->disconnectWorksheets();
141+
}
142+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Writer\Html;
6+
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PhpOffice\PhpSpreadsheet\Style\Alignment;
9+
use PhpOffice\PhpSpreadsheet\Style\Border;
10+
use PhpOffice\PhpSpreadsheet\Style\Color;
11+
use PhpOffice\PhpSpreadsheet\Writer\Html;
12+
use PHPUnit\Framework\TestCase;
13+
14+
class HideMergeTest extends TestCase
15+
{
16+
public function testHideWithMerge(): void
17+
{
18+
$spreadsheet = new Spreadsheet();
19+
$worksheet = $spreadsheet->getActiveSheet();
20+
// just some labels for better visualisation of the problem
21+
$worksheet->setCellValue('A1', 'A');
22+
$worksheet->setCellValue('B1', 'B');
23+
$worksheet->setCellValue('C1', 'C');
24+
// setting the row height to better visualize the problem
25+
for ($i = 1; $i <= 10; ++$i) {
26+
$worksheet->getRowDimension($i)->setRowHeight(17);
27+
}
28+
// Headline - merged over two cells AND two rows
29+
$worksheet->mergeCells('B2:C3');
30+
$worksheet->setCellValue('B2', 'Hello World Headline');
31+
$worksheet->getStyle('B2:C3')->getFont()->setBold(true);
32+
$worksheet->getStyle('B2:C3')
33+
->getAlignment()
34+
->setHorizontal(Alignment::HORIZONTAL_CENTER);
35+
$worksheet->getStyle('B2:C3')
36+
->getBorders()
37+
->getAllBorders()
38+
->setBorderStyle(Border::BORDER_THIN)
39+
->setColor(new Color(Color::COLOR_BLACK));
40+
41+
// Content 1 - merge over two rows
42+
$worksheet->mergeCells('B4:B5');
43+
$worksheet->mergeCells('C4:C5');
44+
$worksheet->setCellValue('B4', 'Label 1');
45+
$worksheet->setCellValue('C4', 'Text 1');
46+
$worksheet->getStyle('B4:B5')->getFont()->setBold(true);
47+
$worksheet->getStyle('B4:C5')
48+
->getAlignment()
49+
->setVertical(Alignment::VERTICAL_CENTER);
50+
$worksheet->getStyle('B4:B5')
51+
->getBorders()
52+
->getAllBorders()
53+
->setBorderStyle(Border::BORDER_THIN)
54+
->setColor(new Color(Color::COLOR_BLACK));
55+
$worksheet->getStyle('C4:C5')
56+
->getBorders()
57+
->getAllBorders()
58+
->setBorderStyle(Border::BORDER_THIN)
59+
->setColor(new Color(Color::COLOR_BLACK));
60+
61+
// Content 2 - merge over two rows
62+
$worksheet->mergeCells('B6:B7');
63+
$worksheet->mergeCells('C6:C7');
64+
$worksheet->setCellValue('B6', 'Label 2');
65+
$worksheet->setCellValue('C6', 'Text 2');
66+
$worksheet->getStyle('B6:B7')->getFont()->setBold(true);
67+
$worksheet->getStyle('B6:C7')
68+
->getAlignment()
69+
->setVertical(Alignment::VERTICAL_CENTER);
70+
$worksheet->getStyle('B6:B7')
71+
->getBorders()
72+
->getAllBorders()
73+
->setBorderStyle(Border::BORDER_THIN)
74+
->setColor(new Color(Color::COLOR_BLACK));
75+
$worksheet->getStyle('C6:C7')
76+
->getBorders()
77+
->getAllBorders()
78+
->setBorderStyle(Border::BORDER_THIN)
79+
->setColor(new Color(Color::COLOR_BLACK));
80+
81+
// This is where the error was introduced (!!!)
82+
$worksheet->getColumnDimension('A')->setVisible(false);
83+
$Dompdf = new Html($spreadsheet);
84+
$html = $Dompdf->generateHtmlAll();
85+
$html = preg_replace('/^\\s+/m', '', $html) ?? $html;
86+
$html = preg_replace('/[\\n\\r]/', '', $html) ?? $html;
87+
self::assertStringContainsString(
88+
'table.sheet0 .column0 { display:none }',
89+
$html
90+
);
91+
self::assertStringContainsString(
92+
'<tr class="row0">'
93+
. '<td class="column0 style0 s">A</td>'
94+
. '<td class="column1 style0 s">B</td>'
95+
. '<td class="column2 style0 s">C</td>'
96+
. '</tr>',
97+
$html
98+
);
99+
self::assertStringContainsString(
100+
'<tr class="row1">'
101+
. '<td class="column0 style0">&nbsp;</td>'
102+
. '<td class="column1 style1 s style1" colspan="2" rowspan="2">Hello World Headline</td>'
103+
. '</tr>',
104+
$html
105+
);
106+
self::assertStringContainsString(
107+
'<tr class="row2">'
108+
. '<td class="column0 style0">&nbsp;</td>'
109+
. '</tr>',
110+
$html
111+
);
112+
self::assertStringContainsString(
113+
'<tr class="row3">'
114+
. '<td class="column0 style0">&nbsp;</td>'
115+
. '<td class="column1 style2 s style2" rowspan="2">Label 1</td>'
116+
. '<td class="column2 style3 s style3" rowspan="2">Text 1</td>'
117+
. '</tr>',
118+
$html
119+
);
120+
self::assertStringContainsString(
121+
'<tr class="row4">'
122+
. '<td class="column0 style0">&nbsp;</td>'
123+
. '</tr>',
124+
$html
125+
);
126+
self::assertStringContainsString(
127+
'<tr class="row5">'
128+
. '<td class="column0 style0">&nbsp;</td>'
129+
. '<td class="column1 style2 s style2" rowspan="2">Label 2</td>'
130+
. '<td class="column2 style3 s style3" rowspan="2">Text 2</td>'
131+
. '</tr>',
132+
$html
133+
);
134+
self::assertStringContainsString(
135+
'<tr class="row6">'
136+
. '<td class="column0 style0">&nbsp;</td>'
137+
. '</tr>',
138+
$html
139+
);
140+
$spreadsheet->disconnectWorksheets();
141+
}
142+
}

0 commit comments

Comments
 (0)