From a2be574f3678ec41bcb99094e13d7785921aa806 Mon Sep 17 00:00:00 2001 From: Roman Devman Date: Tue, 19 Oct 2021 18:13:57 +0300 Subject: [PATCH] Restore imperfect array formula values in xlsx writer oleibman said: The results of uncommenting the statements will often not be successful. In Excel, if I enter `=MINVERSE({2,0;0,1})` into cell A1, you will get a `dynamic array` (which we do not yet support) - A1 will contain 0.5, A2 and B1 will contain 0, and B2 will contain 1. There are also `spill` implications for such a formula. The XML for these cells will be: ``` xml MINVERSE({2,0;0,1}) 0.5 0 0 1 ``` I believe that the PhpSpreadsheet equivalent of doing this (with the statements uncommented) is: ```php $spreadsheet = new Spreadsheet(); $calculation = Calculation::getInstance($spreadsheet); $calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_ARRAY); $sheet = $spreadsheet->getActiveSheet(); $sheet->getCell('A1')->setValue('=MINVERSE({2,0;0,1})'); $writer = new Xlsx($spreadsheet); $oufil = 'issue.2343.xlsx'; $writer->save($oufil); ``` But our output file only fills in A1: ```xml MINVERSE({2,0;0,1}) 0.5 ``` And, even though A1 has its correct value, note that its `f` tag does not have a `t` attribute. This is because we never set any formula attributes, except in Xlsx Reader (see next paragraph), so we do not encounter the `'array'` condtion for a formula newly added to a spreadsheet. We do slightly better when we read the first file (as opposed to creating a new spreadsheet), but we succeed only by accident. Because B1, A2, and B2 are assigned values in the XML, all 4 cells will have the expected values. But they are now independent of each other, not part of a dynamic array. When we write this out, it is almost correct: ```xml MINVERSE({2,0;0,1}) 0.5 0 0 1 ``` Again, the `f` tag has no `t` attribute, and it doesn't seem to matter whether we set RETURN_TYPE_ARRAY or not. I think this particular aspect of the problem might be relatively easy to fix. --- src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 41 ++++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 204c4953..3e1b537e 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -1205,27 +1205,26 @@ class Worksheet extends WriterPart $objWriter->writeAttribute('t', 'b'); $calculatedValue = (int) $calculatedValue; } - // array values are not yet supported - //$attributes = $pCell->getFormulaAttributes(); - //if (($attributes['t'] ?? null) === 'array') { - // $objWriter->startElement('f'); - // $objWriter->writeAttribute('t', 'array'); - // $objWriter->writeAttribute('ref', $pCellAddress); - // $objWriter->writeAttribute('aca', '1'); - // $objWriter->writeAttribute('ca', '1'); - // $objWriter->text(substr($cellValue, 1)); - // $objWriter->endElement(); - //} else { - // $objWriter->writeElement('f', Xlfn::addXlfnStripEquals($cellValue)); - //} - $objWriter->writeElement('f', Xlfn::addXlfnStripEquals($cellValue)); - self::writeElementIf( - $objWriter, - $this->getParentWriter()->getOffice2003Compatibility() === false, - 'v', - ($this->getParentWriter()->getPreCalculateFormulas() && !is_array($calculatedValue) && substr($calculatedValue ?? '', 0, 1) !== '#') - ? StringHelper::formatNumber($calculatedValue) : '0' - ); + + $attributes = $cell->getFormulaAttributes(); + if (($attributes['t'] ?? null) === 'array') { + $objWriter->startElement('f'); + $objWriter->writeAttribute('t', 'array'); + $objWriter->writeAttribute('ref', $cell->getCoordinate()); + $objWriter->writeAttribute('aca', '1'); + $objWriter->writeAttribute('ca', '1'); + $objWriter->text(substr($cellValue, 1)); + $objWriter->endElement(); + } else { + $objWriter->writeElement('f', Xlfn::addXlfnStripEquals($cellValue)); + self::writeElementIf( + $objWriter, + $this->getParentWriter()->getOffice2003Compatibility() === false, + 'v', + ($this->getParentWriter()->getPreCalculateFormulas() && !is_array($calculatedValue) && substr($calculatedValue, 0, 1) !== '#') + ? StringHelper::formatNumber($calculatedValue) : '0' + ); + } } /**