diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d30c80349..a86794c05b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Fixed +- Html Writer Conditional Formatting Inline Css. [Issue #4539](https://github.com/PHPOffice/PhpSpreadsheet/issues/4539) [PR #4541](https://github.com/PHPOffice/PhpSpreadsheet/pull/4541) - 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) - 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) - Xlsx Reader use dynamic arrays if spreadsheet did so. [PR #4533](https://github.com/PHPOffice/PhpSpreadsheet/pull/4533) diff --git a/src/PhpSpreadsheet/Style/Conditional.php b/src/PhpSpreadsheet/Style/Conditional.php index 736b72be5e..2ef7cacd79 100644 --- a/src/PhpSpreadsheet/Style/Conditional.php +++ b/src/PhpSpreadsheet/Style/Conditional.php @@ -272,7 +272,7 @@ public function addCondition($condition): static public function getStyle(mixed $cellData = null): Style { if ($this->conditionType === self::CONDITION_COLORSCALE && $cellData !== null && $this->colorScale !== null && is_numeric($cellData)) { - $style = new Style(); + $style = new Style(isConditional: true); $style->getFill()->setFillType(Fill::FILL_SOLID); $style->getFill()->getStartColor()->setARGB($this->colorScale->getColorForValue((float) $cellData)); diff --git a/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php b/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php index 10f3ba65dd..645f2387f5 100644 --- a/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php +++ b/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php @@ -14,7 +14,10 @@ class StyleMerger public function __construct(Style $baseStyle) { - $this->baseStyle = $baseStyle; + // Setting to $baseStyle sometimes causes problems later on. + $array = $baseStyle->exportArray(); + $this->baseStyle = new Style(); + $this->baseStyle->applyFromArray($array); } public function getStyle(): Style @@ -75,7 +78,11 @@ protected function mergeBordersStyle(Borders $baseBordersStyle, Borders $borders protected function mergeBorderStyle(Border $baseBorderStyle, Border $borderStyle): void { - $baseBorderStyle->setBorderStyle($borderStyle->getBorderStyle()); + if ($borderStyle->getBorderStyle() !== Border::BORDER_OMIT) { + $baseBorderStyle->setBorderStyle( + $borderStyle->getBorderStyle() + ); + } if ($borderStyle->getColor()->getARGB() !== null) { $baseBorderStyle->setColor($borderStyle->getColor()); } diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index c79b5737a9..d783ec9450 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -1074,13 +1074,13 @@ public function buildCSS(bool $generateSurroundingHTML = true): array * * @return string[] */ - private function createCSSStyle(Style $style): array + private function createCSSStyle(Style $style, bool $conditional = false): array { // Create CSS return array_merge( - $this->createCSSStyleAlignment($style->getAlignment()), + $conditional ? [] : $this->createCSSStyleAlignment($style->getAlignment()), $this->createCSSStyleBorders($style->getBorders()), - $this->createCSSStyleFont($style->getFont()), + $this->createCSSStyleFont($style->getFont(), conditional: $conditional), $this->createCSSStyleFill($style->getFill()) ); } @@ -1124,7 +1124,7 @@ private function createCSSStyleAlignment(Alignment $alignment): array * * @return string[] */ - private function createCSSStyleFont(Font $font, bool $useDefaults = false): array + private function createCSSStyleFont(Font $font, bool $useDefaults = false, bool $conditional = false): array { // Construct CSS $css = []; @@ -1151,12 +1151,29 @@ private function createCSSStyleFont(Font $font, bool $useDefaults = false): arra } $css['color'] = '#' . $font->getColor()->getRGB(); - $css['font-family'] = '\'' . htmlspecialchars((string) $font->getName(), ENT_QUOTES) . '\''; - $css['font-size'] = $font->getSize() . 'pt'; + if (!$conditional) { + $css['font-family'] = '\'' . htmlspecialchars((string) $font->getName(), ENT_QUOTES) . '\''; + $css['font-size'] = $font->getSize() . 'pt'; + } return $css; } + /** + * @param string[] $css + */ + private function styleBorder(array &$css, string $index, Border $border): void + { + $borderStyle = $border->getBorderStyle(); + // Mpdf doesn't process !important, so omit unimportant border none + if ($borderStyle === Border::BORDER_NONE && $this instanceof Pdf\Mpdf) { + return; + } + if ($borderStyle !== Border::BORDER_OMIT) { + $css[$index] = $this->createCSSStyleBorder($border); + } + } + /** * Create CSS style. * @@ -1170,26 +1187,10 @@ private function createCSSStyleBorders(Borders $borders): array $css = []; // Create CSS - if (!($this instanceof Pdf\Mpdf)) { - $css['border-bottom'] = $this->createCSSStyleBorder($borders->getBottom()); - $css['border-top'] = $this->createCSSStyleBorder($borders->getTop()); - $css['border-left'] = $this->createCSSStyleBorder($borders->getLeft()); - $css['border-right'] = $this->createCSSStyleBorder($borders->getRight()); - } else { - // Mpdf doesn't process !important, so omit unimportant border none - if ($borders->getBottom()->getBorderStyle() !== Border::BORDER_NONE) { - $css['border-bottom'] = $this->createCSSStyleBorder($borders->getBottom()); - } - if ($borders->getTop()->getBorderStyle() !== Border::BORDER_NONE) { - $css['border-top'] = $this->createCSSStyleBorder($borders->getTop()); - } - if ($borders->getLeft()->getBorderStyle() !== Border::BORDER_NONE) { - $css['border-left'] = $this->createCSSStyleBorder($borders->getLeft()); - } - if ($borders->getRight()->getBorderStyle() !== Border::BORDER_NONE) { - $css['border-right'] = $this->createCSSStyleBorder($borders->getRight()); - } - } + $this->styleBorder($css, 'border-bottom', $borders->getBottom()); + $this->styleBorder($css, 'border-top', $borders->getTop()); + $this->styleBorder($css, 'border-left', $borders->getLeft()); + $this->styleBorder($css, 'border-right', $borders->getRight()); return $css; } @@ -1393,7 +1394,11 @@ private function generateRowStart(Worksheet $worksheet, int $sheetIndex, int $ro $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $row]) ? $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $row]) : ''; - $html .= ' ' . PHP_EOL; + if ($style === '') { + $html .= ' ' . PHP_EOL; + } else { + $html .= ' ' . PHP_EOL; + } } return $html; @@ -1605,6 +1610,7 @@ private function generateRowWriteCell( $html .= ' data-type="' . DataType::TYPE_STRING . '"'; } } + $holdCss = ''; if (!$this->useInlineCss && !$this->isPdf && is_string($cssClass)) { $html .= ' class="' . $cssClass . '"'; if ($htmlx) { @@ -1650,9 +1656,17 @@ private function generateRowWriteCell( $xcssClass['position'] = 'relative'; } /** @var string[] $xcssClass */ - $html .= ' style="' . $this->assembleCSS($xcssClass) . '"'; + $holdCss = $this->assembleCSS($xcssClass); if ($this->useInlineCss) { - $html .= ' class="gridlines gridlinesp"'; + $prntgrid = $worksheet->getPrintGridlines(); + $viewgrid = $this->isPdf ? $prntgrid : $worksheet->getShowGridlines(); + if ($viewgrid && $prntgrid) { + $html .= ' class="gridlines gridlinesp"'; + } elseif ($viewgrid) { + $html .= ' class="gridlines"'; + } elseif ($prntgrid) { + $html .= ' class="gridlinesp"'; + } } } @@ -1700,14 +1714,23 @@ private function generateRowWriteCell( } } if ($matched) { - $styles = $this->createCSSStyle($styleMerger->getStyle()); + $styles = $this->createCSSStyle($styleMerger->getStyle(), true); $html .= ' style="'; + if ($holdCss !== '') { + $html .= "$holdCss; "; + $holdCss = ''; + } foreach ($styles as $key => $value) { - $html .= $key . ':' . $value . ';'; + if (!str_starts_with($key, 'border-') || $value !== 'none #000000') { + $html .= $key . ':' . $value . ';'; + } } $html .= '"'; } } + if ($holdCss !== '') { + $html .= ' style="' . $holdCss . '"'; + } $html .= '>'; $html .= $htmlx; diff --git a/tests/PhpSpreadsheetTests/Writer/Dompdf/HideMergeTest.php b/tests/PhpSpreadsheetTests/Writer/Dompdf/HideMergeTest.php index 7bbe5c6cc3..a8b365baad 100644 --- a/tests/PhpSpreadsheetTests/Writer/Dompdf/HideMergeTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Dompdf/HideMergeTest.php @@ -99,7 +99,7 @@ public function testHideWithMerge(): void self::assertStringContainsString( '' . ' ' - . 'Hello World Headline' + . 'Hello World Headline' . '', $html ); @@ -112,8 +112,8 @@ public function testHideWithMerge(): void self::assertStringContainsString( '' . ' ' - . 'Label 1' - . 'Text 1' + . 'Label 1' + . 'Text 1' . '', $html ); @@ -126,8 +126,8 @@ public function testHideWithMerge(): void self::assertStringContainsString( '' . ' ' - . 'Label 2' - . 'Text 2' + . 'Label 2' + . 'Text 2' . '', $html ); diff --git a/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php b/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php index 71d81122a9..e033c8ab4b 100644 --- a/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php @@ -139,6 +139,7 @@ public function testInline(): void { $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); + $sheet->setPrintGridlines(true); $sheet->getCell('A1')->setValue(1); $sheet->getCell('B1')->setValue('Hello'); $sheet->getCell('C1')->setValue(true); @@ -153,15 +154,15 @@ public function testInline(): void $writer->setUseInlineCss(true); $html = $writer->generateHtmlAll(); $html = str_replace('vertical-align:bottom; color:#000000; font-family:\'Calibri\'; font-size:11pt; ', '', $html); - $html = str_replace(' width:42pt" class="gridlines gridlinesp"', '"', $html); + $html = str_replace(' width:42pt"', '"', $html); self::assertStringNotContainsString('TRUE', $html); - self::assertStringContainsString('1', $html); - self::assertStringContainsString('Hello', $html); - self::assertStringContainsString('VRAI', $html); - self::assertStringContainsString('FAUX', $html); - self::assertStringContainsString('1', $html); - self::assertStringContainsString('AB', $html); - self::assertStringContainsString('3', $html); + self::assertStringContainsString('1', $html); + self::assertStringContainsString('Hello', $html); + self::assertStringContainsString('VRAI', $html); + self::assertStringContainsString('FAUX', $html); + self::assertStringContainsString('1', $html); + self::assertStringContainsString('AB', $html); + self::assertStringContainsString('3', $html); $reloaded = $this->writeAndReload($spreadsheet, 'Html', null, $this->setBetter(...)); $spreadsheet->disconnectWorksheets(); diff --git a/tests/PhpSpreadsheetTests/Writer/Html/Issue3678Test.php b/tests/PhpSpreadsheetTests/Writer/Html/Issue3678Test.php index e4f292c2da..6177434639 100644 --- a/tests/PhpSpreadsheetTests/Writer/Html/Issue3678Test.php +++ b/tests/PhpSpreadsheetTests/Writer/Html/Issue3678Test.php @@ -15,6 +15,8 @@ public function testInlineAndNot(): void { $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); + $sheet->setShowGridlines(false); + $sheet->setPrintGridlines(true); $sheet->getCell('A1')->setValue(1); $styleArray = [ 'fill' => [ @@ -34,7 +36,10 @@ public function testInlineAndNot(): void self::assertStringContainsString('.n { text-align:right }', $html); $writer->setUseInlineCss(true); $html = $writer->generateHtmlAll(); - self::assertStringContainsString('1', $html); + self::assertStringContainsString('1', $html); + $sheet->setPrintGridlines(false); + $html = $writer->generateHtmlAll(); + self::assertStringContainsString('1', $html); $spreadsheet->disconnectWorksheets(); } } diff --git a/tests/PhpSpreadsheetTests/Writer/Html/Issue4539Test.php b/tests/PhpSpreadsheetTests/Writer/Html/Issue4539Test.php new file mode 100644 index 0000000000..ee9924c7af --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/Issue4539Test.php @@ -0,0 +1,48 @@ +load($infile); + $writer = new Html($spreadsheet); + $writer->setConditionalFormatting(true); + $writer->setUseInlineCss(true); + $html = $writer->generateHtmlAll(); + $expected = '5'; + self::assertStringContainsString($expected, $html, 'inline conditional style'); + $expected = 'Column Heading'; + self::assertStringContainsString($expected, $html, 'inline no conditional style'); + $expected = '1'; + self::assertStringContainsString($expected, $html, 'inline border-top'); + $expected = '2'; + self::assertStringContainsString($expected, $html, 'inline border-top and bold'); + $expected = '3'; + self::assertStringContainsString($expected, $html, 'inline nomatch'); + + $writer->setUseInlineCss(false); + $html = $writer->generateHtmlAll(); + $expected = '5'; + self::assertStringContainsString($expected, $html, 'notinline conditional style'); + $expected = 'Column Heading'; + self::assertStringContainsString($expected, $html, 'notinline no conditional style'); + $expected = '1'; + self::assertStringContainsString($expected, $html, 'notinline border-top'); + $expected = '2'; + self::assertStringContainsString($expected, $html, 'notinline border-top bold'); + $expected = '3'; + self::assertStringContainsString($expected, $html, 'notinline nomatch'); + + $spreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Html/NoTitleTest.php b/tests/PhpSpreadsheetTests/Writer/Html/NoTitleTest.php index 7faf8a6ad3..4a6a5f4c2d 100644 --- a/tests/PhpSpreadsheetTests/Writer/Html/NoTitleTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Html/NoTitleTest.php @@ -23,7 +23,7 @@ public function testNoTitle(): void $writer->setUseInlineCss(true); $html = $writer->generateHTMLAll(); self::assertStringContainsString('Sheet1', $html); - self::assertStringContainsString('C1', $html); + self::assertStringContainsString('C1', $html); $writer->setUseInlineCss(false); $html = $writer->generateHTMLAll(); self::assertStringContainsString('C1', $html); @@ -55,8 +55,8 @@ public function testHideSomeGridlines(): void $writer = new Html($spreadsheet); $writer->setUseInlineCss(true); $html = $writer->generateHTMLAll(); - self::assertStringContainsString('7', $html); - self::assertStringContainsString('19', $html); + self::assertStringContainsString('7', $html); + self::assertStringContainsString('19', $html); $spreadsheet->disconnectWorksheets(); } } diff --git a/tests/PhpSpreadsheetTests/Writer/Mpdf/HideMergeTest.php b/tests/PhpSpreadsheetTests/Writer/Mpdf/HideMergeTest.php index d6f9b02702..9753bed441 100644 --- a/tests/PhpSpreadsheetTests/Writer/Mpdf/HideMergeTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Mpdf/HideMergeTest.php @@ -93,7 +93,7 @@ public function testHideWithMerge(): void ); self::assertStringContainsString( '' - . 'Hello World Headline' + . 'Hello World Headline' . '', $html ); @@ -104,8 +104,8 @@ public function testHideWithMerge(): void ); self::assertStringContainsString( '' - . 'Label 1' - . 'Text 1' + . 'Label 1' + . 'Text 1' . '', $html ); @@ -116,8 +116,8 @@ public function testHideWithMerge(): void ); self::assertStringContainsString( '' - . 'Label 2' - . 'Text 2' + . 'Label 2' + . 'Text 2' . '', $html ); diff --git a/tests/PhpSpreadsheetTests/Writer/Tcpdf/HideMergeTest.php b/tests/PhpSpreadsheetTests/Writer/Tcpdf/HideMergeTest.php index 04284c0814..8eea9a1b42 100644 --- a/tests/PhpSpreadsheetTests/Writer/Tcpdf/HideMergeTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Tcpdf/HideMergeTest.php @@ -17,6 +17,7 @@ public function testHideWithMerge(): void { $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); + $worksheet->setPrintGridlines(true); // just some labels for better visualisation of the problem $worksheet->setCellValue('A1', 'A'); $worksheet->setCellValue('B1', 'B'); @@ -87,15 +88,15 @@ public function testHideWithMerge(): void self::assertStringContainsString( '' . '' - . 'B' - . 'C' + . 'B' + . 'C' . '', $html ); self::assertStringContainsString( '' . '' - . 'Hello World Headline' + . 'Hello World Headline' . '', $html ); @@ -109,16 +110,16 @@ public function testHideWithMerge(): void self::assertStringContainsString( '' . '' - . 'Label 1' - . 'Text 1' + . 'Label 1' + . 'Text 1' . '', $html ); self::assertStringContainsString( '' . '' - . 'Label 2' - . 'Text 2' + . 'Label 2' + . 'Text 2' . '', $html ); diff --git a/tests/PhpSpreadsheetTests/Writer/Tcpdf/MergedBorderTest.php b/tests/PhpSpreadsheetTests/Writer/Tcpdf/MergedBorderTest.php index 5b61c649aa..9c3df41f8c 100644 --- a/tests/PhpSpreadsheetTests/Writer/Tcpdf/MergedBorderTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Tcpdf/MergedBorderTest.php @@ -32,7 +32,7 @@ public static function testMergedBorder(): void $sheet->setShowGridlines(false); $writer = new Tcpdf($spreadsheet); $html = $writer->generateHtmlAll(); - 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)); + self::assertSame(1, preg_match('/ colspan="2" rowspan="4" style="vertical-align:bottom; 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;[^>]+/', $html)); $spreadsheet->disconnectWorksheets(); } } diff --git a/tests/data/Reader/XLSX/issue.4539.xlsx b/tests/data/Reader/XLSX/issue.4539.xlsx new file mode 100644 index 0000000000..e44a88d5ba Binary files /dev/null and b/tests/data/Reader/XLSX/issue.4539.xlsx differ