From 0f515eee0b3fa2469a9f44aaff9e833bca0df4e6 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Wed, 15 May 2024 17:41:34 -0700 Subject: [PATCH] Xls Conditional Format Improvements While researching another problem, I noticed that font color was not working as expected for Xls Conditional Formats, at least not when a "non-standard" color is used. In such cases, the color might wind up being rendered as black. The reason is as follows. Xls Writer includes a color palette which is dynamically generated from the (non-Conditional) styles used in the workbook. Any colors used in the workbook are indexes to this dynamic palette. However, Conditional colors use a static palette found in class ColorMap to determine the index, so the determination of index will often not find a match, and, if a match is found, it is not necessarily correct. (Also, the ColorMap method was case-sensitive and needs to be insensitive.) In order to correct this, the `addColor` method in Xls Writer Workbook needs to be accessible to the Conditional logic which is found in Xls Writer Worksheet. This is accomplished by passing the Workbook in the Worksheet's constructor, and changing the method to public, and changing Conditional Font to use this method rather than ColorMap. The logic for Conditional Fill colors is similarly changed. Although Xls Conditional Fill has appeared to just not work, I was finally able to figure out the problem. Excel Xls Conditional Fill with fill type Solid requires that the fill color be specified as startColor, and that endColor be omitted. Our conditional samples used endColor, and are now changed to use startColor instead; the same is true for our online documentation, and for some tests. Xlsx continues to work as expected, and now Xls does at least some of the time. If the condition is one that Excel Xls does not recognize (e.g. cell contains), it will, of course, not work. A surprising situation that also doesn't work is the use of ISODD or ISEVEN in formulas. Those are "add-in functions" which are handled differently than other functions, and I'm not sure how to support them. I will document this in issue #3403. Samples 08_Conditional_Formatting(_2) had produced corrupt Xls versions. This turned out to be because the code was using hash codes to avoid having to write out duplicate conditionals; this is often a good idea, but not in this case. Allowing the duplicates fixes the corruption problem. Conditional Border colors also ought to figure in this change, but the current code does not support Border colors, and I have not yet been able to figure out how to implement it (BIFF format can be very messy to figure out). With this change, I could delete ColorMap altogether. However, it is a public class with a static public method, so maybe someone is using it for a purpose I'm not familiar with. I will just deprecate it. --- docs/topics/conditional-formatting.md | 8 +-- .../01_Basic_Comparisons.php | 6 --- .../02_Text_Comparisons.php | 6 +-- .../03_Blank_Comparisons.php | 4 +- .../04_Error_Comparisons.php | 4 +- .../05_Date_Comparisons.php | 2 +- .../06_Duplicate_Comparisons.php | 4 +- .../07_Expression_Comparisons.php | 4 +- src/PhpSpreadsheet/Reader/Xls.php | 8 ++- src/PhpSpreadsheet/Writer/Xls.php | 2 +- .../Writer/Xls/Style/ColorMap.php | 10 +++- src/PhpSpreadsheet/Writer/Xls/Workbook.php | 4 +- src/PhpSpreadsheet/Writer/Xls/Worksheet.php | 24 ++++----- .../Calculation/XlfnFunctionsTest.php | 8 +-- tests/PhpSpreadsheetTests/Cell/CellTest.php | 22 ++++----- .../Functional/ConditionalStopIfTrueTest.php | 4 +- .../Reader/Xls/IsOddTest.php | 27 ++++++++++ .../Reader/Xlsx/Xlsx2Test.php | 2 +- .../Style/ConditionalBoolTest.php | 4 +- .../Style/ConditionalTest.php | 8 +-- .../Writer/Xls/ConditionalFontColorTest.php | 46 ++++++++++++++++++ tests/data/Reader/XLS/isodd.xls | Bin 0 -> 30208 bytes 22 files changed, 144 insertions(+), 63 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Xls/IsOddTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Xls/ConditionalFontColorTest.php create mode 100644 tests/data/Reader/XLS/isodd.xls diff --git a/docs/topics/conditional-formatting.md b/docs/topics/conditional-formatting.md index bc04c7cc0..4e79e3371 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()->getEndColor()->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()->getEndColor()->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()->getEndColor()->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()->getEndColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_GREEN); +$wizard->getStyle()->getFill()->getStartColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_GREEN); $conditional = $wizard->getConditional(); ``` diff --git a/samples/ConditionalFormatting/01_Basic_Comparisons.php b/samples/ConditionalFormatting/01_Basic_Comparisons.php index 5c5381aee..781a5bd21 100644 --- a/samples/ConditionalFormatting/01_Basic_Comparisons.php +++ b/samples/ConditionalFormatting/01_Basic_Comparisons.php @@ -74,22 +74,16 @@ $yellowStyle = new Style(false, true); $yellowStyle->getFill() ->setFillType(Fill::FILL_SOLID) ->getStartColor()->setARGB(Color::COLOR_YELLOW); -$yellowStyle->getFill() - ->getEndColor()->setARGB(Color::COLOR_YELLOW); $yellowStyle->getFont()->setColor(new Color(Color::COLOR_BLUE)); $greenStyle = new Style(false, true); $greenStyle->getFill() ->setFillType(Fill::FILL_SOLID) ->getStartColor()->setARGB(Color::COLOR_GREEN); -$greenStyle->getFill() - ->getEndColor()->setARGB(Color::COLOR_GREEN); $greenStyle->getFont()->setColor(new Color(Color::COLOR_DARKRED)); $redStyle = new Style(false, true); $redStyle->getFill() ->setFillType(Fill::FILL_SOLID) ->getStartColor()->setARGB(Color::COLOR_RED); -$redStyle->getFill() - ->getEndColor()->setARGB(Color::COLOR_RED); $redStyle->getFont()->setColor(new Color(Color::COLOR_GREEN)); // Set conditional formatting rules and styles diff --git a/samples/ConditionalFormatting/02_Text_Comparisons.php b/samples/ConditionalFormatting/02_Text_Comparisons.php index 10cd25b89..dfccf8571 100644 --- a/samples/ConditionalFormatting/02_Text_Comparisons.php +++ b/samples/ConditionalFormatting/02_Text_Comparisons.php @@ -73,17 +73,17 @@ $helper->log('Define some styles for our Conditionals'); $yellowStyle = new Style(false, true); $yellowStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_YELLOW); + ->getStartColor()->setARGB(Color::COLOR_YELLOW); $yellowStyle->getFont()->setColor(new Color(Color::COLOR_BLUE)); $greenStyle = new Style(false, true); $greenStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_GREEN); + ->getStartColor()->setARGB(Color::COLOR_GREEN); $greenStyle->getFont()->setColor(new Color(Color::COLOR_DARKRED)); $redStyle = new Style(false, true); $redStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_RED); + ->getStartColor()->setARGB(Color::COLOR_RED); $redStyle->getFont()->setColor(new Color(Color::COLOR_GREEN)); // Set conditional formatting rules and styles diff --git a/samples/ConditionalFormatting/03_Blank_Comparisons.php b/samples/ConditionalFormatting/03_Blank_Comparisons.php index 33b17ef6e..17dca3545 100644 --- a/samples/ConditionalFormatting/03_Blank_Comparisons.php +++ b/samples/ConditionalFormatting/03_Blank_Comparisons.php @@ -45,12 +45,12 @@ $helper->log('Define some styles for our Conditionals'); $greenStyle = new Style(false, true); $greenStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_GREEN); + ->getStartColor()->setARGB(Color::COLOR_GREEN); $greenStyle->getFont()->setColor(new Color(Color::COLOR_DARKRED)); $redStyle = new Style(false, true); $redStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_RED); + ->getStartColor()->setARGB(Color::COLOR_RED); $redStyle->getFont()->setColor(new Color(Color::COLOR_GREEN)); // Set conditional formatting rules and styles diff --git a/samples/ConditionalFormatting/04_Error_Comparisons.php b/samples/ConditionalFormatting/04_Error_Comparisons.php index 1c9c7b58b..cb11c81d8 100644 --- a/samples/ConditionalFormatting/04_Error_Comparisons.php +++ b/samples/ConditionalFormatting/04_Error_Comparisons.php @@ -48,12 +48,12 @@ $helper->log('Define some styles for our Conditionals'); $greenStyle = new Style(false, true); $greenStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_GREEN); + ->getStartColor()->setARGB(Color::COLOR_GREEN); $greenStyle->getFont()->setColor(new Color(Color::COLOR_DARKRED)); $redStyle = new Style(false, true); $redStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_RED); + ->getStartColor()->setARGB(Color::COLOR_RED); $redStyle->getFont()->setColor(new Color(Color::COLOR_GREEN)); // Set conditional formatting rules and styles diff --git a/samples/ConditionalFormatting/05_Date_Comparisons.php b/samples/ConditionalFormatting/05_Date_Comparisons.php index 9384a826c..34d0615e0 100644 --- a/samples/ConditionalFormatting/05_Date_Comparisons.php +++ b/samples/ConditionalFormatting/05_Date_Comparisons.php @@ -111,7 +111,7 @@ $helper->log('Define some styles for our Conditionals'); $yellowStyle = new Style(false, true); $yellowStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_YELLOW); + ->getStartColor()->setARGB(Color::COLOR_YELLOW); $yellowStyle->getFont()->setColor(new Color(Color::COLOR_BLUE)); // Set conditional formatting rules and styles diff --git a/samples/ConditionalFormatting/06_Duplicate_Comparisons.php b/samples/ConditionalFormatting/06_Duplicate_Comparisons.php index cbed0eb2d..98b8dcfbb 100644 --- a/samples/ConditionalFormatting/06_Duplicate_Comparisons.php +++ b/samples/ConditionalFormatting/06_Duplicate_Comparisons.php @@ -54,12 +54,12 @@ $helper->log('Define some styles for our Conditionals'); $yellowStyle = new Style(false, true); $yellowStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_YELLOW); + ->getStartColor()->setARGB(Color::COLOR_YELLOW); $yellowStyle->getFont()->setColor(new Color(Color::COLOR_BLUE)); $greenStyle = new Style(false, true); $greenStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_GREEN); + ->getStartColor()->setARGB(Color::COLOR_GREEN); $greenStyle->getFont()->setColor(new Color(Color::COLOR_DARKRED)); // Set conditional formatting rules and styles diff --git a/samples/ConditionalFormatting/07_Expression_Comparisons.php b/samples/ConditionalFormatting/07_Expression_Comparisons.php index 0f2b1a841..aa064201d 100644 --- a/samples/ConditionalFormatting/07_Expression_Comparisons.php +++ b/samples/ConditionalFormatting/07_Expression_Comparisons.php @@ -73,12 +73,12 @@ $helper->log('Define some styles for our Conditionals'); $yellowStyle = new Style(false, true); $yellowStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_YELLOW); + ->getStartColor()->setARGB(Color::COLOR_YELLOW); $yellowStyle->getFont()->setColor(new Color(Color::COLOR_BLUE)); $greenStyle = new Style(false, true); $greenStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_GREEN); + ->getStartColor()->setARGB(Color::COLOR_GREEN); $greenStyle->getFont()->setColor(new Color(Color::COLOR_DARKRED)); $greenStyleMoney = clone $greenStyle; diff --git a/src/PhpSpreadsheet/Reader/Xls.php b/src/PhpSpreadsheet/Reader/Xls.php index 5910ad4cc..01fc8dafa 100644 --- a/src/PhpSpreadsheet/Reader/Xls.php +++ b/src/PhpSpreadsheet/Reader/Xls.php @@ -7493,11 +7493,15 @@ class Xls extends BaseReader // bit: 0-6; mask: 0x007F; type $color1 = (0x007F & $fillColors) >> 0; - $style->getFill()->getStartColor()->setRGB(Xls\Color::map($color1, $this->palette, $this->version)['rgb']); // bit: 7-13; mask: 0x3F80; type $color2 = (0x3F80 & $fillColors) >> 7; - $style->getFill()->getEndColor()->setRGB(Xls\Color::map($color2, $this->palette, $this->version)['rgb']); + if ($fillPattern === Fill::FILL_SOLID) { + $style->getFill()->getStartColor()->setRGB(Xls\Color::map($color2, $this->palette, $this->version)['rgb']); + } else { + $style->getFill()->getStartColor()->setRGB(Xls\Color::map($color1, $this->palette, $this->version)['rgb']); + $style->getFill()->getEndColor()->setRGB(Xls\Color::map($color2, $this->palette, $this->version)['rgb']); + } } } diff --git a/src/PhpSpreadsheet/Writer/Xls.php b/src/PhpSpreadsheet/Writer/Xls.php index 9e57afd46..401fad18b 100644 --- a/src/PhpSpreadsheet/Writer/Xls.php +++ b/src/PhpSpreadsheet/Writer/Xls.php @@ -119,7 +119,7 @@ class Xls extends BaseWriter // Initialise worksheet writers $countSheets = $this->spreadsheet->getSheetCount(); for ($i = 0; $i < $countSheets; ++$i) { - $this->writerWorksheets[$i] = new Worksheet($this->strTotal, $this->strUnique, $this->strTable, $this->colors, $this->parser, $this->preCalculateFormulas, $this->spreadsheet->getSheet($i)); + $this->writerWorksheets[$i] = new Worksheet($this->strTotal, $this->strUnique, $this->strTable, $this->colors, $this->parser, $this->preCalculateFormulas, $this->spreadsheet->getSheet($i), $this->writerWorkbook); } // build Escher objects. Escher objects for workbooks needs to be build before Escher object for workbook. diff --git a/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php b/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php index 46f7bd5b4..209d13bce 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php +++ b/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php @@ -4,6 +4,14 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Xls\Style; use PhpOffice\PhpSpreadsheet\Style\Color; +/* + * Static array incorrectly used by Xls Writer for Conditional Styles. + * + * @deprecated since version 2.2 + * + * @codecoverageignore + */ + class ColorMap { /** @@ -70,7 +78,7 @@ class ColorMap public static function lookup(Color $color, int $defaultIndex = 0x00): int { - $colorRgb = $color->getRGB(); + $colorRgb = strtoupper($color->getRGB()); if (is_string($colorRgb) && array_key_exists("#{$colorRgb}", self::$colorMap)) { return self::$colorMap["#{$colorRgb}"]; } diff --git a/src/PhpSpreadsheet/Writer/Xls/Workbook.php b/src/PhpSpreadsheet/Writer/Xls/Workbook.php index 7a31b5f50..62d4b08d9 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Workbook.php +++ b/src/PhpSpreadsheet/Writer/Xls/Workbook.php @@ -272,7 +272,7 @@ class Workbook extends BIFFwriter * * @return int Color index */ - private function addColor(string $rgb): int + public function addColor(string $rgb, int $default = 0): int { if (!isset($this->colors[$rgb])) { $color @@ -298,7 +298,7 @@ class Workbook extends BIFFwriter $this->colors[$rgb] = $colorIndex; } else { // no room for more custom colors, just map to black - $colorIndex = 0; + $colorIndex = $default; } } } else { diff --git a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php index dadfc7003..9b03b55f7 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php @@ -154,6 +154,8 @@ class Worksheet extends BIFFwriter private int $printHeaders; + private ?Workbook $writerWorkbook; + /** * Constructor. * @@ -165,7 +167,7 @@ class Worksheet extends BIFFwriter * @param bool $preCalculateFormulas Flag indicating whether formulas should be calculated or just written * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $phpSheet The worksheet to write */ - public function __construct(int &$str_total, int &$str_unique, array &$str_table, array &$colors, Parser $parser, bool $preCalculateFormulas, \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $phpSheet) + public function __construct(int &$str_total, int &$str_unique, array &$str_table, array &$colors, Parser $parser, bool $preCalculateFormulas, \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $phpSheet, ?Workbook $writerWorkbook = null) { // It needs to call its parent's constructor explicitly parent::__construct(); @@ -208,6 +210,7 @@ class Worksheet extends BIFFwriter if ($this->lastColumnIndex > 255) { $this->lastColumnIndex = 255; } + $this->writerWorkbook = $writerWorkbook; } /** @@ -491,8 +494,6 @@ class Worksheet extends BIFFwriter $arrConditionalStyles = $this->phpSheet->getConditionalStylesCollection(); if (!empty($arrConditionalStyles)) { - $arrConditional = []; - // Write ConditionalFormattingTable records foreach ($arrConditionalStyles as $cellCoordinate => $conditionalStyles) { $cfHeaderWritten = false; @@ -506,10 +507,7 @@ class Worksheet extends BIFFwriter if ($cfHeaderWritten === false) { $cfHeaderWritten = $this->writeCFHeader($cellCoordinate, $conditionalStyles); } - if ($cfHeaderWritten === true && !isset($arrConditional[$conditional->getHashCode()])) { - // This hash code has been handled - $arrConditional[$conditional->getHashCode()] = true; - + if ($cfHeaderWritten === true) { // Write CFRULE record $this->writeCFRule($conditionalFormulaHelper, $conditional, $cellCoordinate); } @@ -2971,8 +2969,7 @@ class Worksheet extends BIFFwriter // Not used (3) $dataBlockFont .= pack('vC', 0x0000, 0x00); // Font color index - $colorIdx = Style\ColorMap::lookup($conditional->getStyle()->getFont()->getColor(), 0x00); - + $colorIdx = $this->workbookColorIndex($conditional->getStyle()->getFont()->getColor()->getRgb(), 0); $dataBlockFont .= pack('V', $colorIdx); // Not used (4) $dataBlockFont .= pack('V', 0x00000000); @@ -3043,9 +3040,9 @@ class Worksheet extends BIFFwriter // Fill Pattern Style $blockFillPatternStyle = Style\CellFill::style($conditional->getStyle()->getFill()); // Background Color - $colorIdxBg = Style\ColorMap::lookup($conditional->getStyle()->getFill()->getStartColor(), 0x41); + $colorIdxBg = $this->workbookColorIndex($conditional->getStyle()->getFill()->getStartColor()->getRgb(), 0x41); // Foreground Color - $colorIdxFg = Style\ColorMap::lookup($conditional->getStyle()->getFill()->getEndColor(), 0x40); + $colorIdxFg = $this->workbookColorIndex($conditional->getStyle()->getFill()->getEndColor()->getRgb(), 0x40); $dataBlockFill = pack('v', $blockFillPatternStyle); $dataBlockFill .= pack('v', $colorIdxFg | ($colorIdxBg << 7)); @@ -3142,4 +3139,9 @@ class Worksheet extends BIFFwriter return $dataBlockProtection; } + + private function workbookColorIndex(?string $rgb, int $default): int + { + return (empty($rgb) || $this->writerWorkbook === null) ? $default : $this->writerWorkbook->addColor($rgb, $default); + } } diff --git a/tests/PhpSpreadsheetTests/Calculation/XlfnFunctionsTest.php b/tests/PhpSpreadsheetTests/Calculation/XlfnFunctionsTest.php index 73d1af3cc..fbeb84622 100644 --- a/tests/PhpSpreadsheetTests/Calculation/XlfnFunctionsTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/XlfnFunctionsTest.php @@ -54,24 +54,24 @@ class XlfnFunctionsTest extends \PHPUnit\Framework\TestCase $condition0->setConditionType(Conditional::CONDITION_EXPRESSION); $condition0->addCondition('ABS(B3)<2'); $condition0->getStyle()->getFill()->setFillType(Fill::FILL_SOLID); - $condition0->getStyle()->getFill()->getEndColor()->setARGB(Color::COLOR_RED); + $condition0->getStyle()->getFill()->getStartColor()->setARGB(Color::COLOR_RED); $condition1 = new Conditional(); $condition1->setConditionType(Conditional::CONDITION_EXPRESSION); $condition1->addCondition('ABS(B3)>2'); $condition1->getStyle()->getFill()->setFillType(Fill::FILL_SOLID); - $condition1->getStyle()->getFill()->getEndColor()->setARGB(Color::COLOR_GREEN); + $condition1->getStyle()->getFill()->getStartColor()->setARGB(Color::COLOR_GREEN); $cond = [$condition0, $condition1]; $sheet->getStyle('B3:B5')->setConditionalStyles($cond); $condition0 = new Conditional(); $condition0->setConditionType(Conditional::CONDITION_EXPRESSION); $condition0->addCondition('ISOWEEKNUM(A3)<10'); $condition0->getStyle()->getFill()->setFillType(Fill::FILL_SOLID); - $condition0->getStyle()->getFill()->getEndColor()->setARGB(Color::COLOR_RED); + $condition0->getStyle()->getFill()->getStartColor()->setARGB(Color::COLOR_RED); $condition1 = new Conditional(); $condition1->setConditionType(Conditional::CONDITION_EXPRESSION); $condition1->addCondition('ISOWEEKNUM(A3)>40'); $condition1->getStyle()->getFill()->setFillType(Fill::FILL_SOLID); - $condition1->getStyle()->getFill()->getEndColor()->setARGB(Color::COLOR_GREEN); + $condition1->getStyle()->getFill()->getStartColor()->setARGB(Color::COLOR_GREEN); $cond = [$condition0, $condition1]; $sheet->getStyle('A3:A5')->setConditionalStyles($cond); $sheet->setSelectedCell('B1'); diff --git a/tests/PhpSpreadsheetTests/Cell/CellTest.php b/tests/PhpSpreadsheetTests/Cell/CellTest.php index fee24c78c..8bd4b9f8f 100644 --- a/tests/PhpSpreadsheetTests/Cell/CellTest.php +++ b/tests/PhpSpreadsheetTests/Cell/CellTest.php @@ -195,15 +195,15 @@ class CellTest extends TestCase $yellowStyle = new Style(false, true); $yellowStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_YELLOW); + ->getStartColor()->setARGB(Color::COLOR_YELLOW); $greenStyle = new Style(false, true); $greenStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_GREEN); + ->getStartColor()->setARGB(Color::COLOR_GREEN); $redStyle = new Style(false, true); $redStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_RED); + ->getStartColor()->setARGB(Color::COLOR_RED); $conditionalStyles = []; $wizardFactory = new Wizard($cellRange); @@ -228,22 +228,22 @@ class CellTest extends TestCase $style = $sheet->getCell('A1')->getAppliedStyle(); self::assertTrue($style->getFont()->getBold()); self::assertEquals($redStyle->getFill()->getFillType(), $style->getFill()->getFillType()); - self::assertEquals($redStyle->getFill()->getEndColor()->getARGB(), $style->getFill()->getEndColor()->getARGB()); + self::assertEquals($redStyle->getFill()->getStartColor()->getARGB(), $style->getFill()->getStartColor()->getARGB()); $style = $sheet->getCell('A2')->getAppliedStyle(); self::assertTrue($style->getFont()->getBold()); self::assertEquals($yellowStyle->getFill()->getFillType(), $style->getFill()->getFillType()); self::assertEquals( - $yellowStyle->getFill()->getEndColor()->getARGB(), - $style->getFill()->getEndColor()->getARGB() + $yellowStyle->getFill()->getStartColor()->getARGB(), + $style->getFill()->getStartColor()->getARGB() ); $style = $sheet->getCell('A3')->getAppliedStyle(); self::assertTrue($style->getFont()->getBold()); self::assertEquals($greenStyle->getFill()->getFillType(), $style->getFill()->getFillType()); self::assertEquals( - $greenStyle->getFill()->getEndColor()->getARGB(), - $style->getFill()->getEndColor()->getARGB() + $greenStyle->getFill()->getStartColor()->getARGB(), + $style->getFill()->getStartColor()->getARGB() ); } @@ -266,11 +266,11 @@ class CellTest extends TestCase $yellowStyle = new Style(false, true); $yellowStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_YELLOW); + ->getStartColor()->setARGB(Color::COLOR_YELLOW); $redStyle = new Style(false, true); $redStyle->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB(Color::COLOR_RED); + ->getStartColor()->setARGB(Color::COLOR_RED); $conditionalCellRange = 'A1:C1'; $conditionalStyles = []; @@ -294,7 +294,7 @@ class CellTest extends TestCase self::assertTrue($style->getFont()->getBold()); self::assertEquals($fillStyle, $style->getFill()->getFillType()); if ($fillStyle === Fill::FILL_SOLID) { - self::assertEquals($fillColor, $style->getFill()->getEndColor()->getARGB()); + self::assertEquals($fillColor, $style->getFill()->getStartColor()->getARGB()); } } diff --git a/tests/PhpSpreadsheetTests/Functional/ConditionalStopIfTrueTest.php b/tests/PhpSpreadsheetTests/Functional/ConditionalStopIfTrueTest.php index 06f4afe0c..b886c9e06 100644 --- a/tests/PhpSpreadsheetTests/Functional/ConditionalStopIfTrueTest.php +++ b/tests/PhpSpreadsheetTests/Functional/ConditionalStopIfTrueTest.php @@ -38,7 +38,7 @@ class ConditionalStopIfTrueTest extends AbstractFunctional $condition1->addCondition(0.6); $condition1->getStyle()->getFill() ->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID) - ->getEndColor()->setARGB(self::COLOR_RED); + ->getStartColor()->setARGB(self::COLOR_RED); // if value above 0.6 -> green background $condition2 = new \PhpOffice\PhpSpreadsheet\Style\Conditional(); @@ -47,7 +47,7 @@ class ConditionalStopIfTrueTest extends AbstractFunctional $condition2->addCondition(0.6); $condition2->getStyle()->getFill() ->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID) - ->getEndColor()->setARGB(self::COLOR_GREEN); + ->getStartColor()->setARGB(self::COLOR_GREEN); $spreadsheet = new Spreadsheet(); $spreadsheet->getActiveSheet()->getCell('A1')->setValue(0.7); diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/IsOddTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/IsOddTest.php new file mode 100644 index 000000000..0fc2063da --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xls/IsOddTest.php @@ -0,0 +1,27 @@ +load($filename); + $sheet = $spreadsheet->getActiveSheet(); + self::assertSame('=ISODD(5)', $sheet->getCell('A1')->getValue()); + self::assertTrue($sheet->getCell('A1')->getCalculatedValue()); + self::assertSame('=ISEVEN(5)', $sheet->getCell('B1')->getValue()); + self::assertFalse($sheet->getCell('B1')->getCalculatedValue()); + $spreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/Xlsx2Test.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/Xlsx2Test.php index 9a9e1e26d..032fa13d2 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xlsx/Xlsx2Test.php +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/Xlsx2Test.php @@ -98,7 +98,7 @@ class Xlsx2Test extends TestCase $cond1 = new Conditional(); $cond1->setConditionType(Conditional::CONDITION_CONTAINSBLANKS); $cond1->getStyle()->getFill()->setFillType(Fill::FILL_SOLID); - $cond1->getStyle()->getFill()->getEndColor()->setARGB(Color::COLOR_RED); + $cond1->getStyle()->getFill()->getStartColor()->setARGB(Color::COLOR_RED); $cond = [$cond1]; $sheet->getStyle('A1:A6')->setConditionalStyles($cond); $writer = IOFactory::createWriter($spreadshee1, 'Xlsx'); diff --git a/tests/PhpSpreadsheetTests/Style/ConditionalBoolTest.php b/tests/PhpSpreadsheetTests/Style/ConditionalBoolTest.php index f0d16058d..164671749 100644 --- a/tests/PhpSpreadsheetTests/Style/ConditionalBoolTest.php +++ b/tests/PhpSpreadsheetTests/Style/ConditionalBoolTest.php @@ -35,7 +35,7 @@ class ConditionalBoolTest extends TestCase $condition1->addCondition(false); $condition1->getStyle()->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB('FFFFFF00'); + ->getStartColor()->setARGB('FFFFFF00'); $conditionalStyles = $sheet->getStyle('A1:A10')->getConditionalStyles(); $conditionalStyles[] = $condition1; $sheet->getStyle('A1:A20')->setConditionalStyles($conditionalStyles); @@ -53,7 +53,7 @@ class ConditionalBoolTest extends TestCase $condition1->addCondition(true); $condition1->getStyle()->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB('FF00FF00'); + ->getStartColor()->setARGB('FF00FF00'); $conditionalStyles = $sheet->getStyle('A1:A10')->getConditionalStyles(); $conditionalStyles[] = $condition1; $sheet->getStyle('A1:A20')->setConditionalStyles($conditionalStyles); diff --git a/tests/PhpSpreadsheetTests/Style/ConditionalTest.php b/tests/PhpSpreadsheetTests/Style/ConditionalTest.php index e29043166..e81b560ea 100644 --- a/tests/PhpSpreadsheetTests/Style/ConditionalTest.php +++ b/tests/PhpSpreadsheetTests/Style/ConditionalTest.php @@ -18,7 +18,7 @@ class ConditionalTest extends TestCase $condition1->addCondition(0.6); $condition1->getStyle()->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB('FFFF0000'); + ->getStartColor()->setARGB('FFFF0000'); $conditionclone = clone $condition1; self::AssertEquals($condition1, $conditionclone); self::AssertEquals($condition1->getStyle(), $conditionclone->getStyle()); @@ -33,21 +33,21 @@ class ConditionalTest extends TestCase $condition1->addCondition(0.6); $condition1->getStyle()->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB('FFFF0000'); + ->getStartColor()->setARGB('FFFF0000'); $condition2 = new Conditional(); $condition2->setConditionType(Conditional::CONDITION_CELLIS); $condition2->setOperatorType(Conditional::OPERATOR_LESSTHAN); $condition2->setConditions(0.6); $condition2->getStyle()->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB('FFFF0000'); + ->getStartColor()->setARGB('FFFF0000'); $condition3 = new Conditional(); $condition3->setConditionType(Conditional::CONDITION_CELLIS); $condition3->setOperatorType(Conditional::OPERATOR_LESSTHAN); $condition3->setConditions([0.6]); $condition3->getStyle()->getFill() ->setFillType(Fill::FILL_SOLID) - ->getEndColor()->setARGB('FFFF0000'); + ->getStartColor()->setARGB('FFFF0000'); self::AssertEquals($condition1, $condition2); self::AssertEquals($condition1, $condition3); } diff --git a/tests/PhpSpreadsheetTests/Writer/Xls/ConditionalFontColorTest.php b/tests/PhpSpreadsheetTests/Writer/Xls/ConditionalFontColorTest.php new file mode 100644 index 000000000..154c40dda --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Xls/ConditionalFontColorTest.php @@ -0,0 +1,46 @@ +getActiveSheet(); + $sheet->getCell('A1')->setValue(10); + $condition1 = new Conditional(); + $condition1->setConditionType(Conditional::CONDITION_CELLIS); + $condition1->setOperatorType(Conditional::OPERATOR_LESSTHAN); + $condition1->addCondition(20); + $condition1->getStyle()->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor()->setARGB('FF2345FA'); + $condition1->getStyle()->getFont() + ->setBold(true); + $condition1->getStyle()->getFont()->getColor() + ->setARGB('FFFF8193'); + $conditionalStyles[] = $condition1; + $sheet->getStyle('A1')->setConditionalStyles($conditionalStyles); + + $robj = $this->writeAndReload($spreadsheet, 'Xls'); + $spreadsheet->disconnectWorksheets(); + $sheet0 = $robj->setActiveSheetIndex(0); + $conditionals = $sheet0->getConditionalStylesCollection(); + self::assertCount(1, $conditionals); + self::assertArrayHasKey('A1', $conditionals); + $font = $conditionals['A1'][0]->getStyle()->getFont(); + self::assertSame('FFFF8193', $font->getColor()->getARGB()); + $fill = $conditionals['A1'][0]->getStyle()->getFill(); + self::assertSame('FF2345FA', $fill->getStartColor()->getARGB()); + self::assertSame(Fill::FILL_SOLID, $fill->getFillType()); + $robj->disconnectWorksheets(); + } +} diff --git a/tests/data/Reader/XLS/isodd.xls b/tests/data/Reader/XLS/isodd.xls new file mode 100644 index 0000000000000000000000000000000000000000..99bfc0303f9b4424917c76cc740ce82ecf077344 GIT binary patch literal 30208 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 literal 0 HcmV?d00001