From a68e44eee6705bd04f059dea8236176e85dd5b05 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sun, 1 Dec 2024 05:31:44 -0800 Subject: [PATCH 1/4] Html Reader/Writer Better Handling of Booleans When Html Writer outputs a cell with a boolean value, the result will either be 1 or null-string; neither of these is optimal for anyone looking at the resulting html. Html Reader already has the ability to recognize data types using the html `data-type` attribute, but Html Writer doesn't use it. This PR adds the ability to generate that attribute for booleans. It will generate a string value appropriate for the locale when it encounters a boolean. Html Reader, when it encounters `data-type="b"`, will interpret the result as true if the value is 1 or a string value recognized as true in any locale; it will interpret the result as false if the value is 0, null-string, null, or a string value recognized as false in any locale; if none of the above, it will leave the value as an unchanged string. So, Reader will wind up with the correct result even if its locale is different than what Writer used. Because this is a breaking change, it is opt-in. You need to call `Writer::setBetterBoolean(true)` in order for it take effect. The current default value for that property is false. When it is time to introduce breaking changes (see PR #4240), the default will be changed to true. --- .../Calculation/Calculation.php | 47 ++++++ src/PhpSpreadsheet/Reader/Html.php | 32 ++++ src/PhpSpreadsheet/Writer/Html.php | 49 +++++- .../Writer/Html/BetterBooleanTest.php | 140 ++++++++++++++++++ 4 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 0cc64c32e8..ab52fb1af9 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -3141,6 +3141,53 @@ private function getLocaleFile(string $localeDir, string $locale, string $langua return $localeFileName; } + /** @var array> */ + private static array $falseTrueArray = []; + + /** @return array> */ + public function getFalseTrueArray(): array + { + if (!empty(self::$falseTrueArray)) { + return self::$falseTrueArray; + } + if (count(self::$validLocaleLanguages) == 1) { + self::loadLocales(); + } + $falseTrueArray = [['FALSE'], ['TRUE']]; + foreach (self::$validLocaleLanguages as $language) { + if (str_starts_with($language, 'en')) { + continue; + } + $locale = $language; + if (str_contains($locale, '_')) { + [$language] = explode('_', $locale); + } + $localeDir = implode(DIRECTORY_SEPARATOR, [__DIR__, 'locale', null]); + + try { + $functionNamesFile = $this->getLocaleFile($localeDir, $locale, $language, 'functions'); + } catch (Exception $e) { + continue; + } + // Retrieve the list of locale or language specific function names + $localeFunctions = file($functionNamesFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: []; + foreach ($localeFunctions as $localeFunction) { + [$localeFunction] = explode('##', $localeFunction); // Strip out comments + if (str_contains($localeFunction, '=')) { + [$fName, $lfName] = array_map('trim', explode('=', $localeFunction)); + if ($fName === 'FALSE') { + $falseTrueArray[0][] = $lfName; + } elseif ($fName === 'TRUE') { + $falseTrueArray[1][] = $lfName; + } + } + } + } + self::$falseTrueArray = $falseTrueArray; + + return $falseTrueArray; + } + /** * Set the locale code. * diff --git a/src/PhpSpreadsheet/Reader/Html.php b/src/PhpSpreadsheet/Reader/Html.php index 4ca781a87d..045e6b6b6e 100644 --- a/src/PhpSpreadsheet/Reader/Html.php +++ b/src/PhpSpreadsheet/Reader/Html.php @@ -7,6 +7,7 @@ use DOMElement; use DOMNode; use DOMText; +use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Comment; @@ -271,6 +272,12 @@ protected function flushCell(Worksheet $sheet, string $column, int|string $row, ->setQuotePrefix(true); } } + if ($datatype === DataType::TYPE_BOOL) { + $cellContent = self::convertBoolean($cellContent); + if (!is_bool($cellContent)) { + $attributeArray['data-type'] = DataType::TYPE_STRING; + } + } //catching the Exception and ignoring the invalid data types try { @@ -291,6 +298,31 @@ protected function flushCell(Worksheet $sheet, string $column, int|string $row, $cellContent = (string) ''; } + /** @var array> */ + private static array $falseTrueArray = []; + + private function convertBoolean(?string $cellContent): bool|string + { + if ($cellContent === '1') { + return true; + } + if ($cellContent === '0' || $cellContent === '' || $cellContent === null) { + return false; + } + if (empty(self::$falseTrueArray)) { + $calc = Calculation::getInstance(); + self::$falseTrueArray = $calc->getFalseTrueArray(); + } + if (in_array(mb_strtoupper($cellContent), self::$falseTrueArray[1], true)) { + return true; + } + if (in_array(mb_strtoupper($cellContent), self::$falseTrueArray[0], true)) { + return false; + } + + return $cellContent; + } + private function processDomElementBody(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child): void { $attributeArray = []; diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index 1b2d33abff..f85b5f0961 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -5,6 +5,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; +use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Chart\Chart; use PhpOffice\PhpSpreadsheet\Comment; use PhpOffice\PhpSpreadsheet\Document\Properties; @@ -36,6 +37,9 @@ class Html extends BaseWriter private const DEFAULT_CELL_WIDTH_PIXELS = 56; + private const TRUE_SUBSTITUTE = "\u{fffe}"; + private const FALSE_SUBSTITUTE = "\u{feff}"; + /** * Migration aid to tell if html tags will be treated as plaintext in comments. * if ( @@ -141,6 +145,12 @@ class Html extends BaseWriter /** @var Chart[] */ private $sheetCharts; + private bool $betterBoolean = false; + + private string $getTrue = 'TRUE'; + + private string $getFalse = 'FALSE'; + /** * Create a new HTML. */ @@ -148,6 +158,9 @@ public function __construct(Spreadsheet $spreadsheet) { $this->spreadsheet = $spreadsheet; $this->defaultFont = $this->spreadsheet->getDefaultStyle()->getFont(); + $calc = Calculation::getInstance($this->spreadsheet); + $this->getTrue = $calc->getTRUE(); + $this->getFalse = $calc->getFALSE(); } /** @@ -1346,8 +1359,21 @@ private function generateRowCellDataValue(Worksheet $worksheet, Cell $cell, stri if ($cell->getValue() instanceof RichText) { $cellData .= $this->generateRowCellDataValueRich($cell->getValue()); } else { - $origData = $this->preCalculateFormulas ? $cell->getCalculatedValue() : $cell->getValue(); - $origData2 = $this->preCalculateFormulas ? $cell->getCalculatedValueString() : $cell->getValueString(); + if ($this->preCalculateFormulas) { + $origData = $cell->getCalculatedValue(); + if ($this->betterBoolean && is_bool($origData)) { + $origData2 = $origData ? self::TRUE_SUBSTITUTE : self::FALSE_SUBSTITUTE; + } else { + $origData2 = $cell->getCalculatedValueString(); + } + } else { + $origData = $cell->getValue(); + if ($this->betterBoolean && is_bool($origData)) { + $origData2 = $origData ? self::TRUE_SUBSTITUTE : self::FALSE_SUBSTITUTE; + } else { + $origData2 = $cell->getValueString(); + } + } $formatCode = $worksheet->getParentOrThrow()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(); $cellData = NumberFormat::toFormattedString( @@ -1448,6 +1474,13 @@ private function generateRowWriteCell( $htmlx .= $this->generateRowIncludeCharts($worksheet, $coordinate); // Column start $html .= ' <' . $cellType; + if ($cellData === self::TRUE_SUBSTITUTE) { + $html .= ' data-type="' . DataType::TYPE_BOOL . '"'; + $cellData = $this->getTrue; + } elseif ($cellData === self::FALSE_SUBSTITUTE) { + $html .= ' data-type="' . DataType::TYPE_BOOL . '"'; + $cellData = $this->getFalse; + } if (!$this->useInlineCss && !$this->isPdf && is_string($cssClass)) { $html .= ' class="' . $cssClass . '"'; if ($htmlx) { @@ -1903,4 +1936,16 @@ private function shouldGenerateColumn(Worksheet $sheet, string $colStr): bool return $sheet->getColumnDimension($colStr)->getVisible(); } + + public function getBetterBoolean(): bool + { + return $this->betterBoolean; + } + + public function setBetterBoolean(bool $betterBoolean): self + { + $this->betterBoolean = $betterBoolean; + + return $this; + } } diff --git a/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php b/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php new file mode 100644 index 0000000000..558dc7ed34 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php @@ -0,0 +1,140 @@ +locale = $calculation->getLocale(); + } + + protected function tearDown(): void + { + $calculation = Calculation::getInstance(); + $calculation->setLocale($this->locale); + } + + public function testDefault(): void + { + $spreadsheet = new Spreadsheet(); + $writer = new HtmlWriter($spreadsheet); + // Default will change with next PhpSpreadsheet release + self::assertFalse($writer->getBetterBoolean()); + $spreadsheet->disconnectWorksheets(); + } + + public function setBetter(HtmlWriter $writer): void + { + $writer->setBetterBoolean(true); + } + + public function setNotBetter(HtmlWriter $writer): void + { + $writer->setBetterBoolean(false); + } + + public function testBetterBoolean(): void + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->getCell('A1')->setValue(10); + $sheet->getCell('B1')->setValue('Hello'); + $sheet->getCell('C1')->setValue(true); + $sheet->getCell('D1')->setValue('=IF(1>2, TRUE, FALSE)'); + + /** @var callable */ + $callableWriter = [$this, 'setBetter']; + $reloaded = $this->writeAndReload($spreadsheet, 'Html', null, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $rsheet = $reloaded->getActiveSheet(); + self::assertSame(10, $rsheet->getCell('A1')->getValue()); + self::assertSame('Hello', $rsheet->getCell('B1')->getValue()); + self::assertTrue($rsheet->getCell('C1')->getValue()); + self::assertFalse($rsheet->getCell('D1')->getValue()); + $reloaded->disconnectWorksheets(); + } + + public function testNotBetterBoolean(): void + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->getCell('A1')->setValue(10); + $sheet->getCell('B1')->setValue('Hello'); + $sheet->getCell('C1')->setValue(true); + $sheet->getCell('D1')->setValue('=IF(1>2, TRUE, FALSE)'); + + /** @var callable */ + $callableWriter = [$this, 'setNotBetter']; + $reloaded = $this->writeAndReload($spreadsheet, 'Html', null, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $rsheet = $reloaded->getActiveSheet(); + self::assertSame(10, $rsheet->getCell('A1')->getValue()); + self::assertSame('Hello', $rsheet->getCell('B1')->getValue()); + self::assertSame(1, $rsheet->getCell('C1')->getValue()); + self::assertNull($rsheet->getCell('D1')->getValue()); + $reloaded->disconnectWorksheets(); + } + + public function testLocale(): void + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->getCell('A1')->setValue(10); + $sheet->getCell('B1')->setValue('Hello'); + $sheet->getCell('C1')->setValue(true); + $sheet->getCell('D1')->setValue('=IF(1>2, TRUE, FALSE)'); + $calc = Calculation::getInstance(); + $calc->setLocale('fr'); + $writer = new HtmlWriter($spreadsheet); + $writer->setBetterBoolean(true); + $html = $writer->generateHtmlAll(); + self::assertStringContainsString('VRAI', $html); + self::assertStringNotContainsString('TRUE', $html); + + /** @var callable */ + $callableWriter = [$this, 'setBetter']; + $reloaded = $this->writeAndReload($spreadsheet, 'Html', null, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $rsheet = $reloaded->getActiveSheet(); + self::assertSame(10, $rsheet->getCell('A1')->getValue()); + self::assertSame('Hello', $rsheet->getCell('B1')->getValue()); + self::assertTrue($rsheet->getCell('C1')->getValue()); + self::assertFalse($rsheet->getCell('D1')->getValue()); + $reloaded->disconnectWorksheets(); + } + + public function testForeignNoLocale(): void + { + $fragment = '' + . '' + . '' + . '' // Bulgarian TRUE + . '' // Finnish FALSE + . '' + . '' + . '
10HelloИСТИНАEPÄTOSIwhatevertRuE
'; + $reader = new HtmlReader(); + $spreadsheet = $reader->loadFromString($fragment); + $sheet = $spreadsheet->getActiveSheet(); + self::assertTrue($sheet->getCell('C1')->getValue()); + self::assertFalse($sheet->getCell('D1')->getValue()); + self::assertSame('whatever', $sheet->getCell('E1')->getValue()); + self::assertTrue($sheet->getCell('F1')->getValue()); + $spreadsheet->disconnectWorksheets(); + } +} From 3d6f71f6adbfd9ecb1529ed8115decd6c0a091e7 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sun, 1 Dec 2024 09:20:38 -0800 Subject: [PATCH 2/4] Extend to Formulas, and Numbers Stored As String All still require opt-in. --- src/PhpSpreadsheet/Reader/Html.php | 2 +- src/PhpSpreadsheet/Writer/Html.php | 35 +++++++++------ .../Writer/Html/BetterBooleanTest.php | 43 +++++++++++++++---- 3 files changed, 59 insertions(+), 21 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Html.php b/src/PhpSpreadsheet/Reader/Html.php index 045e6b6b6e..431a08ffff 100644 --- a/src/PhpSpreadsheet/Reader/Html.php +++ b/src/PhpSpreadsheet/Reader/Html.php @@ -301,7 +301,7 @@ protected function flushCell(Worksheet $sheet, string $column, int|string $row, /** @var array> */ private static array $falseTrueArray = []; - private function convertBoolean(?string $cellContent): bool|string + private static function convertBoolean(?string $cellContent): bool|string { if ($cellContent === '1') { return true; diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index f85b5f0961..677f59966d 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -37,9 +37,6 @@ class Html extends BaseWriter private const DEFAULT_CELL_WIDTH_PIXELS = 56; - private const TRUE_SUBSTITUTE = "\u{fffe}"; - private const FALSE_SUBSTITUTE = "\u{feff}"; - /** * Migration aid to tell if html tags will be treated as plaintext in comments. * if ( @@ -1362,14 +1359,14 @@ private function generateRowCellDataValue(Worksheet $worksheet, Cell $cell, stri if ($this->preCalculateFormulas) { $origData = $cell->getCalculatedValue(); if ($this->betterBoolean && is_bool($origData)) { - $origData2 = $origData ? self::TRUE_SUBSTITUTE : self::FALSE_SUBSTITUTE; + $origData2 = $origData ? $this->getTrue : $this->getFalse; } else { $origData2 = $cell->getCalculatedValueString(); } } else { $origData = $cell->getValue(); if ($this->betterBoolean && is_bool($origData)) { - $origData2 = $origData ? self::TRUE_SUBSTITUTE : self::FALSE_SUBSTITUTE; + $origData2 = $origData ? $this->getTrue : $this->getFalse; } else { $origData2 = $cell->getValueString(); } @@ -1414,8 +1411,19 @@ private function generateRowCellData(Worksheet $worksheet, null|Cell|string $cel // Extend CSS class? if (!$this->useInlineCss && is_string($cssClass)) { + $dataType = $cell->getDataType(); + if ($this->betterBoolean && $this->preCalculateFormulas && $dataType === DataType::TYPE_FORMULA) { + $calculatedValue = $cell->getCalculatedValue(); + if (is_bool($calculatedValue)) { + $dataType = DataType::TYPE_BOOL; + } elseif (is_numeric($calculatedValue)) { + $dataType = DataType::TYPE_NUMERIC; + } elseif (is_string($calculatedValue)) { + $dataType = DataType::TYPE_STRING; + } + } $cssClass .= ' style' . $cell->getXfIndex(); - $cssClass .= ' ' . $cell->getDataType(); + $cssClass .= ' ' . $dataType; } elseif (is_array($cssClass)) { $index = $cell->getXfIndex(); $styleIndex = 'td.style' . $index . ', th.style' . $index; @@ -1474,12 +1482,15 @@ private function generateRowWriteCell( $htmlx .= $this->generateRowIncludeCharts($worksheet, $coordinate); // Column start $html .= ' <' . $cellType; - if ($cellData === self::TRUE_SUBSTITUTE) { - $html .= ' data-type="' . DataType::TYPE_BOOL . '"'; - $cellData = $this->getTrue; - } elseif ($cellData === self::FALSE_SUBSTITUTE) { - $html .= ' data-type="' . DataType::TYPE_BOOL . '"'; - $cellData = $this->getFalse; + if ($this->betterBoolean) { + $dataType = $worksheet->getCell($coordinate)->getDataType(); + if ($dataType === DataType::TYPE_BOOL) { + $html .= ' data-type="' . DataType::TYPE_BOOL . '"'; + } elseif ($dataType === DataType::TYPE_FORMULA && is_bool($worksheet->getCell($coordinate)->getCalculatedValue())) { + $html .= ' data-type="' . DataType::TYPE_BOOL . '"'; + } elseif (is_numeric($cellData) && $worksheet->getCell($coordinate)->getDataType() === DataType::TYPE_STRING) { + $html .= ' data-type="' . DataType::TYPE_STRING . '"'; + } } if (!$this->useInlineCss && !$this->isPdf && is_string($cssClass)) { $html .= ' class="' . $cssClass . '"'; diff --git a/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php b/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php index 558dc7ed34..0982c2a786 100644 --- a/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php @@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Writer\Html; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; +use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Reader\Html as HtmlReader; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter; @@ -49,10 +50,13 @@ public function testBetterBoolean(): void { $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); - $sheet->getCell('A1')->setValue(10); + $sheet->getCell('A1')->setValue(1); $sheet->getCell('B1')->setValue('Hello'); $sheet->getCell('C1')->setValue(true); $sheet->getCell('D1')->setValue('=IF(1>2, TRUE, FALSE)'); + $sheet->getCell('E1')->setValueExplicit(1, DataType::TYPE_STRING); + $sheet->getCell('F1')->setValue('="A"&"B"'); + $sheet->getCell('G1')->setValue('=1+2'); /** @var callable */ $callableWriter = [$this, 'setBetter']; @@ -60,10 +64,13 @@ public function testBetterBoolean(): void $spreadsheet->disconnectWorksheets(); $rsheet = $reloaded->getActiveSheet(); - self::assertSame(10, $rsheet->getCell('A1')->getValue()); + self::assertSame(1, $rsheet->getCell('A1')->getValue()); self::assertSame('Hello', $rsheet->getCell('B1')->getValue()); self::assertTrue($rsheet->getCell('C1')->getValue()); self::assertFalse($rsheet->getCell('D1')->getValue()); + self::assertSame('1', $rsheet->getCell('E1')->getValue()); + self::assertSame('AB', $rsheet->getCell('F1')->getValue()); + self::assertSame(3, $rsheet->getCell('G1')->getValue()); $reloaded->disconnectWorksheets(); } @@ -71,10 +78,13 @@ public function testNotBetterBoolean(): void { $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); - $sheet->getCell('A1')->setValue(10); + $sheet->getCell('A1')->setValue(1); $sheet->getCell('B1')->setValue('Hello'); $sheet->getCell('C1')->setValue(true); $sheet->getCell('D1')->setValue('=IF(1>2, TRUE, FALSE)'); + $sheet->getCell('E1')->setValueExplicit(1, DataType::TYPE_STRING); + $sheet->getCell('F1')->setValue('="A"&"B"'); + $sheet->getCell('G1')->setValue('=1+2'); /** @var callable */ $callableWriter = [$this, 'setNotBetter']; @@ -82,10 +92,13 @@ public function testNotBetterBoolean(): void $spreadsheet->disconnectWorksheets(); $rsheet = $reloaded->getActiveSheet(); - self::assertSame(10, $rsheet->getCell('A1')->getValue()); + self::assertSame(1, $rsheet->getCell('A1')->getValue()); self::assertSame('Hello', $rsheet->getCell('B1')->getValue()); self::assertSame(1, $rsheet->getCell('C1')->getValue()); self::assertNull($rsheet->getCell('D1')->getValue()); + self::assertSame(1, $rsheet->getCell('E1')->getValue()); + self::assertSame('AB', $rsheet->getCell('F1')->getValue()); + self::assertSame(3, $rsheet->getCell('G1')->getValue()); $reloaded->disconnectWorksheets(); } @@ -93,17 +106,24 @@ public function testLocale(): void { $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); - $sheet->getCell('A1')->setValue(10); + $sheet->getCell('A1')->setValue(1); $sheet->getCell('B1')->setValue('Hello'); $sheet->getCell('C1')->setValue(true); $sheet->getCell('D1')->setValue('=IF(1>2, TRUE, FALSE)'); + $sheet->getCell('E1')->setValueExplicit(1, DataType::TYPE_STRING); + $sheet->getCell('F1')->setValue('="A"&"B"'); + $sheet->getCell('G1')->setValue('=1+2'); $calc = Calculation::getInstance(); $calc->setLocale('fr'); $writer = new HtmlWriter($spreadsheet); $writer->setBetterBoolean(true); $html = $writer->generateHtmlAll(); - self::assertStringContainsString('VRAI', $html); self::assertStringNotContainsString('TRUE', $html); + self::assertStringContainsString('VRAI', $html); + self::assertStringContainsString('FAUX', $html); + self::assertStringContainsString('1', $html); + self::assertStringContainsString('AB', $html); + self::assertStringContainsString('3', $html); /** @var callable */ $callableWriter = [$this, 'setBetter']; @@ -111,30 +131,37 @@ public function testLocale(): void $spreadsheet->disconnectWorksheets(); $rsheet = $reloaded->getActiveSheet(); - self::assertSame(10, $rsheet->getCell('A1')->getValue()); + self::assertSame(1, $rsheet->getCell('A1')->getValue()); self::assertSame('Hello', $rsheet->getCell('B1')->getValue()); self::assertTrue($rsheet->getCell('C1')->getValue()); self::assertFalse($rsheet->getCell('D1')->getValue()); + self::assertSame('1', $rsheet->getCell('E1')->getValue()); + self::assertSame('AB', $rsheet->getCell('F1')->getValue()); + self::assertSame(3, $rsheet->getCell('G1')->getValue()); $reloaded->disconnectWorksheets(); } public function testForeignNoLocale(): void { $fragment = '' - . '' + . '' . '' . '' // Bulgarian TRUE . '' // Finnish FALSE . '' . '' + . '' . '
101HelloИСТИНАEPÄTOSIwhatevertRuE1
'; $reader = new HtmlReader(); $spreadsheet = $reader->loadFromString($fragment); $sheet = $spreadsheet->getActiveSheet(); + self::assertSame(1, $sheet->getCell('A1')->getValue()); + self::assertSame('Hello', $sheet->getCell('B1')->getValue()); self::assertTrue($sheet->getCell('C1')->getValue()); self::assertFalse($sheet->getCell('D1')->getValue()); self::assertSame('whatever', $sheet->getCell('E1')->getValue()); self::assertTrue($sheet->getCell('F1')->getValue()); + self::assertSame('1', $sheet->getCell('G1')->getValue()); $spreadsheet->disconnectWorksheets(); } } From 6a4c2c68c4e53e71b9338df8dd664b3fc07ab444 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:27:00 -0800 Subject: [PATCH 3/4] Include InlineCss --- src/PhpSpreadsheet/Writer/Html.php | 24 +++++------ .../Writer/Html/BetterBooleanTest.php | 43 +++++++++++++++++++ 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index 677f59966d..8bb1824490 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -1410,18 +1410,18 @@ private function generateRowCellData(Worksheet $worksheet, null|Cell|string $cel $cellData = nl2br($cellData); // Extend CSS class? - if (!$this->useInlineCss && is_string($cssClass)) { - $dataType = $cell->getDataType(); - if ($this->betterBoolean && $this->preCalculateFormulas && $dataType === DataType::TYPE_FORMULA) { - $calculatedValue = $cell->getCalculatedValue(); - if (is_bool($calculatedValue)) { - $dataType = DataType::TYPE_BOOL; - } elseif (is_numeric($calculatedValue)) { - $dataType = DataType::TYPE_NUMERIC; - } elseif (is_string($calculatedValue)) { - $dataType = DataType::TYPE_STRING; - } + $dataType = $cell->getDataType(); + if ($this->betterBoolean && $this->preCalculateFormulas && $dataType === DataType::TYPE_FORMULA) { + $calculatedValue = $cell->getCalculatedValue(); + if (is_bool($calculatedValue)) { + $dataType = DataType::TYPE_BOOL; + } elseif (is_numeric($calculatedValue)) { + $dataType = DataType::TYPE_NUMERIC; + } elseif (is_string($calculatedValue)) { + $dataType = DataType::TYPE_STRING; } + } + if (!$this->useInlineCss && is_string($cssClass)) { $cssClass .= ' style' . $cell->getXfIndex(); $cssClass .= ' ' . $dataType; } elseif (is_array($cssClass)) { @@ -1437,7 +1437,7 @@ private function generateRowCellData(Worksheet $worksheet, null|Cell|string $cel $sharedStyle->getAlignment()->getHorizontal() == Alignment::HORIZONTAL_GENERAL && isset($this->cssStyles['.' . $cell->getDataType()]['text-align']) ) { - $cssClass['text-align'] = $this->cssStyles['.' . $cell->getDataType()]['text-align']; + $cssClass['text-align'] = $this->cssStyles['.' . $dataType]['text-align']; } } } else { diff --git a/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php b/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php index 0982c2a786..4bd9fb1b66 100644 --- a/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php @@ -141,6 +141,49 @@ public function testLocale(): void $reloaded->disconnectWorksheets(); } + public function testInline(): void + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->getCell('A1')->setValue(1); + $sheet->getCell('B1')->setValue('Hello'); + $sheet->getCell('C1')->setValue(true); + $sheet->getCell('D1')->setValue('=IF(1>2, TRUE, FALSE)'); + $sheet->getCell('E1')->setValueExplicit(1, DataType::TYPE_STRING); + $sheet->getCell('F1')->setValue('="A"&"B"'); + $sheet->getCell('G1')->setValue('=1+2'); + $calc = Calculation::getInstance(); + $calc->setLocale('fr'); + $writer = new HtmlWriter($spreadsheet); + $writer->setBetterBoolean(true); + $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); + self::assertStringNotContainsString('TRUE', $html); + self::assertStringContainsString('1', $html); + self::assertStringContainsString('Hello', $html); + self::assertStringContainsString('VRAI', $html); + self::assertStringContainsString('FAUX', $html); + self::assertStringContainsString('AB', $html); + self::assertStringContainsString('3', $html); + + /** @var callable */ + $callableWriter = [$this, 'setBetter']; + $reloaded = $this->writeAndReload($spreadsheet, 'Html', null, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $rsheet = $reloaded->getActiveSheet(); + self::assertSame(1, $rsheet->getCell('A1')->getValue()); + self::assertSame('Hello', $rsheet->getCell('B1')->getValue()); + self::assertTrue($rsheet->getCell('C1')->getValue()); + self::assertFalse($rsheet->getCell('D1')->getValue()); + self::assertSame('1', $rsheet->getCell('E1')->getValue()); + self::assertSame('AB', $rsheet->getCell('F1')->getValue()); + self::assertSame(3, $rsheet->getCell('G1')->getValue()); + $reloaded->disconnectWorksheets(); + } + public function testForeignNoLocale(): void { $fragment = '' From 1eeb7059752a233048e5bb2c23f881f2638079e4 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Thu, 5 Dec 2024 19:58:35 -0800 Subject: [PATCH 4/4] Update Changelog --- CHANGELOG.md | 4 +++- tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8125f69e15..639e648cfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,9 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Fixed -- Nothing yet. +- Html Reader/Writer Better Handling of Booleans. [PR #4257](https://github.com/PHPOffice/PhpSpreadsheet/pull/4257) +- Fill Patterns/Colors When Xml Attributes are Missing. [Issue #4248](https://github.com/PHPOffice/PhpSpreadsheet/issues/4248) [PR #4250](https://github.com/PHPOffice/PhpSpreadsheet/pull/4250) +- Remove Unneccesary files from Composer Package. [PR #4262](https://github.com/PHPOffice/PhpSpreadsheet/pull/4262) ## 2024-11-22 - 3.5.0 diff --git a/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php b/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php index 4bd9fb1b66..0eac757d4d 100644 --- a/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php @@ -165,6 +165,7 @@ public function testInline(): void self::assertStringContainsString('', $html); self::assertStringContainsString('', $html); self::assertStringContainsString('', $html); + self::assertStringContainsString('', $html); self::assertStringContainsString('', $html); self::assertStringContainsString('', $html);
HelloVRAIFAUX1AB3