Resolve saving cell references, string literals and formula as values for conditional formatting rules in the Xls file save

The code is ugly as sin; but it works... I'll do some refactoring and cleaning later (once I've had some sleep, because I'm stupidly still working on this at 3am)

The main remaining issue is formulae that can't be parsed in BIFF8 files; e.g. formulae that use functions that aren't available. In this case, all CF in that worksheet is corrupted, and the file errors when opened, so it is a serious issue. The ISODD()/ISEVEN example in 07_Expression_Comparisons.php uses these, so I've temporarily commented out setting that CF range until I've solved that problem. There are other limitations listed in the BIFF documentation; but they're harder to detect.
I've also left a couple of debug statements in the code to display these formula errors: I'll remove them once I've resolved the issue.
This commit is contained in:
MarkBaker 2022-03-13 03:04:37 +01:00
parent 7e89d3397e
commit 9ca9d741fe
3 changed files with 59 additions and 19 deletions

View File

@ -28,6 +28,7 @@ $helper->log('Add data');
$spreadsheet->setActiveSheetIndex(0); $spreadsheet->setActiveSheetIndex(0);
$spreadsheet->getActiveSheet() $spreadsheet->getActiveSheet()
->setCellValue('A1', 'Odd/Even Expression Comparison') ->setCellValue('A1', 'Odd/Even Expression Comparison')
->setCellValue('A4', 'Note that these functions are not available for Xls files')
->setCellValue('A15', 'Sales Grid Expression Comparison') ->setCellValue('A15', 'Sales Grid Expression Comparison')
->setCellValue('A25', 'Sales Grid Multiple Expression Comparison'); ->setCellValue('A25', 'Sales Grid Multiple Expression Comparison');
@ -101,9 +102,9 @@ $expressionWizard->expression('ISEVEN(A1)')
->setStyle($yellowStyle); ->setStyle($yellowStyle);
$conditionalStyles[] = $expressionWizard->getConditional(); $conditionalStyles[] = $expressionWizard->getConditional();
$spreadsheet->getActiveSheet() //$spreadsheet->getActiveSheet()
->getStyle($expressionWizard->getCellRange()) // ->getStyle($expressionWizard->getCellRange())
->setConditionalStyles($conditionalStyles); // ->setConditionalStyles($conditionalStyles);
// Set rules for Sales Grid Row match against Country Comparison // Set rules for Sales Grid Row match against Country Comparison
$cellRange = 'A17:D22'; $cellRange = 'A17:D22';

View File

@ -122,7 +122,7 @@ abstract class WizardAbstract
return "{$worksheet}{$column}{$row}"; return "{$worksheet}{$column}{$row}";
} }
protected static function reverseAdjustCellRef(string $condition, string $cellRange): string public static function reverseAdjustCellRef(string $condition, string $cellRange): string
{ {
$conditionalRange = Coordinate::splitRange(str_replace('$', '', strtoupper($cellRange))); $conditionalRange = Coordinate::splitRange(str_replace('$', '', strtoupper($cellRange)));
[$referenceCell] = $conditionalRange[0]; [$referenceCell] = $conditionalRange[0];

View File

@ -13,6 +13,7 @@ use PhpOffice\PhpSpreadsheet\Shared\Xls;
use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Color; use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\Conditional; use PhpOffice\PhpSpreadsheet\Style\Conditional;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
use PhpOffice\PhpSpreadsheet\Style\Protection; use PhpOffice\PhpSpreadsheet\Style\Protection;
use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
use PhpOffice\PhpSpreadsheet\Worksheet\SheetView; use PhpOffice\PhpSpreadsheet\Worksheet\SheetView;
@ -569,7 +570,7 @@ class Worksheet extends BIFFwriter
$arrConditional[$conditional->getHashCode()] = true; $arrConditional[$conditional->getHashCode()] = true;
// Write CFRULE record // Write CFRULE record
$this->writeCFRule($conditional); $this->writeCFRule($conditional, $cellCoordinate);
} }
} }
} }
@ -2779,7 +2780,7 @@ class Worksheet extends BIFFwriter
/** /**
* Write CFRule Record. * Write CFRule Record.
*/ */
private function writeCFRule(Conditional $conditional): void private function writeCFRule(Conditional $conditional, string $cellRange): void
{ {
$record = 0x01B1; // Record identifier $record = 0x01B1; // Record identifier
$type = null; // Type of the CF $type = null; // Type of the CF
@ -2832,21 +2833,59 @@ class Worksheet extends BIFFwriter
// $szValue2 : size of the formula data for second value or formula // $szValue2 : size of the formula data for second value or formula
$arrConditions = $conditional->getConditions(); $arrConditions = $conditional->getConditions();
$numConditions = count($arrConditions); $numConditions = count($arrConditions);
if ($numConditions == 1) {
$szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
$szValue2 = 0x0000;
$operand1 = pack('Cv', 0x1E, $arrConditions[0]);
$operand2 = null;
} elseif ($numConditions == 2 && ($conditional->getOperatorType() == Conditional::OPERATOR_BETWEEN)) {
$szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
$szValue2 = ($arrConditions[1] <= 65535 ? 3 : 0x0000);
$operand1 = pack('Cv', 0x1E, $arrConditions[0]);
$operand2 = pack('Cv', 0x1E, $arrConditions[1]);
} else {
$szValue1 = 0x0000; $szValue1 = 0x0000;
$szValue2 = 0x0000; $szValue2 = 0x0000;
$operand1 = null; $operand1 = null;
$operand2 = null; $operand2 = null;
if ($numConditions == 1) {
if (is_int($arrConditions[0]) || is_float($arrConditions[0])) {
$szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
$operand1 = pack('Cv', 0x1E, $arrConditions[0]);
} else {
try {
$formula1 = Wizard\WizardAbstract::reverseAdjustCellRef((string) $arrConditions[0], $cellRange);
$this->parser->parse($formula1);
$formula1 = $this->parser->toReversePolish();
$szValue1 = strlen($formula1);
} catch (PhpSpreadsheetException $e) {
var_dump("PARSER EXCEPTION: {$e->getMessage()}");
$formula1 = null;
}
$operand1 = $formula1;
}
} elseif ($numConditions == 2 && ($conditional->getOperatorType() == Conditional::OPERATOR_BETWEEN)) {
if (is_int($arrConditions[0]) || is_float($arrConditions[0])) {
$szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
$operand1 = pack('Cv', 0x1E, $arrConditions[0]);
} else {
try {
$formula1 = Wizard\WizardAbstract::reverseAdjustCellRef((string) $arrConditions[0], $cellRange);
$this->parser->parse($formula1);
$formula1 = $this->parser->toReversePolish();
$szValue1 = strlen($formula1);
} catch (PhpSpreadsheetException $e) {
var_dump("PARSER EXCEPTION: {$e->getMessage()}");
$formula1 = null;
}
$operand1 = $formula1;
}
if (is_int($arrConditions[1]) || is_float($arrConditions[1])) {
$szValue2 = ($arrConditions[1] <= 65535 ? 3 : 0x0000);
$operand2 = pack('Cv', 0x1E, $arrConditions[1]);
} else {
try {
$formula2 = Wizard\WizardAbstract::reverseAdjustCellRef((string) $arrConditions[1], $cellRange);
$this->parser->parse($formula2);
$formula2 = $this->parser->toReversePolish();
$szValue2 = strlen($formula2);
} catch (PhpSpreadsheetException $e) {
var_dump("PARSER EXCEPTION: {$e->getMessage()}");
$formula2 = null;
}
$operand2 = $formula2;
}
} }
// $flags : Option flags // $flags : Option flags