Skip to content

Commit 4be3008

Browse files
committed
Writer Mpdf and Tcpdf Borders on Merged Cells
Fix #3557. Borders around merged cells are not handled correctly for Mpdf. Although a perfectly acceptable workaround is suggested in the issue, it would be better if things just worked without the workaround. Html and Dompdf work with the existing code. As it turns out, Tcpdf does not work, but for a different reason than Mpdf. Mpdf was not working because Mpdf does not honor the `!important` attribute in Css. We can get it working almost perfectly by suppressing `border*:none`; the exception is fairly Byzantine, and I'll be glad to discuss the matter should anyone report a problem with it. At any rate, it's not working now in the exception case, so we won't be any worse off. Tcpdf was not working because the merging of attributes happened only when `useInlineCss` was not being used, but Tcpdf does use it. Merging of border attributes is now added for useInlineCss.
1 parent 2ed696f commit 4be3008

File tree

3 files changed

+112
-7
lines changed

3 files changed

+112
-7
lines changed

src/PhpSpreadsheet/Writer/Html.php

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,9 @@ private function mapHAlign(string $hAlign): string
241241
return Alignment::HORIZONTAL_ALIGNMENT_FOR_HTML[$hAlign] ?? '';
242242
}
243243

244+
const BORDER_NONE = 'none';
244245
const BORDER_ARR = [
245-
Border::BORDER_NONE => 'none',
246+
Border::BORDER_NONE => self::BORDER_NONE,
246247
Border::BORDER_DASHDOT => '1px dashed',
247248
Border::BORDER_DASHDOTDOT => '1px dotted',
248249
Border::BORDER_DASHED => '1px dashed',
@@ -263,7 +264,7 @@ private function mapHAlign(string $hAlign): string
263264
*/
264265
private function mapBorderStyle($borderStyle): string
265266
{
266-
return array_key_exists($borderStyle, self::BORDER_ARR) ? self::BORDER_ARR[$borderStyle] : '1px solid';
267+
return self::BORDER_ARR[$borderStyle] ?? '1px solid';
267268
}
268269

269270
/**
@@ -1069,10 +1070,26 @@ private function createCSSStyleBorders(Borders $borders): array
10691070
$css = [];
10701071

10711072
// Create CSS
1072-
$css['border-bottom'] = $this->createCSSStyleBorder($borders->getBottom());
1073-
$css['border-top'] = $this->createCSSStyleBorder($borders->getTop());
1074-
$css['border-left'] = $this->createCSSStyleBorder($borders->getLeft());
1075-
$css['border-right'] = $this->createCSSStyleBorder($borders->getRight());
1073+
if (!($this instanceof Pdf\Mpdf)) {
1074+
$css['border-bottom'] = $this->createCSSStyleBorder($borders->getBottom());
1075+
$css['border-top'] = $this->createCSSStyleBorder($borders->getTop());
1076+
$css['border-left'] = $this->createCSSStyleBorder($borders->getLeft());
1077+
$css['border-right'] = $this->createCSSStyleBorder($borders->getRight());
1078+
} else {
1079+
// Mpdf doesn't process !important, so omit unimportant border none
1080+
if ($borders->getBottom()->getBorderStyle() !== Border::BORDER_NONE) {
1081+
$css['border-bottom'] = $this->createCSSStyleBorder($borders->getBottom());
1082+
}
1083+
if ($borders->getTop()->getBorderStyle() !== Border::BORDER_NONE) {
1084+
$css['border-top'] = $this->createCSSStyleBorder($borders->getTop());
1085+
}
1086+
if ($borders->getLeft()->getBorderStyle() !== Border::BORDER_NONE) {
1087+
$css['border-left'] = $this->createCSSStyleBorder($borders->getLeft());
1088+
}
1089+
if ($borders->getRight()->getBorderStyle() !== Border::BORDER_NONE) {
1090+
$css['border-right'] = $this->createCSSStyleBorder($borders->getRight());
1091+
}
1092+
}
10761093

10771094
return $css;
10781095
}
@@ -1087,7 +1104,7 @@ private function createCSSStyleBorder(Border $border): string
10871104
// Create CSS - add !important to non-none border styles for merged cells
10881105
$borderStyle = $this->mapBorderStyle($border->getBorderStyle());
10891106

1090-
return $borderStyle . ' #' . $border->getColor()->getRGB() . (($borderStyle == 'none') ? '' : ' !important');
1107+
return $borderStyle . ' #' . $border->getColor()->getRGB() . (($borderStyle === self::BORDER_NONE) ? '' : ' !important');
10911108
}
10921109

10931110
/**
@@ -1507,6 +1524,14 @@ private function generateRow(Worksheet $worksheet, array $values, int $row, stri
15071524
$endCellCoord = Coordinate::stringFromColumnIndex($colNum + $colSpan) . ($row + $rowSpan);
15081525
if (!$this->useInlineCss) {
15091526
$cssClass .= ' style' . $worksheet->getCell($endCellCoord)->getXfIndex();
1527+
} else {
1528+
$endBorders = $this->spreadsheet->getCellXfByIndex($worksheet->getCell($endCellCoord)->getXfIndex())->getBorders();
1529+
$altBorders = $this->createCSSStyleBorders($endBorders);
1530+
foreach ($altBorders as $altKey => $altValue) {
1531+
if (str_contains($altValue, '!important')) {
1532+
$cssClass[$altKey] = $altValue;
1533+
}
1534+
}
15101535
}
15111536
}
15121537

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Writer\Mpdf;
6+
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PhpOffice\PhpSpreadsheet\Style\Border;
9+
use PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf;
10+
use PHPUnit\Framework\TestCase;
11+
12+
class MergedBorderTest extends TestCase
13+
{
14+
public static function testMergedBorder(): void
15+
{
16+
$spreadsheet = new Spreadsheet();
17+
$sheet = $spreadsheet->getActiveSheet();
18+
$target = 'A2:B5';
19+
$sheet->mergeCells($target);
20+
$sheet->setCellValue('A2', 'Planning');
21+
$sheet->getStyle($target)->applyFromArray([
22+
'borders' => [
23+
'outline' => [
24+
'borderStyle' => Border::BORDER_HAIR,
25+
'color' => ['rgb' => 'FF0000'],
26+
],
27+
],
28+
]);
29+
$sheet->setSelectedCells('D1');
30+
$sheet->setCellValue('D1', 'Edge');
31+
$sheet->setCellValue('D5', 'Edge');
32+
$sheet->setShowGridlines(false);
33+
$writer = new Mpdf($spreadsheet);
34+
$html = $writer->generateHtmlAll();
35+
self::assertSame(0, preg_match('/border-(top|bottom|right|left):none #000000;/', $html));
36+
self::assertSame(1, preg_match('/border-top:1px solid #FF0000 !important; border-left:1px solid #FF0000 !important;/', $html));
37+
self::assertSame(1, preg_match('/border-bottom:1px solid #FF0000 !important; border-left:1px solid #FF0000 !important;/', $html));
38+
self::assertSame(1, preg_match('/border-top:1px solid #FF0000 !important; border-right:1px solid #FF0000 !important;/', $html));
39+
self::assertSame(1, preg_match('/border-bottom:1px solid #FF0000 !important; border-right:1px solid #FF0000 !important;/', $html));
40+
$spreadsheet->disconnectWorksheets();
41+
}
42+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Writer\Tcpdf;
6+
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PhpOffice\PhpSpreadsheet\Style\Border;
9+
use PhpOffice\PhpSpreadsheet\Writer\Pdf\Tcpdf;
10+
use PHPUnit\Framework\TestCase;
11+
12+
class MergedBorderTest extends TestCase
13+
{
14+
public static function testMergedBorder(): void
15+
{
16+
$spreadsheet = new Spreadsheet();
17+
$sheet = $spreadsheet->getActiveSheet();
18+
$target = 'A2:B5';
19+
$sheet->mergeCells($target);
20+
$sheet->setCellValue('A2', 'Planning');
21+
$sheet->getStyle($target)->applyFromArray([
22+
'borders' => [
23+
'outline' => [
24+
'borderStyle' => Border::BORDER_HAIR,
25+
'color' => ['rgb' => 'FF0000'],
26+
],
27+
],
28+
]);
29+
$sheet->setSelectedCells('D1');
30+
$sheet->setCellValue('D1', 'Edge');
31+
$sheet->setCellValue('D5', 'Edge');
32+
$sheet->setShowGridlines(false);
33+
$writer = new Tcpdf($spreadsheet);
34+
$html = $writer->generateHtmlAll();
35+
self::assertSame(1, preg_match('/border-bottom:1px solid #FF0000 !important; border-top:1px solid #FF0000 !important; border-left:1px solid #FF0000 !important; border-right:1px solid #FF0000 !important; color:#000000;[^>]+ colspan="2" rowspan="4"/', $html));
36+
$spreadsheet->disconnectWorksheets();
37+
}
38+
}

0 commit comments

Comments
 (0)