From 082266aacd37b8b736180baa40bab4b31d2256f6 Mon Sep 17 00:00:00 2001 From: oleibman Date: Sat, 4 Jan 2020 09:50:04 -0800 Subject: [PATCH] Conditionals - Extend Support for (NOT)CONTAINSBLANKS (#1278) Support for the CONTAINSBLANKS conditional style was added a while ago. However, that support was on write only; any cells which used CONTAINSBLANKS on a file being read would drop that style. I am also adding support for NOTCONTAINSBLANKS, on read and write. --- .../Reader/Xlsx/ConditionalStyles.php | 2 + src/PhpSpreadsheet/Style/Conditional.php | 1 + src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 3 + .../PhpSpreadsheetTests/Reader/Xlsx2Test.php | 123 ++++++++++++++++++ .../XLSX/conditionalFormatting2Test.xlsx | Bin 0 -> 9038 bytes 5 files changed, 129 insertions(+) create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx2Test.php create mode 100644 tests/data/Reader/XLSX/conditionalFormatting2Test.xlsx diff --git a/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php b/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php index b3de5d1c..722b7795 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php @@ -36,6 +36,8 @@ class ConditionalStyles if (((string) $cfRule['type'] == Conditional::CONDITION_NONE || (string) $cfRule['type'] == Conditional::CONDITION_CELLIS || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT + || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSBLANKS + || (string) $cfRule['type'] == Conditional::CONDITION_NOTCONTAINSBLANKS || (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION) && isset($this->dxfs[(int) ($cfRule['dxfId'])])) { $conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule; diff --git a/src/PhpSpreadsheet/Style/Conditional.php b/src/PhpSpreadsheet/Style/Conditional.php index 91a000db..48375937 100644 --- a/src/PhpSpreadsheet/Style/Conditional.php +++ b/src/PhpSpreadsheet/Style/Conditional.php @@ -12,6 +12,7 @@ class Conditional implements IComparable const CONDITION_CONTAINSTEXT = 'containsText'; const CONDITION_EXPRESSION = 'expression'; const CONDITION_CONTAINSBLANKS = 'containsBlanks'; + const CONDITION_NOTCONTAINSBLANKS = 'notContainsBlanks'; // Operator types const OPERATOR_NONE = ''; diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index f148b7c3..bf811bdc 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -527,6 +527,9 @@ class Worksheet extends WriterPart } elseif ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSBLANKS) { // formula copied from ms xlsx xml source file $objWriter->writeElement('formula', 'LEN(TRIM(' . $cellCoordinate . '))=0'); + } elseif ($conditional->getConditionType() == Conditional::CONDITION_NOTCONTAINSBLANKS) { + // formula copied from ms xlsx xml source file + $objWriter->writeElement('formula', 'LEN(TRIM(' . $cellCoordinate . '))>0'); } $objWriter->endElement(); diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx2Test.php b/tests/PhpSpreadsheetTests/Reader/Xlsx2Test.php new file mode 100644 index 00000000..0544d74a --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx2Test.php @@ -0,0 +1,123 @@ +load($filename); + $worksheet = $spreadsheet->getActiveSheet(); + + $conditionalStyle = $worksheet->getConditionalStyles('A2:A8'); + self::assertNotEmpty($conditionalStyle); + $conditionalRule = $conditionalStyle[0]; + $conditions = $conditionalRule->getConditions(); + self::assertNotEmpty($conditions); + self::assertEquals(Conditional::CONDITION_NOTCONTAINSBLANKS, $conditionalRule->getConditionType()); + self::assertEquals('LEN(TRIM(A2))>0', $conditions[0]); + + $conditionalStyle = $worksheet->getConditionalStyles('B2:B8'); + self::assertNotEmpty($conditionalStyle); + $conditionalRule = $conditionalStyle[0]; + $conditions = $conditionalRule->getConditions(); + self::assertNotEmpty($conditions); + self::assertEquals(Conditional::CONDITION_CONTAINSBLANKS, $conditionalRule->getConditionType()); + self::assertEquals('LEN(TRIM(B2))=0', $conditions[0]); + + $conditionalStyle = $worksheet->getConditionalStyles('C2:C8'); + self::assertNotEmpty($conditionalStyle); + $conditionalRule = $conditionalStyle[0]; + $conditions = $conditionalRule->getConditions(); + self::assertNotEmpty($conditions); + self::assertEquals(Conditional::CONDITION_CELLIS, $conditionalRule->getConditionType()); + self::assertEquals(Conditional::OPERATOR_GREATERTHAN, $conditionalRule->getOperatorType()); + self::assertEquals('5', $conditions[0]); + } + + public function testReloadXlsxConditionalFormatting2() + { + // Make sure conditionals from existing file are maintained across save + $filename = './data/Reader/XLSX/conditionalFormatting2Test.xlsx'; + $outfile = tempnam(File::sysGetTempDir(), 'phpspreadsheet-test'); + $reader = IOFactory::createReader('Xlsx'); + $spreadshee1 = $reader->load($filename); + $writer = IOFactory::createWriter($spreadshee1, 'Xlsx'); + $writer->save($outfile); + $spreadsheet = $reader->load($outfile); + $worksheet = $spreadsheet->getActiveSheet(); + + $conditionalStyle = $worksheet->getConditionalStyles('A2:A8'); + self::assertNotEmpty($conditionalStyle); + $conditionalRule = $conditionalStyle[0]; + $conditions = $conditionalRule->getConditions(); + self::assertNotEmpty($conditions); + self::assertEquals(Conditional::CONDITION_NOTCONTAINSBLANKS, $conditionalRule->getConditionType()); + self::assertEquals('LEN(TRIM(A2:A8))>0', $conditions[0]); + + $conditionalStyle = $worksheet->getConditionalStyles('B2:B8'); + self::assertNotEmpty($conditionalStyle); + $conditionalRule = $conditionalStyle[0]; + $conditions = $conditionalRule->getConditions(); + self::assertNotEmpty($conditions); + self::assertEquals(Conditional::CONDITION_CONTAINSBLANKS, $conditionalRule->getConditionType()); + self::assertEquals('LEN(TRIM(B2:B8))=0', $conditions[0]); + + $conditionalStyle = $worksheet->getConditionalStyles('C2:C8'); + self::assertNotEmpty($conditionalStyle); + $conditionalRule = $conditionalStyle[0]; + $conditions = $conditionalRule->getConditions(); + self::assertNotEmpty($conditions); + self::assertEquals(Conditional::CONDITION_CELLIS, $conditionalRule->getConditionType()); + self::assertEquals(Conditional::OPERATOR_GREATERTHAN, $conditionalRule->getOperatorType()); + self::assertEquals('5', $conditions[0]); + } + + public function testNewXlsxConditionalFormatting2() + { + // Make sure blanks/non-blanks added by PhpSpreadsheet are handled correctly + $outfile = tempnam(File::sysGetTempDir(), 'phpspreadsheet-test'); + $spreadshee1 = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); + $sheet = $spreadshee1->getActiveSheet(); + $sheet->setCellValue('A2', 'a2'); + $sheet->setCellValue('A4', 'a4'); + $sheet->setCellValue('A6', 'a6'); + $cond1 = new Conditional(); + $cond1->setConditionType(Conditional::CONDITION_CONTAINSBLANKS); + $cond1->getStyle()->getFill()->setFillType(Fill::FILL_SOLID); + $cond1->getStyle()->getFill()->getEndColor()->setARGB(Color::COLOR_RED); + $cond = [$cond1]; + $sheet->getStyle('A1:A6')->setConditionalStyles($cond); + $writer = IOFactory::createWriter($spreadshee1, 'Xlsx'); + $writer->save($outfile); + $reader = IOFactory::createReader('Xlsx'); + $spreadsheet = $reader->load($outfile); + $worksheet = $spreadsheet->getActiveSheet(); + + $conditionalStyle = $worksheet->getConditionalStyles('A1:A6'); + self::assertNotEmpty($conditionalStyle); + $conditionalRule = $conditionalStyle[0]; + $conditions = $conditionalRule->getConditions(); + self::assertNotEmpty($conditions); + self::assertEquals(Conditional::CONDITION_CONTAINSBLANKS, $conditionalRule->getConditionType()); + self::assertEquals('LEN(TRIM(A1:A6))=0', $conditions[0]); + } +} diff --git a/tests/data/Reader/XLSX/conditionalFormatting2Test.xlsx b/tests/data/Reader/XLSX/conditionalFormatting2Test.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..cf2fa87ed4ffaaff348512a67b2c82a4f5822c75 GIT binary patch literal 9038 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 literal 0 HcmV?d00001