sc: round-trip dynamic-array masters through XLSX, strip @ on XLS

XLSX marks a dynamic-array master with cm="1" on the cell and a
matching dynamicArrayProperties metadata block. Both import and
export now travel through mbDynamicArrayMaster on the cell.

XLSX import plumbs the flag through the three formula paths.
XML_normal and XML_shared land in applyCellFormulas and
applySharedFormulas through setCellFormula. XML_array lands in
applyArrayFormulas through setCellArrayFormula. Each path sets
mbDynamicArrayMaster on the imported cell or on the matrix master.

XLSX export reads the flag in XclExpFormulaCell::SaveXml to drive
cm="1" and the dynamicArrayProperties block. The legacy pair of a
Ctrl+Shift+Enter (CSE) master that calls a dynamic-array function
stays as a fallback while the importers and the UI entry path are
being wired through.

XLS has no @ encoding and no dynamic-array marker. Save and load
mirror the ODF behaviour:
- The exporter strips ocSingleValue (alongside ocSpaces and
  ocWhitespace) in GetNextToken so the BIFF stream stays valid.
  The saved formula evaluates to the upper-left value the way XLS
  always did.
- The importer walks the just-built RPN of every plain non-array
  formula and prepends @ when the formula intends an array. The
  resolve runs before the formula cell is constructed, while the
  parse array is still mutable. The static helper compiles a
  missing RPN on demand so the array-intent walk has the post-fix
  order it needs.
- XLS array records keep the legacy CSE behaviour. They have no
  dynamic-array encoding, so loading them as static arrays matches
  what the file format can express.

Tests cover an XLSX import of Spill.xlsx, an XLSX round trip of the
same file, a direct-XML XLSX export check that the spilled master
at C2 keeps cm="1" and the dynamicArrayProperties metadata block,
and an XLS round trip of =@TRANSPOSE(B1:B4) where the @ reappears
after reload.

