tdf#170565 save empty SUM/MAX/MIN functions with parameters to XLSX

Excel won't allow them with no parameters.

In Calc:
- SUM() is 0,
- MAX()/MIN() gives Err:511.

Save them to XLSX as SUM(0), MAX(#VALUE!) and MIN(#VALUE!).

Change-Id: I0f0d05290b67684df294611bd0612090abf1a672
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198543
Reviewed-by: Aron Budea <aron.budea@collabora.com>
Tested-by: Jenkins
This commit is contained in:
Aron Budea
2026-02-05 07:22:05 +01:00
parent e62b0d16c5
commit 3cbf4957d3
4 changed files with 58 additions and 2 deletions
+33 -2
View File
@@ -937,6 +937,11 @@ FormulaToken* FormulaTokenArray::AddStringXML( const OUString& rStr )
return Add( new FormulaStringOpToken( ocStringXML, svl::SharedString( rStr ) ) ); // string not interned
}
FormulaToken* FormulaTokenArray::AddError( FormulaError nErr )
{
return Add( new FormulaErrorToken( nErr ) );
}
FormulaToken* FormulaTokenArray::AddStringName( const OUString& rStr )
{
return Add( new FormulaStringOpToken( ocStringName, svl::SharedString( rStr ) ) ); // string not interned
@@ -969,7 +974,6 @@ void FormulaTokenArray::AddRecalcMode( ScRecalcMode nBits )
SetCombinedBitsRecalcMode( nBits );
}
bool FormulaTokenArray::HasMatrixDoubleRefOps() const
{
if ( !pRPN || !nRPN )
@@ -1074,6 +1078,9 @@ inline bool MissingConventionOOXML::isRewriteNeeded( OpCode eOp )
switch (eOp)
{
case ocIf:
case ocMax:
case ocMin:
case ocSum:
case ocExternal:
case ocEuroConvert:
@@ -1112,8 +1119,9 @@ class FormulaMissingContext
public:
const FormulaToken* mpFunc;
int mnCurArg;
bool mbEmpty; // no parameters: ()
void Clear() { mpFunc = nullptr; mnCurArg = 0; }
void Clear() { mpFunc = nullptr; mnCurArg = 0; mbEmpty = false; }
inline bool AddDefaultArg( FormulaTokenArray* pNewArr, int nArg, double f ) const;
bool AddMissingExternal( FormulaTokenArray* pNewArr ) const;
bool AddMissing( FormulaTokenArray *pNewArr, const MissingConvention & rConv ) const;
@@ -1195,6 +1203,24 @@ void FormulaMissingContext::AddMoreArgs( FormulaTokenArray *pNewArr, const Missi
}
break;
case ocMax:
case ocMin:
if ( mnCurArg == 0 && mbEmpty )
{
// Excel needs at least one parameter in MAX/MIN functions
// In Calc empty MAX/MIN are Err:511
pNewArr->AddError( FormulaError::NoValue );
}
break;
case ocSum:
if ( mnCurArg == 0 && mbEmpty )
{
// Excel needs at least one parameter in SUM function
pNewArr->AddDouble( 0.0 ); // SUM() = 0
}
break;
case ocEuroConvert:
if ( mnCurArg == 2 )
{
@@ -1477,6 +1503,11 @@ FormulaTokenArray * FormulaTokenArray::RewriteMissing( const MissingConvention &
++nFn; // all following operations on _that_ function
pCtx[ nFn ].mpFunc = aIter.PeekPrevNoSpaces();
pCtx[ nFn ].mnCurArg = 0;
FormulaToken* pNext = aIter.PeekNextNoSpaces();
if (pNext != nullptr && pNext->GetOpCode() == ocClose)
pCtx[ nFn ].mbEmpty = true;
else
pCtx[ nFn ].mbEmpty = false;
if (rConv.isPODF() && pCtx[ nFn ].mpFunc && pCtx[ nFn ].mpFunc->GetOpCode() == ocAddress)
pOcas[ nOcas++ ] = nFn; // entering ADDRESS() if PODF
else if ((rConv.isODFF() || rConv.isOOXML()) && pCtx[ nFn ].mpFunc)
+1
View File
@@ -488,6 +488,7 @@ public:
FormulaToken* AddBad( const OUString& rStr ); /// ocBad with OUString
FormulaToken* AddStringXML( const OUString& rStr ); /// ocStringXML with OUString, temporary during import
FormulaToken* AddStringName( const OUString& rStr ); /// ocStringName with OUString - Lambda functions
FormulaToken* AddError( FormulaError nErr );
virtual FormulaToken* MergeArray( );
Binary file not shown.
+24
View File
@@ -2476,6 +2476,30 @@ CPPUNIT_TEST_FIXTURE(ScExportTest4, testTdf165733_leap_day_BCE)
}
}
CPPUNIT_TEST_FIXTURE(ScExportTest4, testTdf170565_empty_functions)
{
createScDoc("ods/tdf170565_empty_functions.ods");
save(TestFilter::XLSX);
xmlDocUniquePtr pSheet = parseExport(u"xl/worksheets/sheet1.xml"_ustr);
CPPUNIT_ASSERT(pSheet);
// Without the fix these would've been exported as SUM(), MIN(), MAX(), which aren't valid in Excel
assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[1]/x:c[1]/x:f", u"SUM(0)");
assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[2]/x:c[1]/x:f", u"MIN(#VALUE!)");
assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[3]/x:c[1]/x:f", u"MAX(#VALUE!)");
// Further checks to ensure there are no regressions
assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[1]/x:c[2]/x:f", u"SUM(,)");
assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[1]/x:c[3]/x:f", u"SUM(100)");
assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[1]/x:c[4]/x:f", u"SUM(C1:C3)");
assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[2]/x:c[2]/x:f", u"MIN(,)");
assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[2]/x:c[3]/x:f", u"MIN(200)");
assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[2]/x:c[4]/x:f", u"MIN(C1:C3)");
assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[3]/x:c[2]/x:f", u"MAX(,)");
assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[3]/x:c[3]/x:f", u"MAX(-100)");
assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[3]/x:c[4]/x:f", u"MAX(C1:C3)");
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */