Skip to content

Commit adb7459

Browse files
authored
Merge pull request #4541 from oleibman/issue4539
Html Writer Conditional Formatting Inline Css
2 parents 994b072 + b489fb3 commit adb7459

File tree

13 files changed

+150
-64
lines changed

13 files changed

+150
-64
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
2929

3030
### Fixed
3131

32+
- Html Writer Conditional Formatting Inline Css. [Issue #4539](https://github.com/PHPOffice/PhpSpreadsheet/issues/4539) [PR #4541](https://github.com/PHPOffice/PhpSpreadsheet/pull/4541)
3233
- Do not use htmlspecialchars when formatting XML. [Issue #4537](https://github.com/PHPOffice/PhpSpreadsheet/issues/4537) [PR #4540](https://github.com/PHPOffice/PhpSpreadsheet/pull/4540)
3334
- Writer Html/Pdf support RTL alignment of tables. [Issue #1104](https://github.com/PHPOffice/PhpSpreadsheet/issues/1104) [PR #4535](https://github.com/PHPOffice/PhpSpreadsheet/pull/4535)
3435
- Xlsx Reader use dynamic arrays if spreadsheet did so. [PR #4533](https://github.com/PHPOffice/PhpSpreadsheet/pull/4533)

src/PhpSpreadsheet/Style/Conditional.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ public function addCondition($condition): static
272272
public function getStyle(mixed $cellData = null): Style
273273
{
274274
if ($this->conditionType === self::CONDITION_COLORSCALE && $cellData !== null && $this->colorScale !== null && is_numeric($cellData)) {
275-
$style = new Style();
275+
$style = new Style(isConditional: true);
276276
$style->getFill()->setFillType(Fill::FILL_SOLID);
277277
$style->getFill()->getStartColor()->setARGB($this->colorScale->getColorForValue((float) $cellData));
278278

src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ class StyleMerger
1414

1515
public function __construct(Style $baseStyle)
1616
{
17-
$this->baseStyle = $baseStyle;
17+
// Setting to $baseStyle sometimes causes problems later on.
18+
$array = $baseStyle->exportArray();
19+
$this->baseStyle = new Style();
20+
$this->baseStyle->applyFromArray($array);
1821
}
1922

2023
public function getStyle(): Style
@@ -75,7 +78,11 @@ protected function mergeBordersStyle(Borders $baseBordersStyle, Borders $borders
7578

7679
protected function mergeBorderStyle(Border $baseBorderStyle, Border $borderStyle): void
7780
{
78-
$baseBorderStyle->setBorderStyle($borderStyle->getBorderStyle());
81+
if ($borderStyle->getBorderStyle() !== Border::BORDER_OMIT) {
82+
$baseBorderStyle->setBorderStyle(
83+
$borderStyle->getBorderStyle()
84+
);
85+
}
7986
if ($borderStyle->getColor()->getARGB() !== null) {
8087
$baseBorderStyle->setColor($borderStyle->getColor());
8188
}

src/PhpSpreadsheet/Writer/Html.php

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,13 +1074,13 @@ public function buildCSS(bool $generateSurroundingHTML = true): array
10741074
*
10751075
* @return string[]
10761076
*/
1077-
private function createCSSStyle(Style $style): array
1077+
private function createCSSStyle(Style $style, bool $conditional = false): array
10781078
{
10791079
// Create CSS
10801080
return array_merge(
1081-
$this->createCSSStyleAlignment($style->getAlignment()),
1081+
$conditional ? [] : $this->createCSSStyleAlignment($style->getAlignment()),
10821082
$this->createCSSStyleBorders($style->getBorders()),
1083-
$this->createCSSStyleFont($style->getFont()),
1083+
$this->createCSSStyleFont($style->getFont(), conditional: $conditional),
10841084
$this->createCSSStyleFill($style->getFill())
10851085
);
10861086
}
@@ -1124,7 +1124,7 @@ private function createCSSStyleAlignment(Alignment $alignment): array
11241124
*
11251125
* @return string[]
11261126
*/
1127-
private function createCSSStyleFont(Font $font, bool $useDefaults = false): array
1127+
private function createCSSStyleFont(Font $font, bool $useDefaults = false, bool $conditional = false): array
11281128
{
11291129
// Construct CSS
11301130
$css = [];
@@ -1151,12 +1151,29 @@ private function createCSSStyleFont(Font $font, bool $useDefaults = false): arra
11511151
}
11521152

11531153
$css['color'] = '#' . $font->getColor()->getRGB();
1154-
$css['font-family'] = '\'' . htmlspecialchars((string) $font->getName(), ENT_QUOTES) . '\'';
1155-
$css['font-size'] = $font->getSize() . 'pt';
1154+
if (!$conditional) {
1155+
$css['font-family'] = '\'' . htmlspecialchars((string) $font->getName(), ENT_QUOTES) . '\'';
1156+
$css['font-size'] = $font->getSize() . 'pt';
1157+
}
11561158

11571159
return $css;
11581160
}
11591161

1162+
/**
1163+
* @param string[] $css
1164+
*/
1165+
private function styleBorder(array &$css, string $index, Border $border): void
1166+
{
1167+
$borderStyle = $border->getBorderStyle();
1168+
// Mpdf doesn't process !important, so omit unimportant border none
1169+
if ($borderStyle === Border::BORDER_NONE && $this instanceof Pdf\Mpdf) {
1170+
return;
1171+
}
1172+
if ($borderStyle !== Border::BORDER_OMIT) {
1173+
$css[$index] = $this->createCSSStyleBorder($border);
1174+
}
1175+
}
1176+
11601177
/**
11611178
* Create CSS style.
11621179
*
@@ -1170,26 +1187,10 @@ private function createCSSStyleBorders(Borders $borders): array
11701187
$css = [];
11711188

11721189
// Create CSS
1173-
if (!($this instanceof Pdf\Mpdf)) {
1174-
$css['border-bottom'] = $this->createCSSStyleBorder($borders->getBottom());
1175-
$css['border-top'] = $this->createCSSStyleBorder($borders->getTop());
1176-
$css['border-left'] = $this->createCSSStyleBorder($borders->getLeft());
1177-
$css['border-right'] = $this->createCSSStyleBorder($borders->getRight());
1178-
} else {
1179-
// Mpdf doesn't process !important, so omit unimportant border none
1180-
if ($borders->getBottom()->getBorderStyle() !== Border::BORDER_NONE) {
1181-
$css['border-bottom'] = $this->createCSSStyleBorder($borders->getBottom());
1182-
}
1183-
if ($borders->getTop()->getBorderStyle() !== Border::BORDER_NONE) {
1184-
$css['border-top'] = $this->createCSSStyleBorder($borders->getTop());
1185-
}
1186-
if ($borders->getLeft()->getBorderStyle() !== Border::BORDER_NONE) {
1187-
$css['border-left'] = $this->createCSSStyleBorder($borders->getLeft());
1188-
}
1189-
if ($borders->getRight()->getBorderStyle() !== Border::BORDER_NONE) {
1190-
$css['border-right'] = $this->createCSSStyleBorder($borders->getRight());
1191-
}
1192-
}
1190+
$this->styleBorder($css, 'border-bottom', $borders->getBottom());
1191+
$this->styleBorder($css, 'border-top', $borders->getTop());
1192+
$this->styleBorder($css, 'border-left', $borders->getLeft());
1193+
$this->styleBorder($css, 'border-right', $borders->getRight());
11931194

11941195
return $css;
11951196
}
@@ -1393,7 +1394,11 @@ private function generateRowStart(Worksheet $worksheet, int $sheetIndex, int $ro
13931394
$style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $row])
13941395
? $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $row]) : '';
13951396

1396-
$html .= ' <tr style="' . $style . '">' . PHP_EOL;
1397+
if ($style === '') {
1398+
$html .= ' <tr>' . PHP_EOL;
1399+
} else {
1400+
$html .= ' <tr style="' . $style . '">' . PHP_EOL;
1401+
}
13971402
}
13981403

13991404
return $html;
@@ -1605,6 +1610,7 @@ private function generateRowWriteCell(
16051610
$html .= ' data-type="' . DataType::TYPE_STRING . '"';
16061611
}
16071612
}
1613+
$holdCss = '';
16081614
if (!$this->useInlineCss && !$this->isPdf && is_string($cssClass)) {
16091615
$html .= ' class="' . $cssClass . '"';
16101616
if ($htmlx) {
@@ -1650,9 +1656,17 @@ private function generateRowWriteCell(
16501656
$xcssClass['position'] = 'relative';
16511657
}
16521658
/** @var string[] $xcssClass */
1653-
$html .= ' style="' . $this->assembleCSS($xcssClass) . '"';
1659+
$holdCss = $this->assembleCSS($xcssClass);
16541660
if ($this->useInlineCss) {
1655-
$html .= ' class="gridlines gridlinesp"';
1661+
$prntgrid = $worksheet->getPrintGridlines();
1662+
$viewgrid = $this->isPdf ? $prntgrid : $worksheet->getShowGridlines();
1663+
if ($viewgrid && $prntgrid) {
1664+
$html .= ' class="gridlines gridlinesp"';
1665+
} elseif ($viewgrid) {
1666+
$html .= ' class="gridlines"';
1667+
} elseif ($prntgrid) {
1668+
$html .= ' class="gridlinesp"';
1669+
}
16561670
}
16571671
}
16581672

@@ -1700,14 +1714,23 @@ private function generateRowWriteCell(
17001714
}
17011715
}
17021716
if ($matched) {
1703-
$styles = $this->createCSSStyle($styleMerger->getStyle());
1717+
$styles = $this->createCSSStyle($styleMerger->getStyle(), true);
17041718
$html .= ' style="';
1719+
if ($holdCss !== '') {
1720+
$html .= "$holdCss; ";
1721+
$holdCss = '';
1722+
}
17051723
foreach ($styles as $key => $value) {
1706-
$html .= $key . ':' . $value . ';';
1724+
if (!str_starts_with($key, 'border-') || $value !== 'none #000000') {
1725+
$html .= $key . ':' . $value . ';';
1726+
}
17071727
}
17081728
$html .= '"';
17091729
}
17101730
}
1731+
if ($holdCss !== '') {
1732+
$html .= ' style="' . $holdCss . '"';
1733+
}
17111734

17121735
$html .= '>';
17131736
$html .= $htmlx;

tests/PhpSpreadsheetTests/Writer/Dompdf/HideMergeTest.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public function testHideWithMerge(): void
9999
self::assertStringContainsString(
100100
'<tr class="row1">'
101101
. '<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>'
102+
. '<td class="column1 style1 s style1" colspan="2" rowspan="2" style="width:84pt; height:17pt">Hello World Headline</td>'
103103
. '</tr>',
104104
$html
105105
);
@@ -112,8 +112,8 @@ public function testHideWithMerge(): void
112112
self::assertStringContainsString(
113113
'<tr class="row3">'
114114
. '<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>'
115+
. '<td class="column1 style2 s style2" rowspan="2" style="width:42pt; height:17pt">Label 1</td>'
116+
. '<td class="column2 style3 s style3" rowspan="2" style="width:42pt; height:17pt">Text 1</td>'
117117
. '</tr>',
118118
$html
119119
);
@@ -126,8 +126,8 @@ public function testHideWithMerge(): void
126126
self::assertStringContainsString(
127127
'<tr class="row5">'
128128
. '<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>'
129+
. '<td class="column1 style2 s style2" rowspan="2" style="width:42pt; height:17pt">Label 2</td>'
130+
. '<td class="column2 style3 s style3" rowspan="2" style="width:42pt; height:17pt">Text 2</td>'
131131
. '</tr>',
132132
$html
133133
);

tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ public function testInline(): void
139139
{
140140
$spreadsheet = new Spreadsheet();
141141
$sheet = $spreadsheet->getActiveSheet();
142+
$sheet->setPrintGridlines(true);
142143
$sheet->getCell('A1')->setValue(1);
143144
$sheet->getCell('B1')->setValue('Hello');
144145
$sheet->getCell('C1')->setValue(true);
@@ -153,15 +154,15 @@ public function testInline(): void
153154
$writer->setUseInlineCss(true);
154155
$html = $writer->generateHtmlAll();
155156
$html = str_replace('vertical-align:bottom; color:#000000; font-family:\'Calibri\'; font-size:11pt; ', '', $html);
156-
$html = str_replace(' width:42pt" class="gridlines gridlinesp"', '"', $html);
157+
$html = str_replace(' width:42pt"', '"', $html);
157158
self::assertStringNotContainsString('TRUE', $html);
158-
self::assertStringContainsString('<td style="text-align:right;">1</td>', $html);
159-
self::assertStringContainsString('<td style="text-align:left;">Hello</td>', $html);
160-
self::assertStringContainsString('<td data-type="b" style="text-align:center;">VRAI</td>', $html);
161-
self::assertStringContainsString('<td data-type="b" style="text-align:center;">FAUX</td>', $html);
162-
self::assertStringContainsString('<td data-type="s" style="text-align:left;">1</td>', $html);
163-
self::assertStringContainsString('<td style="text-align:left;">AB</td>', $html);
164-
self::assertStringContainsString('<td style="text-align:right;">3</td>', $html);
159+
self::assertStringContainsString('<td class="gridlines gridlinesp" style="text-align:right;">1</td>', $html);
160+
self::assertStringContainsString('<td class="gridlines gridlinesp" style="text-align:left;">Hello</td>', $html);
161+
self::assertStringContainsString('<td data-type="b" class="gridlines gridlinesp" style="text-align:center;">VRAI</td>', $html);
162+
self::assertStringContainsString('<td data-type="b" class="gridlines gridlinesp" style="text-align:center;">FAUX</td>', $html);
163+
self::assertStringContainsString('<td data-type="s" class="gridlines gridlinesp" style="text-align:left;">1</td>', $html);
164+
self::assertStringContainsString('<td class="gridlines gridlinesp" style="text-align:left;">AB</td>', $html);
165+
self::assertStringContainsString('<td class="gridlines gridlinesp" style="text-align:right;">3</td>', $html);
165166

166167
$reloaded = $this->writeAndReload($spreadsheet, 'Html', null, $this->setBetter(...));
167168
$spreadsheet->disconnectWorksheets();

tests/PhpSpreadsheetTests/Writer/Html/Issue3678Test.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ public function testInlineAndNot(): void
1515
{
1616
$spreadsheet = new Spreadsheet();
1717
$sheet = $spreadsheet->getActiveSheet();
18+
$sheet->setShowGridlines(false);
19+
$sheet->setPrintGridlines(true);
1820
$sheet->getCell('A1')->setValue(1);
1921
$styleArray = [
2022
'fill' => [
@@ -34,7 +36,10 @@ public function testInlineAndNot(): void
3436
self::assertStringContainsString('.n { text-align:right }', $html);
3537
$writer->setUseInlineCss(true);
3638
$html = $writer->generateHtmlAll();
37-
self::assertStringContainsString('<td style="' . $style2 . '" class="gridlines gridlinesp">1</td>', $html);
39+
self::assertStringContainsString('<td class="gridlinesp" style="' . $style2 . '">1</td>', $html);
40+
$sheet->setPrintGridlines(false);
41+
$html = $writer->generateHtmlAll();
42+
self::assertStringContainsString('<td style="' . $style2 . '">1</td>', $html);
3843
$spreadsheet->disconnectWorksheets();
3944
}
4045
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Writer\Html;
6+
7+
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
8+
use PhpOffice\PhpSpreadsheet\Writer\Html;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class Issue4539Test extends TestCase
12+
{
13+
public function testInlineAndNot(): void
14+
{
15+
$infile = 'tests/data/Reader/XLSX/issue.4539.xlsx';
16+
$reader = new XlsxReader();
17+
$spreadsheet = $reader->load($infile);
18+
$writer = new Html($spreadsheet);
19+
$writer->setConditionalFormatting(true);
20+
$writer->setUseInlineCss(true);
21+
$html = $writer->generateHtmlAll();
22+
$expected = '<td style="vertical-align:bottom; color:#000000; font-family:\'Aptos Narrow\'; font-size:12pt; text-align:right; width:102pt; color:#000000;background-color:#5A8AC6;">5</td>';
23+
self::assertStringContainsString($expected, $html, 'inline conditional style');
24+
$expected = '<td style="vertical-align:bottom; font-weight:bold; color:#000000; font-family:\'Aptos Narrow\'; font-size:12pt; text-align:left; width:102pt">Column Heading</td>';
25+
self::assertStringContainsString($expected, $html, 'inline no conditional style');
26+
$expected = '<td style="vertical-align:bottom; color:#000000; font-family:\'Aptos Narrow\'; font-size:12pt; text-align:right; width:102pt; border-top:1px solid #92D050 !important;color:#000000;">1</td>';
27+
self::assertStringContainsString($expected, $html, 'inline border-top');
28+
$expected = '<td style="vertical-align:bottom; color:#000000; font-family:\'Aptos Narrow\'; font-size:12pt; text-align:right; width:102pt; border-top:1px solid #FF0000 !important;font-weight:bold;color:#000000;">2</td>';
29+
self::assertStringContainsString($expected, $html, 'inline border-top and bold');
30+
$expected = '<td style="vertical-align:bottom; color:#000000; font-family:\'Aptos Narrow\'; font-size:12pt; text-align:right; width:102pt">3</td>';
31+
self::assertStringContainsString($expected, $html, 'inline nomatch');
32+
33+
$writer->setUseInlineCss(false);
34+
$html = $writer->generateHtmlAll();
35+
$expected = '<td class="column0 style0 n" style="color:#000000;background-color:#5A8AC6;">5</td>';
36+
self::assertStringContainsString($expected, $html, 'notinline conditional style');
37+
$expected = '<td class="column0 style1 s">Column Heading</td>';
38+
self::assertStringContainsString($expected, $html, 'notinline no conditional style');
39+
$expected = '<td class="column0 style0 n" style="border-top:1px solid #92D050 !important;color:#000000;">1</td>';
40+
self::assertStringContainsString($expected, $html, 'notinline border-top');
41+
$expected = '<td class="column0 style0 n" style="border-top:1px solid #FF0000 !important;font-weight:bold;color:#000000;">2</td>';
42+
self::assertStringContainsString($expected, $html, 'notinline border-top bold');
43+
$expected = '<td class="column0 style0 n">3</td>';
44+
self::assertStringContainsString($expected, $html, 'notinline nomatch');
45+
46+
$spreadsheet->disconnectWorksheets();
47+
}
48+
}

tests/PhpSpreadsheetTests/Writer/Html/NoTitleTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public function testNoTitle(): void
2323
$writer->setUseInlineCss(true);
2424
$html = $writer->generateHTMLAll();
2525
self::assertStringContainsString('<title>Sheet1</title>', $html);
26-
self::assertStringContainsString('<td style="vertical-align:bottom; color:#000000; font-family:\'Calibri\'; font-size:11pt; text-align:left; width:42pt" class="gridlines gridlinesp">C1</td>', $html);
26+
self::assertStringContainsString('<td class="gridlines" style="vertical-align:bottom; color:#000000; font-family:\'Calibri\'; font-size:11pt; text-align:left; width:42pt">C1</td>', $html);
2727
$writer->setUseInlineCss(false);
2828
$html = $writer->generateHTMLAll();
2929
self::assertStringContainsString('<td class="column2 style0 s">C1</td>', $html);
@@ -55,8 +55,8 @@ public function testHideSomeGridlines(): void
5555
$writer = new Html($spreadsheet);
5656
$writer->setUseInlineCss(true);
5757
$html = $writer->generateHTMLAll();
58-
self::assertStringContainsString('<td style="vertical-align:bottom; color:#000000; font-family:\'Calibri\'; font-size:11pt; text-align:right; width:42pt" class="gridlines gridlinesp">7</td>', $html);
59-
self::assertStringContainsString('<td style="vertical-align:bottom; border-bottom:none #808080; border-top:none #808080; border-left:none #808080; border-right:none #808080; color:#000000; font-family:\'Calibri\'; font-size:11pt; text-align:right; width:42pt" class="gridlines gridlinesp">19</td>', $html);
58+
self::assertStringContainsString('<td class="gridlines" style="vertical-align:bottom; color:#000000; font-family:\'Calibri\'; font-size:11pt; text-align:right; width:42pt">7</td>', $html);
59+
self::assertStringContainsString('<td class="gridlines" style="vertical-align:bottom; border-bottom:none #808080; border-top:none #808080; border-left:none #808080; border-right:none #808080; color:#000000; font-family:\'Calibri\'; font-size:11pt; text-align:right; width:42pt">19</td>', $html);
6060
$spreadsheet->disconnectWorksheets();
6161
}
6262
}

0 commit comments

Comments
 (0)