diff --git a/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php b/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php index 8731d642e5..a03fa71b24 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php @@ -125,6 +125,7 @@ private function readConditionalRuleFromExt(SimpleXMLElement $cfRuleXml, SimpleX { $conditionType = (string) $attributes->type; $operatorType = (string) $attributes->operator; + $priority = (int) (string) $attributes->priority; $operands = []; foreach ($cfRuleXml->children($this->ns['xm']) as $cfRuleOperandsXml) { @@ -134,6 +135,7 @@ private function readConditionalRuleFromExt(SimpleXMLElement $cfRuleXml, SimpleX $conditional = new Conditional(); $conditional->setConditionType($conditionType); $conditional->setOperatorType($operatorType); + $conditional->setPriority($priority); if ( $conditionType === Conditional::CONDITION_CONTAINSTEXT || $conditionType === Conditional::CONDITION_NOTCONTAINSTEXT @@ -184,7 +186,7 @@ private function readConditionalStyles(SimpleXMLElement $xmlSheet): array private function setConditionalStyles(Worksheet $worksheet, array $conditionals, SimpleXMLElement $xmlExtLst): void { foreach ($conditionals as $cellRangeReference => $cfRules) { - ksort($cfRules); + ksort($cfRules); // no longer needed for Xlsx, but helps Xls $conditionalStyles = $this->readStyleRules($cfRules, $xmlExtLst); // Extract all cell references in $cellRangeReference @@ -205,6 +207,7 @@ private function readStyleRules(array $cfRules, SimpleXMLElement $extLst): array $objConditional = new Conditional(); $objConditional->setConditionType((string) $cfRule['type']); $objConditional->setOperatorType((string) $cfRule['operator']); + $objConditional->setPriority((int) (string) $cfRule['priority']); $objConditional->setNoFormatSet(!isset($cfRule['dxfId'])); if ((string) $cfRule['text'] != '') { diff --git a/src/PhpSpreadsheet/Style/Conditional.php b/src/PhpSpreadsheet/Style/Conditional.php index 01a4d8a9f3..d476bdffd2 100644 --- a/src/PhpSpreadsheet/Style/Conditional.php +++ b/src/PhpSpreadsheet/Style/Conditional.php @@ -106,6 +106,8 @@ class Conditional implements IComparable private bool $noFormatSet = false; + private int $priority = 0; + /** * Create a new Conditional. */ @@ -115,6 +117,18 @@ public function __construct() $this->style = new Style(false, true); } + public function getPriority(): int + { + return $this->priority; + } + + public function setPriority(int $priority): self + { + $this->priority = $priority; + + return $this; + } + public function getNoFormatSet(): bool { return $this->noFormatSet; diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 28af258297..98ee0bd31e 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -861,7 +861,12 @@ private static function writeColorScaleElements(XMLWriter $objWriter, ?Condition private function writeConditionalFormatting(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksheet): void { // Conditional id - $id = 1; + $id = 0; + foreach ($worksheet->getConditionalStylesCollection() as $conditionalStyles) { + foreach ($conditionalStyles as $conditional) { + $id = max($id, $conditional->getPriority()); + } + } // Loop through styles in the current worksheet foreach ($worksheet->getConditionalStylesCollection() as $cellCoordinate => $conditionalStyles) { @@ -888,7 +893,8 @@ private function writeConditionalFormatting(XMLWriter $objWriter, Phpspreadsheet 'dxfId', (string) $this->getParentWriter()->getStylesConditionalHashTable()->getIndexForHashCode($conditional->getHashCode()) ); - $objWriter->writeAttribute('priority', (string) $id++); + $priority = $conditional->getPriority() ?: ++$id; + $objWriter->writeAttribute('priority', (string) $priority); self::writeAttributeif( $objWriter, diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalPriorityTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalPriorityTest.php new file mode 100644 index 0000000000..542b78d865 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalPriorityTest.php @@ -0,0 +1,129 @@ +load($filename); + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx'); + $spreadsheet->disconnectWorksheets(); + $worksheet = $reloadedSpreadsheet->getActiveSheet(); + $priorities = []; + foreach ($worksheet->getConditionalStylesCollection() as $conditionalStyles) { + foreach ($conditionalStyles as $conditional) { + $priorities[] = $conditional->getPriority(); + } + } + $expected = [27, 2, 3, 4, 1, 22, 14, 5, 6, 7, 20]; + self::assertSame($expected, $priorities); + $reloadedSpreadsheet->disconnectWorksheets(); + } + + public function testZeroPriority(): void + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->fromArray([ + [1, 1, 1, 1], + [2, 2, 2, 2], + [3, 3, 3, 3], + [4, 4, 4, 4], + [5, 5, 5, 5], + ]); + + $range = 'A1:A5'; + $styles = []; + $new = new Conditional(); + $new->setConditionType(Conditional::CONDITION_CELLIS) + ->setOperatorType(Conditional::OPERATOR_EQUAL) + ->setPriority(30) + ->setConditions(['3']) + ->getStyle() + ->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor() + ->setArgb('FFC00000'); + $styles[] = $new; + $sheet->setConditionalStyles($range, $styles); + + $range = 'B1:B5'; + $styles = []; + $new = new Conditional(); + $new->setConditionType(Conditional::CONDITION_EXPRESSION) + ->setConditions('=MOD(A1,2)=0') + ->getStyle() + ->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor() + ->setArgb('FF00B0F0'); + $styles[] = $new; + $new = new Conditional(); + $new->setConditionType(Conditional::CONDITION_CELLIS) + ->setOperatorType(Conditional::OPERATOR_EQUAL) + ->setPriority(40) + ->setConditions(['4']) + ->getStyle() + ->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor() + ->setArgb('FFFFC000'); + $styles[] = $new; + $sheet->setConditionalStyles($range, $styles); + + $range = 'C1:C5'; + $styles = []; + $new = new Conditional(); + $new->setConditionType(Conditional::CONDITION_CELLIS) + ->setOperatorType(Conditional::OPERATOR_EQUAL) + ->setPriority(20) + ->setConditions(['2']) + ->getStyle() + ->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor() + ->setArgb('FFFFFF00'); + $styles[] = $new; + $new = new Conditional(); + $new->setConditionType(Conditional::CONDITION_CELLIS) + ->setOperatorType(Conditional::OPERATOR_EQUAL) + ->setConditions(['5']) + ->getStyle() + ->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor() + ->setArgb('FF008080'); + $styles[] = $new; + $sheet->setConditionalStyles($range, $styles); + + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx'); + $spreadsheet->disconnectWorksheets(); + $worksheet = $reloadedSpreadsheet->getActiveSheet(); + $priorities = []; + foreach ($worksheet->getConditionalStylesCollection() as $conditionalStyles) { + foreach ($conditionalStyles as $conditional) { + $priorities[] = $conditional->getPriority(); + } + } + // B1:B5 is written in order 41, 40, but Reader sorts them + $expected = [30, 40, 41, 20, 42]; + self::assertSame($expected, $priorities); + $styles = $worksheet->getConditionalStyles('B1:B5'); + self::assertSame(Conditional::CONDITION_CELLIS, $styles[0]->getConditionType()); + self::assertSame(40, $styles[0]->getPriority()); + self::assertSame(Conditional::CONDITION_EXPRESSION, $styles[1]->getConditionType()); + self::assertSame(41, $styles[1]->getPriority()); + $reloadedSpreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue4248Test.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue4248Test.php index 06b0b7f31c..de5ec8f029 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue4248Test.php +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue4248Test.php @@ -66,13 +66,13 @@ public function testStyles(): void $file .= '#xl/worksheets/sheet1.xml'; $data = file_get_contents($file) ?: ''; $expected = '' - . '' + . '' . 'NOT(ISERROR(SEARCH("Oui",C16)))' . '' . ''; self::assertStringContainsString($expected, $data, 'first condition for D18'); $expected = '' - . '' + . '' . 'NOT(ISERROR(SEARCH("Non",C16)))' . '' . ''; diff --git a/tests/data/Reader/XLSX/issue.4312c.xlsx b/tests/data/Reader/XLSX/issue.4312c.xlsx new file mode 100644 index 0000000000..0446ed6cc0 Binary files /dev/null and b/tests/data/Reader/XLSX/issue.4312c.xlsx differ