Skip to content
This repository has been archived by the owner on May 26, 2022. It is now read-only.

Write formulas to XLSX files. #425

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/Spout/Writer/Common/Helper/CellHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ public static function isNonEmptyString($value)
return (gettype($value) === 'string' && $value !== '');
}

/**
* Returns whether the given value looks like a formula
*
* @param $value
* @return bool whether the given value looks like a formula
*/
public static function isFormulaString($value)
{
return (strpos($value,'=') === 0);
}

/**
* Returns whether the given value is numeric.
* A numeric value is from type "integer" or "double" ("float" is not returned by gettype).
Expand Down
6 changes: 4 additions & 2 deletions src/Spout/Writer/XLSX/Internal/Worksheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,10 @@ private function getCellXML($rowIndex, $cellNumber, $cellValue, $styleId)
$columnIndex = CellHelper::getCellIndexFromColumnIndex($cellNumber);
$cellXML = '<c r="' . $columnIndex . $rowIndex . '"';
$cellXML .= ' s="' . $styleId . '"';

if (CellHelper::isNonEmptyString($cellValue)) {

if (CellHelper::isFormulaString($cellValue)) {
$cellXML .= '><f>'.substr($cellValue,1).'</f><v>0</v></c>'; // seriously, that's it.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect that the <v>0</v> is not going to work across all softwares. Which software did you use to test your changes?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, by setting the value to 0, reading back a spreadsheet with a formula won't work as Spout would return "0" for the cell value. Not ideal...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested on OSX, with excel 15 and numbers.

0 works because excel formulas are recalculated when an excel file is loaded.

You are correct that reading a formula will result in unexpected results, I had hoped the title of the PR was specific enough for this to be clear.

Thanks for coming back to me, glad to have been of any use at all.

Copy link

@SimZal SimZal Jun 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried it on Win10 and LibreOffice and it doesn't seem to work. It displays 0, like adrilo predicted. Using empty "<v></v>" seems to work for me.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stevesweets can you try the empty <v></v>?
There may always be some software that won't work but if the main ones work, we can consider merging this change.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi there, realise i left this a long time - i will try this.

S

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how is the code for .ods?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I try do the change on code for .ods
ODS/Internal/Worksheet.php

        if (CellHelper::isFormulaString($cellValue)) {
            $data .= ' table:formula="'.$cellValue.'" office:value-type="float" office:value="' . 0 . '" calcext:value-type="float">';
            $data .= '<text:p>' . 0 . '</text:p>';
            $data .= '</table:table-cell>';
        }else if (CellHelper::isNonEmptyString($cellValue)) {
            $data .= ' office:value-type="string" calcext:value-type="string">';
            $cellValueLines = explode("\n", $cellValue);
            foreach ($cellValueLines as $cellValueLine) {
                $data .= '<text:p>' . $this->stringsEscaper->escape($cellValueLine) . '</text:p>';
            }
            $data .= '</table:table-cell>';    
        } else if (CellHelper::isBoolean($cellValue)) {...}

too add the same change the CellHelper.php like .xlsx

 public static function isFormulaString($value)
    {
        return (strpos($value,'=') === 0);
    }

but now have this problem
not reconize the form in ods like excel. i neet to open document "make change at formule" and save to func
image
image

this is the code .fods

 <table:table-row table:style-name="ro1">
     <table:table-cell office:value-type="float" office:value="1" calcext:value-type="float">
      <text:p>1</text:p>
     </table:table-cell>
     <table:table-cell office:value-type="float" office:value="9" calcext:value-type="float">
      <text:p>9</text:p>
     </table:table-cell>
     <table:table-cell office:value-type="float" office:value="3" calcext:value-type="float">
      <text:p>3</text:p>
     </table:table-cell>
     <table:table-cell office:value-type="float" office:value="7" calcext:value-type="float">
      <text:p>7</text:p>
     </table:table-cell>
     <table:table-cell table:formula="of:=[.A1]+[.B1]+[.C1]+[.D1]" office:value-type="float" office:value="20" calcext:value-type="float">
      <text:p>20</text:p>
     </table:table-cell>
    </table:table-row>

if any can help me i give thanks

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, you can see that formulas don't work the same:

<table:table-cell table:formula="of:=[.A1]+[.B1]+[.C1]+[.D1]" office:value-type="float" office:value="20" calcext:value-type="float">
      <text:p>20</text:p>

This would indicate a formula: table:formula="of:=[.A1]+[.B1]+[.C1]+[.D1]", so your isFormulaString implementation is incorrect. Setting the value to 0 may not work in ODS. This is a trick in XLSX because softwares do recompute the value when you open the file, but for ODS maybe softwares use the given value directly. In this case, I don't have a good solution for you.

} else if (CellHelper::isNonEmptyString($cellValue)) {
$cellXML .= $this->getCellXMLFragmentForNonEmptyString($cellValue);
} else if (CellHelper::isBoolean($cellValue)) {
$cellXML .= ' t="b"><v>' . intval($cellValue) . '</v></c>';
Expand Down