Skip to content

Commit

Permalink
Merge pull request #4271 from oleibman/issue4269
Browse files Browse the repository at this point in the history
Add forceFullCalc Option to Xlsx Writer
  • Loading branch information
oleibman authored Dec 23, 2024
2 parents 08e2260 + 22c4956 commit af07ad1
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).

### Fixed

- Add forceFullCalc option to Xlsx Writer. [Issue #4269](https://github.com/PHPOffice/PhpSpreadsheet/issues/4269) [PR #4271](https://github.com/PHPOffice/PhpSpreadsheet/pull/4271)
- More context options may be needed for http(s) image. [Php issue 17121](https://github.com/php/php-src/issues/17121) [PR #4276](https://github.com/PHPOffice/PhpSpreadsheet/pull/4276)
- Several fixed to ODS Writer. [Issue #4261](https://github.com/PHPOffice/PhpSpreadsheet/issues/4261) [PR #4263](https://github.com/PHPOffice/PhpSpreadsheet/pull/4263) [PR #4264](https://github.com/PHPOffice/PhpSpreadsheet/pull/4264) [PR #4266](https://github.com/PHPOffice/PhpSpreadsheet/pull/4266)

Expand Down
6 changes: 6 additions & 0 deletions docs/topics/reading-and-writing-to-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@ $writer->save("05featuredemo.xlsx");
**Note** Formulas will still be calculated in any column set to be autosized
even if pre-calculated is set to false

**Note** Prior to release 3.7.0, the use of this feature will cause Excel to be used in a mode where opening a sheet saved in this manner *might* not automatically recalculate a cell's formula when a cell used it the formula changes. Furthermore, that behavior might be applied to all spreadsheets open at the time. To avoid this behavior, add the following statement after `setPreCalculateFormulas` above:
```php
$writer->setForceFullCalc(false);
```
In a future release, the property's default may change to `false` and that statement may no longer be required.

#### Office 2003 compatibility pack

Because of a bug in the Office2003 compatibility pack, there can be some
Expand Down
20 changes: 19 additions & 1 deletion src/PhpSpreadsheet/Writer/Xlsx.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ class Xlsx extends BaseWriter

private bool $useDynamicArray = false;

private ?bool $forceFullCalc = null;

/**
* Create a new Xlsx Writer.
*/
Expand Down Expand Up @@ -342,7 +344,7 @@ public function save($filename, int $flags = 0): void
$zipContent['xl/styles.xml'] = $this->getWriterPartStyle()->writeStyles($this->spreadSheet);

// Add workbook to ZIP file
$zipContent['xl/workbook.xml'] = $this->getWriterPartWorkbook()->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas);
$zipContent['xl/workbook.xml'] = $this->getWriterPartWorkbook()->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas, $this->forceFullCalc);

$chartCount = 0;
// Add worksheets
Expand Down Expand Up @@ -747,4 +749,20 @@ private function determineUseDynamicArrays(): void
{
$this->useDynamicArray = $this->preCalculateFormulas && Calculation::getInstance($this->spreadSheet)->getInstanceArrayReturnType() === Calculation::RETURN_ARRAY_AS_ARRAY && !$this->useCSEArrays;
}

/**
* If this is set when a spreadsheet is opened,
* values may not be automatically re-calculated,
* and a button will be available to force re-calculation.
* This may apply to all spreadsheets open at that time.
* If null, this will be set to the opposite of $preCalculateFormulas.
* It is likely that false is the desired setting, although
* cases have been reported where true is required (issue #456).
*/
public function setForceFullCalc(?bool $forceFullCalc): self
{
$this->forceFullCalc = $forceFullCalc;

return $this;
}
}
13 changes: 9 additions & 4 deletions src/PhpSpreadsheet/Writer/Xlsx/Workbook.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ class Workbook extends WriterPart
* Write workbook to XML format.
*
* @param bool $preCalculateFormulas If true, formulas will be calculated before writing
* @param ?bool $forceFullCalc If null, !$preCalculateFormulas
*
* @return string XML Output
*/
public function writeWorkbook(Spreadsheet $spreadsheet, bool $preCalculateFormulas = false): string
public function writeWorkbook(Spreadsheet $spreadsheet, bool $preCalculateFormulas = false, ?bool $forceFullCalc = null): string
{
// Create XML writer
if ($this->getParentWriter()->getUseDiskCaching()) {
Expand Down Expand Up @@ -57,7 +58,7 @@ public function writeWorkbook(Spreadsheet $spreadsheet, bool $preCalculateFormul
(new DefinedNamesWriter($objWriter, $spreadsheet))->write();

// calcPr
$this->writeCalcPr($objWriter, $preCalculateFormulas);
$this->writeCalcPr($objWriter, $preCalculateFormulas, $forceFullCalc);

$objWriter->endElement();

Expand Down Expand Up @@ -148,7 +149,7 @@ private function writeWorkbookProtection(XMLWriter $objWriter, Spreadsheet $spre
*
* @param bool $preCalculateFormulas If true, formulas will be calculated before writing
*/
private function writeCalcPr(XMLWriter $objWriter, bool $preCalculateFormulas = true): void
private function writeCalcPr(XMLWriter $objWriter, bool $preCalculateFormulas, ?bool $forceFullCalc): void
{
$objWriter->startElement('calcPr');

Expand All @@ -160,7 +161,11 @@ private function writeCalcPr(XMLWriter $objWriter, bool $preCalculateFormulas =
// fullCalcOnLoad isn't needed if we will calculate before writing
$objWriter->writeAttribute('calcCompleted', ($preCalculateFormulas) ? '1' : '0');
$objWriter->writeAttribute('fullCalcOnLoad', ($preCalculateFormulas) ? '0' : '1');
$objWriter->writeAttribute('forceFullCalc', ($preCalculateFormulas) ? '0' : '1');
if ($forceFullCalc === null) {
$objWriter->writeAttribute('forceFullCalc', $preCalculateFormulas ? '0' : '1');
} else {
$objWriter->writeAttribute('forceFullCalc', $forceFullCalc ? '1' : '0');
}

$objWriter->endElement();
}
Expand Down
65 changes: 65 additions & 0 deletions tests/PhpSpreadsheetTests/Writer/Xlsx/Issue4269Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;

use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

class Issue4269Test extends TestCase
{
private string $outputFile = '';

protected function tearDown(): void
{
if ($this->outputFile !== '') {
unlink($this->outputFile);
$this->outputFile = '';
}
}

#[DataProvider('validationProvider')]
public function testWriteArrayFormulaTextJoin(
bool $preCalculateFormulas,
?bool $forceFullCalc,
string $expected
): void {
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValue('A1', '=A2*2');
$sheet->setCellValue('A2', 0);

$writer = new XlsxWriter($spreadsheet);
$writer->setPreCalculateFormulas($preCalculateFormulas);
if ($forceFullCalc !== null) {
$writer->setForceFullCalc($forceFullCalc);
}
$this->outputFile = File::temporaryFilename();
$writer->save($this->outputFile);
$spreadsheet->disconnectWorksheets();

$file = 'zip://';
$file .= $this->outputFile;
$file .= '#xl/workbook.xml';
$data = file_get_contents($file);
if ($data === false) {
self::fail('Unable to read file');
} else {
self::assertStringContainsString($expected, $data);
}
}

public static function validationProvider(): array
{
return [
'normal case' => [true, null, 'calcMode="auto" calcCompleted="1" fullCalcOnLoad="0" forceFullCalc="0"'],
'issue 456' => [false, null, 'calcMode="auto" calcCompleted="0" fullCalcOnLoad="1" forceFullCalc="1"'],
'better choice for no precalc' => [false, false, 'calcMode="auto" calcCompleted="0" fullCalcOnLoad="1" forceFullCalc="0"'],
'unlikely use case' => [true, true, 'calcMode="auto" calcCompleted="1" fullCalcOnLoad="0" forceFullCalc="1"'],
];
}
}

0 comments on commit af07ad1

Please sign in to comment.