From 55797126887ce385e9b38c91e964a0de3d81bf2c Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sun, 27 Feb 2022 23:06:22 +0100 Subject: [PATCH 1/6] Make CSV Reader boolean casting (e.g. string ``"TRUE"` to boolean `TRUE`) locale-aware. --- docs/topics/accessing-cells.md | 10 +++++----- src/PhpSpreadsheet/Reader/Csv.php | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/topics/accessing-cells.md b/docs/topics/accessing-cells.md index 346e5858..9d3fb1cb 100644 --- a/docs/topics/accessing-cells.md +++ b/docs/topics/accessing-cells.md @@ -37,9 +37,7 @@ $spreadsheet->getActiveSheet() ### Creating a new Cell If you make a call to `getCell()`, and the cell doesn't already exist, then -PhpSpreadsheet will (by default) create the cell for you. If you don't want -to create a new cell, then you can pass a second argument of false, and then -`getCell()` will return a null if the cell doesn't exist. +PhpSpreadsheet will create that cell for you. ### BEWARE: Cells assigned to variables as a Detached Reference @@ -532,7 +530,7 @@ types of entered data using a cell's `setValue()` method (the Optionally, the default behaviour of PhpSpreadsheet can be modified, allowing easier data entry. For example, a `\PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder` class is available. -It automatically converts percentages, number in scientific format, and +It automatically converts percentages, numbers in scientific format, and dates entered as strings to the correct format, also setting the cell's style information. The following example demonstrates how to set the value binder in PhpSpreadsheet: @@ -577,7 +575,9 @@ $stringValueBinder->setNumericConversion(false) \PhpOffice\PhpSpreadsheet\Cell\Cell::setValueBinder( $stringValueBinder ); ``` -**Creating your own value binder is relatively straightforward.** When more specialised +### Creating your own value binder + +Creating your own value binder is relatively straightforward. When more specialised value binding is required, you can implement the `\PhpOffice\PhpSpreadsheet\Cell\IValueBinder` interface or extend the existing `\PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder` or diff --git a/src/PhpSpreadsheet/Reader/Csv.php b/src/PhpSpreadsheet/Reader/Csv.php index 0146fca0..f367e0b5 100644 --- a/src/PhpSpreadsheet/Reader/Csv.php +++ b/src/PhpSpreadsheet/Reader/Csv.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader; +use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Reader\Csv\Delimiter; @@ -353,9 +354,9 @@ class Csv extends BaseReader private function convertBoolean(&$rowDatum, bool $preserveBooleanString): void { if (is_string($rowDatum) && !$preserveBooleanString) { - if (strcasecmp('true', $rowDatum) === 0) { + if (strcasecmp(Calculation::getTRUE(), $rowDatum) === 0 || strcasecmp('true', $rowDatum) === 0) { $rowDatum = true; - } elseif (strcasecmp('false', $rowDatum) === 0) { + } elseif (strcasecmp(Calculation::getFALSE(), $rowDatum) === 0 || strcasecmp('false', $rowDatum) === 0) { $rowDatum = false; } } elseif ($rowDatum === null) { From f3d502851837d5101fbc9967e9100f700b91285a Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sun, 27 Feb 2022 23:24:20 +0100 Subject: [PATCH 2/6] Work on setting up locale-aware formatted number conversion for the Csv Reader Unit tests for locale-aware boolean conversion for Csv Reader --- CHANGELOG.md | 7 +- src/PhpSpreadsheet/Reader/Csv.php | 57 ++++++ .../Reader/Csv/CsvIssue2232Test.php | 40 +++- .../Reader/Csv/CsvNumberFormatLocaleTest.php | 145 +++++++++++++++ .../Reader/Csv/CsvNumberFormatTest.php | 173 ++++++++++++++++++ tests/data/Reader/CSV/NumberFormatTest.csv | 3 + tests/data/Reader/CSV/NumberFormatTest.de.csv | 3 + tests/data/Reader/CSV/issue.2232.csv | 2 + 8 files changed, 427 insertions(+), 3 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Csv/CsvNumberFormatLocaleTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Csv/CsvNumberFormatTest.php create mode 100644 tests/data/Reader/CSV/NumberFormatTest.csv create mode 100644 tests/data/Reader/CSV/NumberFormatTest.de.csv diff --git a/CHANGELOG.md b/CHANGELOG.md index c2acd357..0b625d14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Added -- Implementation of the ISREF() information function +- Implementation of the ISREF() information function. +- Allow Boolean Conversion in Csv Reader to be locale-aware when using the String Value Binder. + + (i.e. `"Vrai"` wil be converted to a boolean `true` if the Locale is set to `fr`.) ### Changed @@ -27,7 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Fixed -- Fixed behaviour of XLSX font style vertical align settings +- Fixed behaviour of XLSX font style vertical align settings. - Resolved formula translations to handle separators (row and column) for array functions as well as for function argument separators; and cleanly handle nesting levels. Note that this method is used when translating Excel functions between en and other locale languages, as well as when converting formulae between different spreadsheet formats (e.g. Ods to Excel). diff --git a/src/PhpSpreadsheet/Reader/Csv.php b/src/PhpSpreadsheet/Reader/Csv.php index f367e0b5..a2d43bd4 100644 --- a/src/PhpSpreadsheet/Reader/Csv.php +++ b/src/PhpSpreadsheet/Reader/Csv.php @@ -9,6 +9,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Csv\Delimiter; use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; +use PhpOffice\PhpSpreadsheet\Style\NumberFormat; class Csv extends BaseReader { @@ -85,6 +86,16 @@ class Csv extends BaseReader */ private static $constructorCallback; + /** + * @var bool + */ + protected $castFormattedNumberToNumeric = false; + + /** + * @var bool + */ + protected $preserveNumericFormatting = false; + /** * Create a new CSV Reader instance. */ @@ -283,6 +294,14 @@ class Csv extends BaseReader return $retVal; } + public function castFormattedNumberToNumeric( + bool $castFormattedNumberToNumeric, + bool $preserveNumericFormatting = false + ): void { + $this->castFormattedNumberToNumeric = $castFormattedNumberToNumeric; + $this->preserveNumericFormatting = $preserveNumericFormatting; + } + /** * Loads PhpSpreadsheet from file into PhpSpreadsheet instance. */ @@ -319,6 +338,7 @@ class Csv extends BaseReader $columnLetter = 'A'; foreach ($rowData as $rowDatum) { $this->convertBoolean($rowDatum, $preserveBooleanString); + $numberFormatMask = $this->convertFormattedNumber($rowDatum); if ($rowDatum !== '' && $this->readFilter->readCell($columnLetter, $currentRow)) { if ($this->contiguous) { if ($noOutputYet) { @@ -328,6 +348,10 @@ class Csv extends BaseReader } else { $outRow = $currentRow; } + // Set basic styling for the value (Note that this could be overloaded by styling in a value binder) + $sheet->getCell($columnLetter . $outRow)->getStyle() + ->getNumberFormat() + ->setFormatCode($numberFormatMask); // Set cell value $sheet->getCell($columnLetter . $outRow)->setValue($rowDatum); } @@ -364,6 +388,39 @@ class Csv extends BaseReader } } + /** + * Convert numeric strings to int or float values. + * + * @param mixed $rowDatum + */ + private function convertFormattedNumber(&$rowDatum): string + { + $numberFormatMask = NumberFormat::FORMAT_GENERAL; + if ($this->castFormattedNumberToNumeric === true && is_string($rowDatum)) { + $numeric = str_replace( + [StringHelper::getThousandsSeparator(), StringHelper::getDecimalSeparator()], + ['', '.'], + $rowDatum + ); + + if (is_numeric($numeric)) { + $decimalPos = strpos($rowDatum, StringHelper::getDecimalSeparator()); + if ($this->preserveNumericFormatting === true) { + $numberFormatMask = (strpos($rowDatum, StringHelper::getThousandsSeparator()) !== false) + ? '#,##0' : '0'; + if ($decimalPos !== false) { + $decimals = strlen($rowDatum) - $decimalPos - 1; + $numberFormatMask .= '.' . str_repeat('0', min($decimals, 6)); + } + } + + $rowDatum = ($decimalPos !== false) ? (float) $numeric : (int) $numeric; + } + } + + return $numberFormatMask; + } + public function getDelimiter(): ?string { return $this->delimiter; diff --git a/tests/PhpSpreadsheetTests/Reader/Csv/CsvIssue2232Test.php b/tests/PhpSpreadsheetTests/Reader/Csv/CsvIssue2232Test.php index c463c271..f9321102 100644 --- a/tests/PhpSpreadsheetTests/Reader/Csv/CsvIssue2232Test.php +++ b/tests/PhpSpreadsheetTests/Reader/Csv/CsvIssue2232Test.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Reader\Csv; +use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\IValueBinder; use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder; @@ -31,7 +32,7 @@ class CsvIssue2232Test extends TestCase * @param mixed $b2Value * @param mixed $b3Value */ - public function testEncodings(bool $useStringBinder, ?bool $preserveBoolString, $b2Value, $b3Value): void + public function testBooleanConversions(bool $useStringBinder, ?bool $preserveBoolString, $b2Value, $b3Value): void { if ($useStringBinder) { $binder = new StringValueBinder(); @@ -60,4 +61,41 @@ class CsvIssue2232Test extends TestCase [true, true, 'FaLSe', 'tRUE'], ]; } + + /** + * @dataProvider providerIssue2232locale + * + * @param mixed $b4Value + * @param mixed $b5Value + */ + public function testBooleanConversionsLocaleAware(bool $useStringBinder, ?bool $preserveBoolString, $b4Value, $b5Value): void + { + if ($useStringBinder) { + $binder = new StringValueBinder(); + if (is_bool($preserveBoolString)) { + $binder->setBooleanConversion($preserveBoolString); + } + Cell::setValueBinder($binder); + } + + Calculation::getInstance()->setLocale('fr'); + + $reader = new Csv(); + $filename = 'tests/data/Reader/CSV/issue.2232.csv'; + $spreadsheet = $reader->load($filename); + $sheet = $spreadsheet->getActiveSheet(); + self::assertSame($b4Value, $sheet->getCell('B4')->getValue()); + self::assertSame($b5Value, $sheet->getCell('B5')->getValue()); + $spreadsheet->disconnectWorksheets(); + } + + public function providerIssue2232locale(): array + { + return [ + [true, true, 'Faux', 'Vrai'], + [true, true, 'Faux', 'Vrai'], + [false, false, false, true], + [false, false, false, true], + ]; + } } diff --git a/tests/PhpSpreadsheetTests/Reader/Csv/CsvNumberFormatLocaleTest.php b/tests/PhpSpreadsheetTests/Reader/Csv/CsvNumberFormatLocaleTest.php new file mode 100644 index 00000000..1ac093c4 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Csv/CsvNumberFormatLocaleTest.php @@ -0,0 +1,145 @@ +currentLocale = setlocale(LC_ALL, '0'); + + if (!setlocale(LC_ALL, 'de_DE.UTF-8', 'deu_deu')) { + $this->localeAdjusted = false; + + return; + } + + $this->localeAdjusted = true; + + $this->filename = 'tests/data/Reader/CSV/NumberFormatTest.de.csv'; + $this->csvReader = new Csv(); + } + + protected function tearDown(): void + { + if ($this->localeAdjusted && is_string($this->currentLocale)) { + setlocale(LC_ALL, $this->currentLocale); + } + } + + /** + * @dataProvider providerNumberFormatNoConversionTest + * + * @param mixed $expectedValue + */ + public function testNumberFormatNoConversion($expectedValue, string $expectedFormat, string $cellAddress): void + { + if (!$this->localeAdjusted) { + self::markTestSkipped('Unable to set locale for testing.'); + } + + $spreadsheet = $this->csvReader->load($this->filename); + $worksheet = $spreadsheet->getActiveSheet(); + + $cell = $worksheet->getCell($cellAddress); + + self::assertSame($expectedValue, $cell->getValue(), 'Expected value check'); + self::assertSame($expectedFormat, $cell->getFormattedValue(), 'Format mask check'); + } + + public function providerNumberFormatNoConversionTest(): array + { + return [ + [ + -123, + '-123', + 'A1', + ], + [ + '12.345,67', + '12.345,67', + 'C1', + ], + [ + '-1.234,567', + '-1.234,567', + 'A3', + ], + ]; + } + + /** + * @dataProvider providerNumberValueConversionTest + * + * @param mixed $expectedValue + */ + public function testNumberValueConversion($expectedValue, string $cellAddress): void + { + if (!$this->localeAdjusted) { + self::markTestSkipped('Unable to set locale for testing.'); + } + + $this->csvReader->castFormattedNumberToNumeric(true); + $spreadsheet = $this->csvReader->load($this->filename); + $worksheet = $spreadsheet->getActiveSheet(); + + $cell = $worksheet->getCell($cellAddress); + + self::assertSame(DataType::TYPE_NUMERIC, $cell->getDataType(), 'Datatype check'); + self::assertSame($expectedValue, $cell->getValue(), 'Expected value check'); + } + + public function providerNumberValueConversionTest(): array + { + return [ + 'A1' => [ + -123, + 'A1', + ], + 'B1' => [ + 1234, + 'B1', + ], + 'C1' => [ + 12345.67, + 'C1', + ], + 'A2' => [ + 123.4567, + 'A2', + ], + 'B2' => [ + 123.456789012, + 'B2', + ], + 'A3' => [ + -1234.567, + 'A3', + ], + ]; + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Csv/CsvNumberFormatTest.php b/tests/PhpSpreadsheetTests/Reader/Csv/CsvNumberFormatTest.php new file mode 100644 index 00000000..c4c59d01 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Csv/CsvNumberFormatTest.php @@ -0,0 +1,173 @@ +filename = 'tests/data/Reader/CSV/NumberFormatTest.csv'; + $this->csvReader = new Csv(); + } + + /** + * @dataProvider providerNumberFormatNoConversionTest + * + * @param mixed $expectedValue + */ + public function testNumberFormatNoConversion($expectedValue, string $expectedFormat, string $cellAddress): void + { + $spreadsheet = $this->csvReader->load($this->filename); + $worksheet = $spreadsheet->getActiveSheet(); + + $cell = $worksheet->getCell($cellAddress); + + self::assertSame($expectedValue, $cell->getValue(), 'Expected value check'); + self::assertSame($expectedFormat, $cell->getFormattedValue(), 'Format mask check'); + } + + public function providerNumberFormatNoConversionTest(): array + { + return [ + [ + -123, + '-123', + 'A1', + ], + [ + '12,345.67', + '12,345.67', + 'C1', + ], + [ + '-1,234.567', + '-1,234.567', + 'A3', + ], + ]; + } + + /** + * @dataProvider providerNumberValueConversionTest + * + * @param mixed $expectedValue + */ + public function testNumberValueConversion($expectedValue, string $cellAddress): void + { + $this->csvReader->castFormattedNumberToNumeric(true); + $spreadsheet = $this->csvReader->load($this->filename); + $worksheet = $spreadsheet->getActiveSheet(); + + $cell = $worksheet->getCell($cellAddress); + + self::assertSame(DataType::TYPE_NUMERIC, $cell->getDataType(), 'Datatype check'); + self::assertSame($expectedValue, $cell->getValue(), 'Expected value check'); + } + + public function providerNumberValueConversionTest(): array + { + return [ + 'A1' => [ + -123, + 'A1', + ], + 'B1' => [ + 1234, + 'B1', + ], + 'C1' => [ + 12345.67, + 'C1', + ], + 'A2' => [ + 123.4567, + 'A2', + ], + 'B2' => [ + 123.456789012, + 'B2', + ], + 'A3' => [ + -1234.567, + 'A3', + ], + 'B3' => [ + 1234.567, + 'B3', + ], + ]; + } + + /** + * @dataProvider providerNumberFormatConversionTest + * + * @param mixed $expectedValue + */ + public function testNumberFormatConversion($expectedValue, string $expectedFormat, string $cellAddress): void + { + $this->csvReader->castFormattedNumberToNumeric(true, true); + $spreadsheet = $this->csvReader->load($this->filename); + $worksheet = $spreadsheet->getActiveSheet(); + + $cell = $worksheet->getCell($cellAddress); + + self::assertSame(DataType::TYPE_NUMERIC, $cell->getDataType(), 'Datatype check'); + self::assertSame($expectedValue, $cell->getValue(), 'Expected value check'); + self::assertSame($expectedFormat, $cell->getFormattedValue(), 'Format mask check'); + } + + public function providerNumberFormatConversionTest(): array + { + return [ + 'A1' => [ + -123, + '-123', + 'A1', + ], + 'B1' => [ + 1234, + '1,234', + 'B1', + ], + 'C1' => [ + 12345.67, + '12,345.67', + 'C1', + ], + 'A2' => [ + 123.4567, + '123.4567', + 'A2', + ], + 'B2' => [ + 123.456789012, + '123.456789', + 'B2', + ], + 'A3' => [ + -1234.567, + '-1,234.567', + 'A3', + ], + 'B3' => [ + 1234.567, + '1234.567', + 'B3', + ], + ]; + } +} diff --git a/tests/data/Reader/CSV/NumberFormatTest.csv b/tests/data/Reader/CSV/NumberFormatTest.csv new file mode 100644 index 00000000..d2ba90a4 --- /dev/null +++ b/tests/data/Reader/CSV/NumberFormatTest.csv @@ -0,0 +1,3 @@ +"-123","1,234","12,345.67" +"123.4567","123.456789012" +"-1,234.567",1234.567 diff --git a/tests/data/Reader/CSV/NumberFormatTest.de.csv b/tests/data/Reader/CSV/NumberFormatTest.de.csv new file mode 100644 index 00000000..47c28453 --- /dev/null +++ b/tests/data/Reader/CSV/NumberFormatTest.de.csv @@ -0,0 +1,3 @@ +"-123","1.234","12.345,67" +"123,4567","123,456789012" +"-1.234,567" diff --git a/tests/data/Reader/CSV/issue.2232.csv b/tests/data/Reader/CSV/issue.2232.csv index 626d0255..aa83ee0c 100644 --- a/tests/data/Reader/CSV/issue.2232.csv +++ b/tests/data/Reader/CSV/issue.2232.csv @@ -1,3 +1,5 @@ 1,2,3 a,FaLSe,b cc,tRUE,cc +dd,Faux,ee +ff,Vrai,gg From 45ca934a5316deedcebf9a596de9faf2242eacdb Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Wed, 2 Mar 2022 12:33:20 +0100 Subject: [PATCH 3/6] Update Change Log and Documentation --- CHANGELOG.md | 11 +++++++++-- docs/topics/reading-files.md | 38 ++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b625d14..d68fc896 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,15 +10,22 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Added - Implementation of the ISREF() information function. -- Allow Boolean Conversion in Csv Reader to be locale-aware when using the String Value Binder. +- Added support for reading "formatted" numeric values from Csv files; although default behaviour of reading these values as strings is preserved. - (i.e. `"Vrai"` wil be converted to a boolean `true` if the Locale is set to `fr`.) + (i.e a value of "12,345.67" will be read as numeric `1235.67`, not as a string `"12,345.67"`. + + This functionality is locale-aware, using the server's locale settings to identify the thousands and decimal separators. ### Changed - Gnumeric Reader now loads number formatting for cells. - Gnumeric Reader now correctly identifies selected worksheet. - Some Refactoring of the Ods Reader, moving all formula and address translation from Ods to Excel into a separate class to eliminate code duplication and ensure consistency. +- Make Boolean Conversion in Csv Reader locale-aware when using the String Value Binder. + + This is determined b the Calculation Engine locale setting. + + (i.e. `"Vrai"` wil be converted to a boolean `true` if the Locale is set to `fr`.) ### Deprecated diff --git a/docs/topics/reading-files.md b/docs/topics/reading-files.md index 6c30f266..38428166 100644 --- a/docs/topics/reading-files.md +++ b/docs/topics/reading-files.md @@ -560,6 +560,44 @@ Xlsx | NO | Xls | NO | Xml | NO | Ods | NO | SYLK | NO | Gnumeric | NO | CSV | YES | HTML | NO + +### Reading formatted Numbers from a CSV File + +Unfortunately, numbers in a CSV file may be formatted as strings. +If that number is a simple integer or float (with a decimal `.` separator) without any thousands separator, then it will be treated as a number. +However, if the value has a thousands separator (e.g. `12,345`), or a decimal separator that isn't a `.` (e.g. `123,45` for a European locale), then it will be loaded as a string with that formatting. +If you want the Csv Reader to convert that value to a numeric when it loads the file, the you need to tell it to do so. The `castFormattedNumberToNumeric()` lets you do this. + +(Assuming that our server is configured with German locale settings: otherwise it may be necessary to call `setlocale()` before loading the file.) +```php +$inputFileType = 'Csv'; +$inputFileName = './sampleData/example1.de.csv'; + +/** It may be necessary to call setlocale() first if this is not your default locale */ +// setlocale(LC_ALL, 'de_DE.UTF-8', 'deu_deu'); + +/** Create a new Reader of the type defined in $inputFileType **/ +$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType); +/** Enable loading numeric values formatted with German , decimal separator and . thousands separator **/ +$reader->castFormattedNumberToNumeric(true); + +/** Load the file to a Spreadsheet Object **/ +$spreadsheet = $reader->load($inputFileName); +``` +This will attempt to load those formatted numeric values as numbers, based on the server's locale settings. + +If you want to load those values as numbers, but also to retain the formatting as a number format mask, then you can pass a boolean `true` as a second argument to the `castFormattedNumberToNumeric()` method to tell the Reader to identify the format masking to use for that value. This option does have an arbitrary limit of 6 decimal places. + +If your Csv file includes other formats for numbers (currencies, scientific format, etc); then you should probably also use the Advanced Value Binder to handle these cases. + +Applies to: + +Reader | Y/N |Reader | Y/N |Reader | Y/N | +----------|:---:|--------|:---:|--------------|:---:| +Xlsx | NO | Xls | NO | Xml | NO | +Ods | NO | SYLK | NO | Gnumeric | NO | +CSV | YES | HTML | NO + ### A Brief Word about the Advanced Value Binder When loading data from a file that contains no formatting information, From e87be5e16d411063a5b4ebaf61061784cdcc3402 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Fri, 4 Mar 2022 00:11:04 -0800 Subject: [PATCH 4/6] Failing Test Requiring Too Much Precision (#2647) The new array tests for IMCSC fail on my system because of a rounding error in the 14th (!) decimal position. This is not a real failure. Change the test to use only the first 8 decimal positions. --- .../Calculation/Functions/Engineering/ImCscTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/ImCscTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/ImCscTest.php index ee21bd1a..bb1ca92b 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/ImCscTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/ImCscTest.php @@ -52,6 +52,18 @@ class ImCscTest extends TestCase $formula = "=IMCSC({$complex})"; $result = $calculation->_calculateFormulaValue($formula); + // Avoid testing for excess precision + foreach ($expectedResult as &$array) { + foreach ($array as &$string) { + $string = preg_replace('/(\\d{8})\\d+/', '$1', $string); + } + } + foreach ($result as &$array) { + foreach ($array as &$string) { + $string = preg_replace('/(\\d{8})\\d+/', '$1', $string); + } + } + self::assertEquals($expectedResult, $result); } From dade9cc063c76bd2f50f4fa52454f32f8c38a67c Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Fri, 4 Mar 2022 00:51:11 -0800 Subject: [PATCH 5/6] Corrupt Sample Output for 20 and 30 (#2648) Xls Reader can read drawing offsetX and offsetY as float. However, Excel Xlsx (and PhpSpreadsheet) wants them only as int. This leads 20_Read_Xls and 30_Template to produce corrupt Xlsx files for any Php release. Change Xls Reader to treat values as int, eliminating the corrupt files. --- src/PhpSpreadsheet/Reader/Xls.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Xls.php b/src/PhpSpreadsheet/Reader/Xls.php index 6fa51a1f..0fd05c87 100644 --- a/src/PhpSpreadsheet/Reader/Xls.php +++ b/src/PhpSpreadsheet/Reader/Xls.php @@ -1101,8 +1101,8 @@ class Xls extends BaseReader $height = SharedXls::getDistanceY($this->phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY); // calculate offsetX and offsetY of the shape - $offsetX = $startOffsetX * SharedXls::sizeCol($this->phpSheet, $startColumn) / 1024; - $offsetY = $startOffsetY * SharedXls::sizeRow($this->phpSheet, $startRow) / 256; + $offsetX = (int) ($startOffsetX * SharedXls::sizeCol($this->phpSheet, $startColumn) / 1024); + $offsetY = (int) ($startOffsetY * SharedXls::sizeRow($this->phpSheet, $startRow) / 256); switch ($obj['otObjType']) { case 0x19: From 23631cb9e53c53d625a586d19f0c8b0c65585c86 Mon Sep 17 00:00:00 2001 From: Paul Weidner Date: Fri, 4 Mar 2022 01:41:21 -0800 Subject: [PATCH 6/6] Correct fill color example in conditional formatting docs. (#2646) --- docs/topics/conditional-formatting.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/topics/conditional-formatting.md b/docs/topics/conditional-formatting.md index dc6ec756..086a7942 100644 --- a/docs/topics/conditional-formatting.md +++ b/docs/topics/conditional-formatting.md @@ -43,7 +43,7 @@ $conditional->setOperatorType(\PhpOffice\PhpSpreadsheet\Style\Conditional::OPERA $conditional->addCondition(80); $conditional->getStyle()->getFont()->getColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_DARKGREEN); $conditional->getStyle()->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID); -$conditional->getStyle()->getFill()->getColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_GREEN); +$conditional->getStyle()->getFill()->getStartColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_GREEN); $conditionalStyles = $spreadsheet->getActiveSheet()->getStyle('A1:A10')->getConditionalStyles(); $conditionalStyles[] = $conditional; @@ -63,7 +63,7 @@ $wizard = $wizardFactory->newRule(\PhpOffice\PhpSpreadsheet\Style\ConditionalFor $wizard->greaterThan(80); $wizard->getStyle()->getFont()->getColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_DARKGREEN); $wizard->getStyle()->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID); -$wizard->getStyle()->getFill()->getColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_GREEN); +$wizard->getStyle()->getFill()->getStartColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_GREEN); $conditional = $wizard->getConditional(); ``` @@ -84,7 +84,7 @@ $conditional2->setOperatorType(\PhpOffice\PhpSpreadsheet\Style\Conditional::OPER $conditional2->addCondition(10); $conditional2->getStyle()->getFont()->getColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_DARKRED); $conditional2->getStyle()->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID); -$conditional2->getStyle()->getFill()->getColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_RED); +$conditional2->getStyle()->getFill()->getStartColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_RED); $conditionalStyles = $spreadsheet->getActiveSheet()->getStyle('A1:A10')->getConditionalStyles(); $conditionalStyles[] = $conditional2; @@ -98,7 +98,7 @@ $wizard = $wizardFactory->newRule(\PhpOffice\PhpSpreadsheet\Style\ConditionalFor $wizard->lessThan(10); $wizard->getStyle()->getFont()->getColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_DARKGREEN); $wizard->getStyle()->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID); -$wizard->getStyle()->getFill()->getColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_GREEN); +$wizard->getStyle()->getFill()->getStartColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_GREEN); $conditional = $wizard->getConditional(); ```