Skip to content

Commit

Permalink
Slk Shared Formulas (#3776)
Browse files Browse the repository at this point in the history
* Slk Shared Formulas

Fix #2267. The Slk format has a way to express a "shared formula", but the Slk reader does not yet understand it. Thanks to @SheetJSDev for documenting the problem and pointing the way towards a solution. It has taken a long time to get there. Part of the problem is that I have not been successful in getting Excel to use this type of construction when saving a Slk file. So I have resorted to saving a Slk file where shared formulas *could* be used, and then editing it by hand to actually use them. It would not surprise me in the least to have neglected one or more possible ways to specify a shared formula; but, at least the issue as documented is resolved, and if new issues arise, we'll probably be in better shape to deal with them.

* Update CHANGELOG.md
  • Loading branch information
oleibman authored Oct 30, 2023
1 parent dcccd63 commit 64a1d1a
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
- Missing Font Index in Some Xls. [PR #3734](https://github.com/PHPOffice/PhpSpreadsheet/pull/3734)
- Load Tables even with READ_DATA_ONLY. [PR #3726](https://github.com/PHPOffice/PhpSpreadsheet/pull/3726)
- Theme File Missing but Referenced in Spreadsheet. [Issue #3770](https://github.com/PHPOffice/PhpSpreadsheet/issues/3770) [PR #3772](https://github.com/PHPOffice/PhpSpreadsheet/pull/3772)
- Slk Shared Formulas. [Issue #2267](https://github.com/PHPOffice/PhpSpreadsheet/issues/2267) [PR #3776](https://github.com/PHPOffice/PhpSpreadsheet/pull/3776)

## 1.29.0 - 2023-06-15

Expand Down
32 changes: 30 additions & 2 deletions src/PhpSpreadsheet/Reader/Slk.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\ReferenceHelper;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Border;
Expand Down Expand Up @@ -269,14 +270,14 @@ private function processCRecord(array $rowData, Spreadsheet &$spreadsheet, strin
$hasCalculatedValue = false;
$tryNumeric = false;
$cellDataFormula = $cellData = '';
$sharedColumn = $sharedRow = -1;
$sharedFormula = false;
foreach ($rowData as $rowDatum) {
switch ($rowDatum[0]) {
case 'C':
case 'X':
$column = substr($rowDatum, 1);

break;
case 'R':
case 'Y':
$row = substr($rowDatum, 1);

Expand All @@ -298,9 +299,36 @@ private function processCRecord(array $rowData, Spreadsheet &$spreadsheet, strin
->getText()
->createText($comment);

break;
case 'C':
$sharedColumn = (int) substr($rowDatum, 1);

break;
case 'R':
$sharedRow = (int) substr($rowDatum, 1);

break;
case 'S':
$sharedFormula = true;

break;
}
}
if ($sharedFormula === true && $sharedRow >= 0 && $sharedColumn >= 0) {
$thisCoordinate = Coordinate::stringFromColumnIndex((int) $column) . $row;
$sharedCoordinate = Coordinate::stringFromColumnIndex($sharedColumn) . $sharedRow;
$formula = $spreadsheet->getActiveSheet()->getCell($sharedCoordinate)->getValue();
$spreadsheet->getActiveSheet()->getCell($thisCoordinate)->setValue($formula);
$referenceHelper = ReferenceHelper::getInstance();
$newFormula = $referenceHelper->updateFormulaReferences($formula, 'A1', (int) $column - $sharedColumn, (int) $row - $sharedRow, '', true, false);
$spreadsheet->getActiveSheet()->getCell($thisCoordinate)->setValue($newFormula);
//$calc = $spreadsheet->getActiveSheet()->getCell($thisCoordinate)->getCalculatedValue();
//$spreadsheet->getActiveSheet()->getCell($thisCoordinate)->setCalculatedValue($calc);
$cellData = Calculation::unwrapResult($cellData);
$spreadsheet->getActiveSheet()->getCell($thisCoordinate)->setCalculatedValue($cellData, $tryNumeric);

return;
}
$columnLetter = Coordinate::stringFromColumnIndex((int) $column);
$cellData = Calculation::unwrapResult($cellData);

Expand Down
38 changes: 38 additions & 0 deletions tests/PhpSpreadsheetTests/Reader/Slk/SlkSharedFormulasTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Reader\Slk;

use PhpOffice\PhpSpreadsheet\Reader\Slk;

class SlkSharedFormulasTest extends \PHPUnit\Framework\TestCase
{
public function testComments(): void
{
$testbook = 'tests/data/Reader/Slk/issue.2267c.slk';
$reader = new Slk();
$spreadsheet = $reader->load($testbook);
$sheet = $spreadsheet->getActiveSheet();
$range = 'A1:' . $sheet->getHighestDataColumn() . $sheet->getHighestDataRow();
$values = $sheet->RangeToArray($range, null, false, false, false, false); // just get values, don't calculate
$expected = [
[1, 10, 100, 101, 102],
['=A1+1', '=B1+1', '=C1+1', '=D1+1', '=E1+1'],
['=A2+1', '=B2+1', '=C2+1', '=D2+1', '=E2+1'],
['=A3+1', '=B3+1', '=C3+1', '=D3+1', '=E3+1'],
['=A4+1', '=B4+1', '=C4+1', '=D4+1', '=E4+1'],
];
self::assertSame($expected, $values);
$calcValues = $sheet->RangeToArray($range, null, true, false, false, false); // get calculated values
$expectedCalc = [
[1, 10, 100, 101, 102],
[2, 11, 101, 102, 103],
[3, 12, 102, 103, 104],
[4, 13, 103, 104, 105],
[5, 14, 104, 105, 106],
];
self::assertSame($expectedCalc, $calcValues);
$spreadsheet->disconnectWorksheets();
}
}
89 changes: 89 additions & 0 deletions tests/data/Reader/Slk/issue.2267c.slk
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
ID;PWXL;N;E
P;PGeneral
P;P0
P;P0.00
P;P#,##0
P;P#,##0.00
P;P#,##0_);;\(#,##0\)
P;P#,##0_);;[Red]\(#,##0\)
P;P#,##0.00_);;\(#,##0.00\)
P;P#,##0.00_);;[Red]\(#,##0.00\)
P;P"$"#,##0_);;\("$"#,##0\)
P;P"$"#,##0_);;[Red]\("$"#,##0\)
P;P"$"#,##0.00_);;\("$"#,##0.00\)
P;P"$"#,##0.00_);;[Red]\("$"#,##0.00\)
P;P0%
P;P0.00%
P;P0.00E+00
P;P##0.0E+0
P;P#\ ?/?
P;P#\ ??/??
P;Pyyyy/mm/dd
P;Pdd/mmm/yy
P;Pdd/mmm
P;Pmmm/yy
P;Ph:mm\ AM/PM
P;Ph:mm:ss\ AM/PM
P;Ph:mm
P;Ph:mm:ss
P;Pyyyy/mm/dd\ h:mm
P;Pmm:ss
P;Pmm:ss.0
P;P@
P;P[h]:mm:ss
P;P_("$"* #,##0_);;_("$"* \(#,##0\);;_("$"* "-"_);;_(@_)
P;P_(* #,##0_);;_(* \(#,##0\);;_(* "-"_);;_(@_)
P;P_("$"* #,##0.00_);;_("$"* \(#,##0.00\);;_("$"* "-"??_);;_(@_)
P;P_(* #,##0.00_);;_(* \(#,##0.00\);;_(* "-"??_);;_(@_)
P;FCalibri;M220;L9
P;FCalibri;M220;L9
P;FCalibri;M220;L9
P;FCalibri;M220;L9
P;ECalibri;M220;L9
P;ECalibri Light;M360;L55
P;ECalibri;M300;SB;L55
P;ECalibri;M260;SB;L55
P;ECalibri;M220;SB;L55
P;ECalibri;M220;L18
P;ECalibri;M220;L21
P;ECalibri;M220;L61
P;ECalibri;M220;L63
P;ECalibri;M220;SB;L64
P;ECalibri;M220;SB;L53
P;ECalibri;M220;L53
P;ECalibri;M220;SB;L10
P;ECalibri;M220;L11
P;ECalibri;M220;SI;L24
P;ECalibri;M220;SB;L9
P;ECalibri;M220;L10
P;ESegoe UI;M200;L9
F;P0;DG0G8;M320
B;Y6;X5;D0 0 5 4
O;D;V0;K47;G100 0.001
F;W1 16384 10
C;Y1;X1;K1
C;X2;K10
C;X3;K100
C;X4;K101
C;X5;K102
C;Y2;X1;K2;ER[-1]C+1
C;X2;K11;ER[-1]C+1
C;X3;K101;S;R2;C1
C;X4;K102;S;R2;C1
C;X5;K103;S;R2;C1
C;Y3;X1;K3;S;R2;C1
C;X2;K12;ER[-1]C+1
C;X3;K102;S;R2;C1
C;X4;K103;S;R2;C1
C;X5;K104;S;R2;C1
C;Y4;X1;K4;S;R2;C1
C;X2;K13;ER[-1]C+1
C;X3;K103;S;R2;C1
C;X4;K104;S;R2;C1
C;X5;K105;S;R2;C1
C;Y5;X1;K5;S;R2;C1
C;X2;K14;ER[-1]C+1
C;X3;K104;S;R2;C1
C;X4;K105;S;R2;C1
C;X5;K106;S;R2;C1
E

0 comments on commit 64a1d1a

Please sign in to comment.