Signed-off-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk>
Change-Id: I30318bad4cdb50bbd92240fd71d70df0959a0eda
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/4664
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com>
(cherry picked from commit 79eda1a177abf9a348dbc67c211b26cabd0cb4c1)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/207218
Tested-by: Xisco Fauli <xiscofauli@libreoffice.org>
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
Tomaž Vajngerl
2026-07-08 18:00:00 +02:00
committed by Xisco Fauli
parent bfb49b1038
commit eb239f1a15
14 changed files with 232 additions and 42 deletions
+7
View File
@@ -361,6 +361,13 @@ public:
bool IsDynamicArrayMaster() const { return mbDynamicArrayMaster; }
SC_DLLPUBLIC void SetDynamicArrayMaster( bool bDynamic );
SC_DLLPUBLIC void ResolveImplicitIntersection();
// Run this on the bare token array before it is wrapped in a cell.
// The cell constructor finalizes the parse array. Once finalized,
// appending a new token silently no-ops. The document and position
// arguments compile a missing RPN so the array-intent walk has the
// post-fix order it needs.
SC_DLLPUBLIC static void ResolveImplicitIntersection(ScTokenArray& rCode, ScDocument& rDoc,
const ScAddress& rPos);
ScTokenArray* GetCode() { return pCode;}
const ScTokenArray* GetCode() const { return pCode;}
+30
View File
@@ -1872,6 +1872,36 @@ CPPUNIT_TEST_FIXTURE(ScExportTest2, testArrayFormulaSpillRoundtripXLSX)
CPPUNIT_ASSERT_EQUAL(40.0, pDocument->GetValue(ScAddress(0, 3, 0)));
}
CPPUNIT_TEST_FIXTURE(ScExportTest2, testDynamicArraySpilledExportXLSX)
{
// Spill.xlsx has C2 = UNIQUE(A2:A5) imported as a dynamic-array
// master that already spilled into C2:C5. Saving back to XLSX must
// write cm="1" on the master cell, t="array" with ref="C2:C5" on
// the formula, and the matching dynamic-array metadata block.
// Slave cells in the spilled range must not carry cm.
createScDoc("xlsx/Spill.xlsx");
save(TestFilter::XLSX);
xmlDocUniquePtr pSheet = parseExport(u"xl/worksheets/sheet1.xml"_ustr);
CPPUNIT_ASSERT(pSheet);
assertXPath(pSheet, "/x:worksheet/x:sheetData/x:row[2]/x:c[@r='C2']", "cm", u"1");
assertXPath(pSheet, "/x:worksheet/x:sheetData/x:row[2]/x:c[@r='C2']/x:f", "t", u"array");
assertXPath(pSheet, "/x:worksheet/x:sheetData/x:row[2]/x:c[@r='C2']/x:f", "ref", u"C2:C5");
assertXPathNoAttribute(pSheet, "/x:worksheet/x:sheetData/x:row[3]/x:c[@r='C3']", "cm");
assertXPathNoAttribute(pSheet, "/x:worksheet/x:sheetData/x:row[4]/x:c[@r='C4']", "cm");
assertXPathNoAttribute(pSheet, "/x:worksheet/x:sheetData/x:row[5]/x:c[@r='C5']", "cm");
xmlDocUniquePtr pMetadata = parseExport(u"xl/metadata.xml"_ustr);
CPPUNIT_ASSERT_MESSAGE("xl/metadata.xml must be written", pMetadata);
assertXPath(pMetadata,
"/x:metadata/x:futureMetadata[@name='XLDAPR']/x:bk/x:extLst/x:ext"
"/xda:dynamicArrayProperties",
"fDynamic", u"1");
}
CPPUNIT_TEST_FIXTURE(ScExportTest2, testArrayFormulaSpillRoundtripODS)
{
// Same scenario as testArrayFormulaSpillRoundtripXLSX but for ODS.
+29
View File
@@ -12,6 +12,7 @@
#include <docsh.hxx>
#include <scitems.hxx>
#include <attrib.hxx>
#include <formulacell.hxx>
#include <stlpool.hxx>
#include <validat.hxx>
#include <scresid.hxx>
@@ -19,6 +20,7 @@
#include <subtotalparam.hxx>
#include <globstr.hrc>
#include <dpobject.hxx>
#include <formula/errorcodes.hxx>
#include <comphelper/processfactory.hxx>
#include <editeng/wghtitem.hxx>
@@ -155,6 +157,33 @@ CPPUNIT_TEST_FIXTURE(ScExportTest4, testTdf120177)
CPPUNIT_ASSERT_EQUAL(sGroupName1, sGroupName2);
}
CPPUNIT_TEST_FIXTURE(ScExportTest4, testSingleValueDroppedOnXlsExport)
{
// The @ implicit-intersection marker survives an XLS round trip
// even though the BIFF stream has no token for it.
createScDoc();
ScDocument* pDoc = getScDoc();
pDoc->SetValue(ScAddress(1, 0, 0), 10.0);
pDoc->SetValue(ScAddress(1, 1, 0), 20.0);
pDoc->SetValue(ScAddress(1, 2, 0), 30.0);
pDoc->SetValue(ScAddress(1, 3, 0), 40.0);
pDoc->SetFormula(ScAddress(0, 0, 0), u"=@TRANSPOSE(B1:B4)"_ustr,
formula::FormulaGrammar::GRAM_NATIVE);
CPPUNIT_ASSERT_EQUAL(10.0, pDoc->GetValue(ScAddress(0, 0, 0)));
saveAndReload(TestFilter::XLS);
pDoc = getScDoc();
ScFormulaCell* pCell = pDoc->GetFormulaCell(ScAddress(0, 0, 0));
CPPUNIT_ASSERT(pCell);
CPPUNIT_ASSERT_EQUAL(sal_Int32(FormulaError::NONE), sal_Int32(pCell->GetErrCode()));
CPPUNIT_ASSERT_EQUAL(u"=@TRANSPOSE(B1:B4)"_ustr, pDoc->GetFormula(0, 0, 0));
CPPUNIT_ASSERT_EQUAL(10.0, pDoc->GetValue(ScAddress(0, 0, 0)));
}
CPPUNIT_TEST_FIXTURE(ScExportTest4, testTdf85553)
{
createScDoc("ods/tdf85553.ods");
+33
View File
@@ -1648,6 +1648,39 @@ CPPUNIT_TEST_FIXTURE(ScFiltersTest, testNonAsciiWithDotXLSX)
CPPUNIT_ASSERT_EQUAL(5.0, aValue);
}
CPPUNIT_TEST_FIXTURE(ScFiltersTest, testCmImportSetsDynamicArrayMaster)
{
// The cm="1" attribute on an XLSX cell lands as a dynamic-array master.
createScDoc("xlsx/Spill.xlsx");
ScDocument* pDoc = getScDoc();
// C2 carries cm="1" with t="array" ref="C2:C5", the spilled master.
ScFormulaCell* pSpilled = pDoc->GetFormulaCell(ScAddress(2, 1, 0));
CPPUNIT_ASSERT(pSpilled);
CPPUNIT_ASSERT(pSpilled->IsDynamicArrayMaster());
// A2 is a plain value cell, so the document holds no formula at A2.
ScFormulaCell* pValueCell = pDoc->GetFormulaCell(ScAddress(0, 1, 0));
CPPUNIT_ASSERT(!pValueCell);
}
CPPUNIT_TEST_FIXTURE(ScFiltersTest, testCmRoundTripXLSX)
{
// The dynamic-array marker survives an XLSX save and reload through
// the cm="1" attribute on the master cell.
createScDoc("xlsx/Spill.xlsx");
saveAndReload(TestFilter::XLSX);
ScDocument* pDoc = getScDoc();
ScFormulaCell* pMaster = pDoc->GetFormulaCell(ScAddress(2, 1, 0));
CPPUNIT_ASSERT(pMaster);
CPPUNIT_ASSERT(pMaster->IsDynamicArrayMaster());
}
CPPUNIT_TEST_FIXTURE(ScFiltersTest, testArrayFormulaSpillXLSX)
{
// D2 holds =UNIQUE(A2:A5) imported in #SPILL! state with blocker text
+20 -8
View File
@@ -2675,17 +2675,29 @@ void ScFormulaCell::SetDynamicArrayMaster(bool bDynamic)
}
}
void ScFormulaCell::ResolveImplicitIntersection(ScTokenArray& rCode, ScDocument& rDoc,
const ScAddress& rPos)
{
if (rCode.IsHyperLink() || rCode.GetLen() == 0)
return;
if (rCode.GetCodeLen() == 0)
{
// Parse has tokens but RPN was not built yet. Build it so the
// walk has post-fix order to inspect.
ScCompiler aComp(rDoc, rPos, rCode, formula::FormulaGrammar::GRAM_DEFAULT, true, false);
aComp.CompileTokenArray();
}
if (rpnTopIsImplicitIntersection(rCode))
return;
if (rpnIntendsArrayResult(rCode))
prependImplicitIntersection(rCode);
}
void ScFormulaCell::ResolveImplicitIntersection()
{
if (!pCode
|| cMatrixFlag != ScMatrixMode::NONE
|| pCode->IsHyperLink() || mxGroup
|| pCode->GetCodeLen() == 0)
if (!pCode || cMatrixFlag != ScMatrixMode::NONE || mxGroup)
return;
if (rpnTopIsImplicitIntersection(*pCode))
return;
if (rpnIntendsArrayResult(*pCode))
prependImplicitIntersection(*pCode);
ResolveImplicitIntersection(*pCode, rDocument, aPos);
}
void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows )
+11 -1
View File
@@ -119,7 +119,13 @@ void ImportExcel::Formula(
if (pSharedCode)
{
ScFormulaCell* pCell;
pCell = new ScFormulaCell(rD, aScPos, pSharedCode->Clone());
// XLS has no @ encoding. Bake an implicit @ into the
// RPN when the formula intends an array result so a
// TRANSPOSE-style call returns the upper-left value at
// the cell position.
std::unique_ptr<ScTokenArray> pTokens = pSharedCode->Clone();
ScFormulaCell::ResolveImplicitIntersection(*pTokens, rD, aScPos);
pCell = new ScFormulaCell(rD, aScPos, std::move(pTokens));
pCell->GetCode()->WrapReference(aScPos, EXC_MAXCOL8, EXC_MAXROW8);
rDoc.getDoc().EnsureTable(aScPos.Tab());
rDoc.setFormulaCell(aScPos, pCell);
@@ -147,6 +153,10 @@ void ImportExcel::Formula(
if (pResult)
{
// XLS has no @ encoding. Bake an implicit @ into the RPN when
// the formula intends an array result so a TRANSPOSE-style call
// returns the upper-left value at the cell position.
ScFormulaCell::ResolveImplicitIntersection(*pResult, rDoc.getDoc(), aScPos);
pCell = new ScFormulaCell(rDoc.getDoc(), aScPos, std::move(pResult));
pCell->GetCode()->WrapReference(aScPos, EXC_MAXCOL8, EXC_MAXROW8);
rDoc.getDoc().CheckLinkFormulaNeedingCheck( *pCell->GetCode());
+4
View File
@@ -849,6 +849,10 @@ void ImportExcel::Shrfmla()
ScDocumentImport& rDoc = GetDocImport();
// XLS has no @ encoding. Bake an implicit @ into the RPN when the
// formula intends an array result so a TRANSPOSE-style call returns
// the upper-left value at the cell position.
ScFormulaCell::ResolveImplicitIntersection(*pResult, rD, aPos);
ScFormulaCell* pCell = new ScFormulaCell(rD, aPos, std::move(pResult));
pCell->GetCode()->WrapReference(aPos, EXC_MAXCOL8, EXC_MAXROW8);
rDoc.getDoc().CheckLinkFormulaNeedingCheck( *pCell->GetCode());
+9 -2
View File
@@ -844,9 +844,16 @@ bool XclExpFmlaCompImpl::GetNextToken( XclExpScToken& rTokData )
rTokData.mpScToken = GetNextRawToken();
rTokData.mnSpaces = 0;
/* TODO: handle ocWhitespace characters? */
while (rTokData.GetOpCode() == ocSpaces || rTokData.GetOpCode() == ocWhitespace)
// XLS predates the @ implicit-intersection operator. The BIFF token
// stream has no encoding for ocSingleValue, so drop it on the way out
// and let the operand flow through unchanged. XLS does not auto-spill
// anyway, so the saved formula evaluates to the same upper-left value
// it did before.
while (rTokData.GetOpCode() == ocSpaces || rTokData.GetOpCode() == ocWhitespace
|| rTokData.GetOpCode() == ocSingleValue)
{
rTokData.mnSpaces += static_cast<const FormulaByteToken*>(rTokData.mpScToken)->GetByte();
if (rTokData.GetOpCode() != ocSingleValue)
rTokData.mnSpaces += static_cast<const FormulaByteToken*>(rTokData.mpScToken)->GetByte();
rTokData.mpScToken = GetNextRawToken();
}
return rTokData.Is();
+7 -3
View File
@@ -949,10 +949,14 @@ void XclExpFormulaCell::SaveXml( XclExpXmlStream& rStrm )
// Mark dynamic array masters with cm="1" so it is recognised as a
// dynamic array and reads the matching dynamicArrayProperties out of
// xl/metadata.xml.
// xl/metadata.xml. The cell flag is the primary source. The legacy
// pair of a Ctrl+Shift+Enter (CSE) master that calls a dynamic-array
// function stays as a fallback while the importers and the UI entry
// path are being wired through.
const bool bDynamicArrayMaster
= mrScFmlaCell.GetMatrixFlag() == ScMatrixMode::Formula
&& mrScFmlaCell.GetCode() && mrScFmlaCell.GetCode()->HasDynamicArrayFunction();
= mrScFmlaCell.IsDynamicArrayMaster()
|| (mrScFmlaCell.GetMatrixFlag() == ScMatrixMode::Formula
&& mrScFmlaCell.GetCode() && mrScFmlaCell.GetCode()->HasDynamicArrayFunction());
if (bDynamicArrayMaster)
rStrm.NoteDynamicArrayFormula();
+26 -7
View File
@@ -42,17 +42,28 @@ public:
OUString maCellValue;
sal_Int32 mnSharedId;
sal_Int32 mnValueType;
/// True when the XLSX cell carried cm="1", marking it as a dynamic-array
/// master.
bool mbDynamicArrayMaster = false;
SharedFormulaDesc(
const ScAddress& rAddr, sal_Int32 nSharedId,
OUString aCellValue, sal_Int32 nValueType );
OUString aCellValue, sal_Int32 nValueType,
bool bDynamicArrayMaster = false );
};
struct TokenAddressItem
{
OUString maTokenStr;
ScAddress maAddress;
TokenAddressItem( OUString aTokenStr, const ScAddress& rAddress ) : maTokenStr(std::move( aTokenStr )), maAddress( rAddress ) {}
/// True when the XLSX cell carried cm="1", marking it as a dynamic-array
/// master.
bool mbDynamicArrayMaster = false;
TokenAddressItem( OUString aTokenStr, const ScAddress& rAddress,
bool bDynamicArrayMaster = false )
: maTokenStr(std::move( aTokenStr ))
, maAddress( rAddress )
, mbDynamicArrayMaster(bDynamicArrayMaster) {}
};
struct TokenRangeAddressItem
@@ -64,12 +75,17 @@ public:
/// blocker preservation so reference cells aren't materialised over
/// real user data sitting in the matrix range.
bool mbCachedSpill;
/// True when the XLSX cell carried cm="1", marking it as a dynamic-array
/// master.
bool mbDynamicArrayMaster = false;
TokenRangeAddressItem(TokenAddressItem aTokenAndAddress, const ScRange& rRange,
bool bCachedSpill = false)
bool bCachedSpill = false,
bool bDynamicArrayMaster = false)
: maTokenAndAddress(std::move(aTokenAndAddress))
, maRange(rRange)
, mbCachedSpill(bCachedSpill)
, mbCachedSpill(bCachedSpill)
, mbDynamicArrayMaster(bDynamicArrayMaster)
{}
};
@@ -108,11 +124,13 @@ private:
public:
explicit FormulaBuffer( const WorkbookHelper& rHelper );
void finalizeImport();
void setCellFormula( const ScAddress& rAddress, const OUString& );
void setCellFormula( const ScAddress& rAddress, const OUString&,
bool bDynamicArrayMaster = false );
void setCellFormula(
const ScAddress& rAddress, sal_Int32 nSharedId,
const OUString& rCellValue, sal_Int32 nValueType );
const OUString& rCellValue, sal_Int32 nValueType,
bool bDynamicArrayMaster = false );
void setCellFormulaValue(
const ScAddress& rAddress, const OUString& rValueStr, sal_Int32 nCellType );
@@ -120,7 +138,8 @@ public:
void setCellArrayFormula(const ScRange& rRangeAddress,
const ScAddress& rTokenAddress,
const OUString&,
bool bCachedSpill = false);
bool bCachedSpill = false,
bool bDynamicArrayMaster = false);
void createSharedFormulaMapEntry( const ScAddress& rAddress,
sal_Int32 nSharedId, const OUString& rTokens );
+7 -3
View File
@@ -284,13 +284,17 @@ public:
/** Finalizes the import of named sheet views, by creating the sheet views from the data. */
void finalizeNamedSheetViews();
void setCellFormula( const ScAddress& rTokenAddress, const OUString& );
void setCellFormula( const ScAddress& rTokenAddress, const OUString&,
bool bDynamicArrayMaster = false );
void setCellFormula(
const ScAddress& rAddr, sal_Int32 nSharedId,
const OUString& rCellValue, sal_Int32 nValueType );
const OUString& rCellValue, sal_Int32 nValueType,
bool bDynamicArrayMaster = false );
void setCellArrayFormula(const ScRange& rRangeAddress, const ScAddress& rTokenAddress, const OUString& rTokenStr, bool bCachedSpill = false);
void setCellArrayFormula(const ScRange& rRangeAddress, const ScAddress& rTokenAddress,
const OUString& rTokenStr, bool bCachedSpill = false,
bool bDynamicArrayMaster = false);
void createSharedFormulaMapEntry(
const ScAddress& rAddress,
+33 -9
View File
@@ -154,6 +154,9 @@ void applySharedFormulas(
else
pCell = new ScFormulaCell(rDoc.getDoc(), aPos, *pArray);
if (rDesc.mbDynamicArrayMaster)
pCell->SetDynamicArrayMaster(true);
rDoc.setFormulaCell(aPos, pCell);
const bool bNeedNumberFormat = ((rDoc.getDoc().GetNumberFormat(
aPos.Col(), aPos.Row(), aPos.Tab()) % SV_COUNTRY_LANGUAGE_OFFSET) == 0);
@@ -263,6 +266,9 @@ void applyCellFormulas(
else
pCell = new ScFormulaCell(rDoc.getDoc(), aPos, p->mpCell->GetCode()->Clone());
if (rItem.mbDynamicArrayMaster)
pCell->SetDynamicArrayMaster(true);
rDoc.setFormulaCell(aPos, pCell);
if (rDoc.getDoc().GetNumberFormat(aPos.Col(), aPos.Row(), aPos.Tab()) % SV_COUNTRY_LANGUAGE_OFFSET == 0)
pCell->SetNeedNumberFormat(true);
@@ -282,6 +288,8 @@ void applyCellFormulas(
aCompiler.CompileTokenArray(); // Generate RPN tokens.
ScFormulaCell* pCell = new ScFormulaCell(rDoc.getDoc(), aPos, std::move(pCode));
if (rItem.mbDynamicArrayMaster)
pCell->SetDynamicArrayMaster(true);
rDoc.setFormulaCell(aPos, pCell);
if (rDoc.getDoc().GetNumberFormat(aPos.Col(), aPos.Row(), aPos.Tab()) % SV_COUNTRY_LANGUAGE_OFFSET == 0)
pCell->SetNeedNumberFormat(true);
@@ -306,6 +314,12 @@ void applyArrayFormulas(
rDoc.setMatrixCells(rAddressItem.maRange, *pArray,
formula::FormulaGrammar::GRAM_OOXML,
rAddressItem.mbCachedSpill);
if (rAddressItem.mbDynamicArrayMaster)
{
ScFormulaCell* pMaster = rDoc.getDoc().GetFormulaCell(aPos);
if (pMaster)
pMaster->SetDynamicArrayMaster(true);
}
}
}
}
@@ -422,9 +436,14 @@ FormulaBuffer::SharedFormulaEntry::SharedFormulaEntry(
maAddress(rAddr), maTokenStr(std::move(aTokenStr)), mnSharedId(nSharedId) {}
FormulaBuffer::SharedFormulaDesc::SharedFormulaDesc(
const ScAddress& rAddr, sal_Int32 nSharedId,
OUString aCellValue, sal_Int32 nValueType ) :
maAddress(rAddr), maCellValue(std::move(aCellValue)), mnSharedId(nSharedId), mnValueType(nValueType) {}
const ScAddress& rAddr, sal_Int32 nSharedId,
OUString aCellValue, sal_Int32 nValueType, bool bDynamicArrayMaster)
: maAddress(rAddr)
, maCellValue(std::move(aCellValue))
, mnSharedId(nSharedId)
, mnValueType(nValueType)
, mbDynamicArrayMaster(bDynamicArrayMaster)
{}
FormulaBuffer::SheetItem::SheetItem() :
mpCellFormulas(nullptr),
@@ -513,25 +532,30 @@ void FormulaBuffer::createSharedFormulaMapEntry(
rSharedFormulas.push_back( aEntry );
}
void FormulaBuffer::setCellFormula( const ScAddress& rAddress, const OUString& rTokenStr )
void FormulaBuffer::setCellFormula(const ScAddress& rAddress, const OUString& rTokenStr,
bool bDynamicArrayMaster)
{
assert( rAddress.Tab() >= 0 && o3tl::make_unsigned(rAddress.Tab()) < maCellFormulas.size() );
maCellFormulas[ rAddress.Tab() ].emplace_back( rTokenStr, rAddress );
maCellFormulas[rAddress.Tab()].emplace_back(rTokenStr, rAddress, bDynamicArrayMaster);
}
void FormulaBuffer::setCellFormula(
const ScAddress& rAddress, sal_Int32 nSharedId, const OUString& rCellValue, sal_Int32 nValueType )
const ScAddress& rAddress, sal_Int32 nSharedId, const OUString& rCellValue, sal_Int32 nValueType,
bool bDynamicArrayMaster)
{
assert( rAddress.Tab() >= 0 && o3tl::make_unsigned(rAddress.Tab()) < maSharedFormulaIds.size() );
maSharedFormulaIds[rAddress.Tab()].emplace_back(rAddress, nSharedId, rCellValue, nValueType);
maSharedFormulaIds[rAddress.Tab()].emplace_back(rAddress, nSharedId, rCellValue, nValueType,
bDynamicArrayMaster);
}
void FormulaBuffer::setCellArrayFormula( const ScRange& rRangeAddress, const ScAddress& rTokenAddress, const OUString& rTokenStr, bool bCachedSpill)
void FormulaBuffer::setCellArrayFormula(const ScRange& rRangeAddress, const ScAddress& rTokenAddress,
const OUString& rTokenStr, bool bCachedSpill,
bool bDynamicArrayMaster)
{
TokenAddressItem tokenPair( rTokenStr, rTokenAddress );
assert( rRangeAddress.aStart.Tab() >= 0 && o3tl::make_unsigned(rRangeAddress.aStart.Tab()) < maCellArrayFormulas.size() );
maCellArrayFormulas[rRangeAddress.aStart.Tab()].emplace_back(tokenPair, rRangeAddress, bCachedSpill);
maCellArrayFormulas[rRangeAddress.aStart.Tab()].emplace_back(tokenPair, rRangeAddress, bCachedSpill, bDynamicArrayMaster);
}
void FormulaBuffer::setCellFormulaValue(
+5 -3
View File
@@ -143,7 +143,7 @@ void SheetDataContext::onEndElement()
// master will create the matrix cells via setMatrixCells.
if (!maFormulaStr.isEmpty() || mbCalcAlways)
{
setCellFormula(maCellData.maCellAddr, maFormulaStr);
setCellFormula(maCellData.maCellAddr, maFormulaStr, mbCellMetadata);
mrSheetData.setCellFormat(maCellData);
// If a number cell has some preloaded value, stick it into the buffer
@@ -161,7 +161,8 @@ void SheetDataContext::onEndElement()
if( mbValidRange && maFmlaData.isValidSharedRef( maCellData.maCellAddr ) )
createSharedFormulaMapEntry(maCellData.maCellAddr, maFmlaData.mnSharedId, maFormulaStr);
setCellFormula(maCellData.maCellAddr, maFmlaData.mnSharedId, maCellValue, maCellData.mnCellType);
setCellFormula(maCellData.maCellAddr, maFmlaData.mnSharedId, maCellValue, maCellData.mnCellType,
mbCellMetadata);
mrSheetData.setCellFormat( maCellData );
}
else
@@ -181,7 +182,8 @@ void SheetDataContext::onEndElement()
= maCellData.mnCellType == XML_e
&& mbCellMetadata
&& maCellValue == u"#VALUE!";
setCellArrayFormula(maFmlaData.maFormulaRef, maCellData.maCellAddr, maFormulaStr, bCachedSpill);
setCellArrayFormula(maFmlaData.maFormulaRef, maCellData.maCellAddr, maFormulaStr,
bCachedSpill, mbCellMetadata);
}
// set cell formatting, but do not set result as cell value
mrSheetData.setBlankCell( maCellData );
+11 -6
View File
@@ -1644,21 +1644,26 @@ void WorksheetHelper::finalizeNamedSheetViews()
mrSheetGlob.finalizeNamedSheetViews();
}
void WorksheetHelper::setCellFormula( const ScAddress& rTokenAddress, const OUString& rTokenStr )
void WorksheetHelper::setCellFormula(const ScAddress& rTokenAddress, const OUString& rTokenStr,
bool bDynamicArrayMaster)
{
getFormulaBuffer().setCellFormula( rTokenAddress, rTokenStr );
getFormulaBuffer().setCellFormula(rTokenAddress, rTokenStr, bDynamicArrayMaster);
}
void WorksheetHelper::setCellFormula(
const ScAddress& rAddr, sal_Int32 nSharedId,
const OUString& rCellValue, sal_Int32 nValueType )
const OUString& rCellValue, sal_Int32 nValueType,
bool bDynamicArrayMaster)
{
getFormulaBuffer().setCellFormula(rAddr, nSharedId, rCellValue, nValueType);
getFormulaBuffer().setCellFormula(rAddr, nSharedId, rCellValue, nValueType, bDynamicArrayMaster);
}
void WorksheetHelper::setCellArrayFormula(const ScRange& rRangeAddress, const ScAddress& rTokenAddress, const OUString& rTokenStr, bool bCachedSpill)
void WorksheetHelper::setCellArrayFormula(const ScRange& rRangeAddress, const ScAddress& rTokenAddress,
const OUString& rTokenStr, bool bCachedSpill,
bool bDynamicArrayMaster)
{
getFormulaBuffer().setCellArrayFormula(rRangeAddress, rTokenAddress, rTokenStr, bCachedSpill);
getFormulaBuffer().setCellArrayFormula(rRangeAddress, rTokenAddress, rTokenStr, bCachedSpill,
bDynamicArrayMaster);
}
void WorksheetHelper::createSharedFormulaMapEntry(