Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Style/Conditional.php
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down
11 changes: 9 additions & 2 deletions src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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());
}
Expand Down
85 changes: 54 additions & 31 deletions src/PhpSpreadsheet/Writer/Html.php
Original file line number Diff line number Diff line change
Expand Up @@ -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())
);
}
Expand Down Expand Up @@ -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 = [];
Expand All @@ -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.
*
Expand All @@ -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;
}
Expand Down Expand Up @@ -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 .= ' <tr style="' . $style . '">' . PHP_EOL;
if ($style === '') {
$html .= ' <tr>' . PHP_EOL;
} else {
$html .= ' <tr style="' . $style . '">' . PHP_EOL;
}
}

return $html;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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"';
}
}
}

Expand Down Expand Up @@ -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;
Expand Down
10 changes: 5 additions & 5 deletions tests/PhpSpreadsheetTests/Writer/Dompdf/HideMergeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public function testHideWithMerge(): void
self::assertStringContainsString(
'<tr class="row1">'
. '<td class="column0 style0" style="width:42pt; height:17pt">&nbsp;</td>'
. '<td class="column1 style1 s style1" style="width:84pt; height:17pt" colspan="2" rowspan="2">Hello World Headline</td>'
. '<td class="column1 style1 s style1" colspan="2" rowspan="2" style="width:84pt; height:17pt">Hello World Headline</td>'
. '</tr>',
$html
);
Expand All @@ -112,8 +112,8 @@ public function testHideWithMerge(): void
self::assertStringContainsString(
'<tr class="row3">'
. '<td class="column0 style0" style="width:42pt; height:17pt">&nbsp;</td>'
. '<td class="column1 style2 s style2" style="width:42pt; height:17pt" rowspan="2">Label 1</td>'
. '<td class="column2 style3 s style3" style="width:42pt; height:17pt" rowspan="2">Text 1</td>'
. '<td class="column1 style2 s style2" rowspan="2" style="width:42pt; height:17pt">Label 1</td>'
. '<td class="column2 style3 s style3" rowspan="2" style="width:42pt; height:17pt">Text 1</td>'
. '</tr>',
$html
);
Expand All @@ -126,8 +126,8 @@ public function testHideWithMerge(): void
self::assertStringContainsString(
'<tr class="row5">'
. '<td class="column0 style0" style="width:42pt; height:17pt">&nbsp;</td>'
. '<td class="column1 style2 s style2" style="width:42pt; height:17pt" rowspan="2">Label 2</td>'
. '<td class="column2 style3 s style3" style="width:42pt; height:17pt" rowspan="2">Text 2</td>'
. '<td class="column1 style2 s style2" rowspan="2" style="width:42pt; height:17pt">Label 2</td>'
. '<td class="column2 style3 s style3" rowspan="2" style="width:42pt; height:17pt">Text 2</td>'
. '</tr>',
$html
);
Expand Down
17 changes: 9 additions & 8 deletions tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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('<td style="text-align:right;">1</td>', $html);
self::assertStringContainsString('<td style="text-align:left;">Hello</td>', $html);
self::assertStringContainsString('<td data-type="b" style="text-align:center;">VRAI</td>', $html);
self::assertStringContainsString('<td data-type="b" style="text-align:center;">FAUX</td>', $html);
self::assertStringContainsString('<td data-type="s" style="text-align:left;">1</td>', $html);
self::assertStringContainsString('<td style="text-align:left;">AB</td>', $html);
self::assertStringContainsString('<td style="text-align:right;">3</td>', $html);
self::assertStringContainsString('<td class="gridlines gridlinesp" style="text-align:right;">1</td>', $html);
self::assertStringContainsString('<td class="gridlines gridlinesp" style="text-align:left;">Hello</td>', $html);
self::assertStringContainsString('<td data-type="b" class="gridlines gridlinesp" style="text-align:center;">VRAI</td>', $html);
self::assertStringContainsString('<td data-type="b" class="gridlines gridlinesp" style="text-align:center;">FAUX</td>', $html);
self::assertStringContainsString('<td data-type="s" class="gridlines gridlinesp" style="text-align:left;">1</td>', $html);
self::assertStringContainsString('<td class="gridlines gridlinesp" style="text-align:left;">AB</td>', $html);
self::assertStringContainsString('<td class="gridlines gridlinesp" style="text-align:right;">3</td>', $html);

$reloaded = $this->writeAndReload($spreadsheet, 'Html', null, $this->setBetter(...));
$spreadsheet->disconnectWorksheets();
Expand Down
7 changes: 6 additions & 1 deletion tests/PhpSpreadsheetTests/Writer/Html/Issue3678Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => [
Expand All @@ -34,7 +36,10 @@ public function testInlineAndNot(): void
self::assertStringContainsString('.n { text-align:right }', $html);
$writer->setUseInlineCss(true);
$html = $writer->generateHtmlAll();
self::assertStringContainsString('<td style="' . $style2 . '" class="gridlines gridlinesp">1</td>', $html);
self::assertStringContainsString('<td class="gridlinesp" style="' . $style2 . '">1</td>', $html);
$sheet->setPrintGridlines(false);
$html = $writer->generateHtmlAll();
self::assertStringContainsString('<td style="' . $style2 . '">1</td>', $html);
$spreadsheet->disconnectWorksheets();
}
}
48 changes: 48 additions & 0 deletions tests/PhpSpreadsheetTests/Writer/Html/Issue4539Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Writer\Html;

use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\Writer\Html;
use PHPUnit\Framework\TestCase;

class Issue4539Test extends TestCase
{
public function testInlineAndNot(): void
{
$infile = 'tests/data/Reader/XLSX/issue.4539.xlsx';
$reader = new XlsxReader();
$spreadsheet = $reader->load($infile);
$writer = new Html($spreadsheet);
$writer->setConditionalFormatting(true);
$writer->setUseInlineCss(true);
$html = $writer->generateHtmlAll();
$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>';
self::assertStringContainsString($expected, $html, 'inline conditional style');
$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>';
self::assertStringContainsString($expected, $html, 'inline no conditional style');
$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>';
self::assertStringContainsString($expected, $html, 'inline border-top');
$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>';
self::assertStringContainsString($expected, $html, 'inline border-top and bold');
$expected = '<td style="vertical-align:bottom; color:#000000; font-family:\'Aptos Narrow\'; font-size:12pt; text-align:right; width:102pt">3</td>';
self::assertStringContainsString($expected, $html, 'inline nomatch');

$writer->setUseInlineCss(false);
$html = $writer->generateHtmlAll();
$expected = '<td class="column0 style0 n" style="color:#000000;background-color:#5A8AC6;">5</td>';
self::assertStringContainsString($expected, $html, 'notinline conditional style');
$expected = '<td class="column0 style1 s">Column Heading</td>';
self::assertStringContainsString($expected, $html, 'notinline no conditional style');
$expected = '<td class="column0 style0 n" style="border-top:1px solid #92D050 !important;color:#000000;">1</td>';
self::assertStringContainsString($expected, $html, 'notinline border-top');
$expected = '<td class="column0 style0 n" style="border-top:1px solid #FF0000 !important;font-weight:bold;color:#000000;">2</td>';
self::assertStringContainsString($expected, $html, 'notinline border-top bold');
$expected = '<td class="column0 style0 n">3</td>';
self::assertStringContainsString($expected, $html, 'notinline nomatch');

$spreadsheet->disconnectWorksheets();
}
}
6 changes: 3 additions & 3 deletions tests/PhpSpreadsheetTests/Writer/Html/NoTitleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public function testNoTitle(): void
$writer->setUseInlineCss(true);
$html = $writer->generateHTMLAll();
self::assertStringContainsString('<title>Sheet1</title>', $html);
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);
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);
$writer->setUseInlineCss(false);
$html = $writer->generateHTMLAll();
self::assertStringContainsString('<td class="column2 style0 s">C1</td>', $html);
Expand Down Expand Up @@ -55,8 +55,8 @@ public function testHideSomeGridlines(): void
$writer = new Html($spreadsheet);
$writer->setUseInlineCss(true);
$html = $writer->generateHTMLAll();
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);
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);
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);
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);
$spreadsheet->disconnectWorksheets();
}
}
Loading