sc/inc/compiler.hxx | 3 ++ sc/source/core/tool/compiler.cxx | 50 ++++++++++++++++++++++++++++++++------ sc/source/filter/excel/xename.cxx | 43 +++++++++++++++++--------------- 3 files changed, 69 insertions(+), 27 deletions(-)
New commits: commit 0a3ee4978e90a68d1e22c36d4e41f5208b854389 Author: Karthik Godha <[email protected]> AuthorDate: Thu Jan 22 18:14:44 2026 +0530 Commit: Michael Stahl <[email protected]> CommitDate: Tue Jan 27 14:14:03 2026 +0100 sc: XLSX - Sanitize Defined Name Sanitize defined name in XLSX export, it can't have any special characters ex:- ('[',']','.',' '). Also it can't contain any valid cell references bug-document: forum-mso-de-35646.xls Change-Id: Id64cd529c10801f182afa6cda165edf9e7d4ab30 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197817 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Michael Stahl <[email protected]> diff --git a/sc/inc/compiler.hxx b/sc/inc/compiler.hxx index 1af31899280e..95ac76286f0f 100644 --- a/sc/inc/compiler.hxx +++ b/sc/inc/compiler.hxx @@ -523,6 +523,9 @@ public: const ScAddress& rFormulaPos); bool HasUnhandledPossibleImplicitIntersections() const { return !mUnhandledPossibleImplicitIntersections.empty(); } + + SC_DLLPUBLIC static OUString SanitizeDefinedName(const OUString& rStr, const ScDocument& rDoc); + #ifdef DBG_UTIL const std::set<OpCode>& UnhandledPossibleImplicitIntersectionsOpCodes() { return mUnhandledPossibleImplicitIntersectionsOpCodes; } #endif diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx index c209b59c3549..b0a48d9ba4c4 100644 --- a/sc/source/core/tool/compiler.cxx +++ b/sc/source/core/tool/compiler.cxx @@ -5364,6 +5364,48 @@ bool ScCompiler::IsCharFlagAllConventions( return ScGlobal::getCharClass().isLetterNumeric( rStr, nPos ); } +OUString ScCompiler::SanitizeDefinedName(const OUString& rStr, const ScDocument& rDoc) +{ + OUStringBuffer aBuffer; + bool bValidName = true; + + for (sal_Int32 i = 0; i < rStr.getLength(); i++) + { + sal_Unicode c = rStr[i]; + + // Left/Right Quotations are allowed + bool bQuotations = (c == 0x2018 || c == 0x2019 || c == 0x201C || c == 0x201D); + + if (ScCompiler::IsCharFlagAllConventions(rStr, i, ScCharFlags::Name) || bQuotations) + { + aBuffer.append(rStr[i]); + + if (!i && !ScCompiler::IsCharFlagAllConventions(rStr, i, ScCharFlags::CharName) + && !bQuotations) + bValidName = false; + } + else + aBuffer.append('_'); + } + + OUString sName = aBuffer.makeStringAndClear(); + + // Name can't be a valid cell reference + if ((ScAddress().Parse(sName, rDoc, ::formula::FormulaGrammar::CONV_XL_A1) != ScRefFlags::ZERO) + || (ScRange().Parse(sName, rDoc, ::formula::FormulaGrammar::CONV_XL_R1C1) + != ScRefFlags::ZERO)) + bValidName = false; + + if (!bValidName || sName != rStr) + { + sName = bValidName ? sName : "_" + sName; + SAL_WARN("sc.filter", + "'" << rStr << "' is an invalid name, using '" << sName << "' instead."); + } + + return sName; +} + void ScCompiler::CreateStringFromExternal( OUStringBuffer& rBuffer, const FormulaToken* pTokenP ) const { const FormulaToken* t = pTokenP; @@ -5808,13 +5850,7 @@ void ScCompiler::CreateStringFromIndex( OUStringBuffer& rBuffer, const FormulaTo aBuffer.append("[0]" + OUStringChar(pConv->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR))); } - OUString sName = pData->GetName(); - // If the name is a valid reference then add underscore to the name - if ((ScAddress().Parse(sName, rDoc, ::formula::FormulaGrammar::CONV_XL_A1) - != ScRefFlags::ZERO) - || (ScRange().Parse(sName, rDoc, ::formula::FormulaGrammar::CONV_XL_R1C1) - != ScRefFlags::ZERO)) - sName = "_" + sName; + OUString sName = ScCompiler::SanitizeDefinedName(pData->GetName(), rDoc); aBuffer.append(sName); } } diff --git a/sc/source/filter/excel/xename.cxx b/sc/source/filter/excel/xename.cxx index 9e9ce1bd2a00..6bd1ec43dbfe 100644 --- a/sc/source/filter/excel/xename.cxx +++ b/sc/source/filter/excel/xename.cxx @@ -346,33 +346,36 @@ void XclExpName::SaveXml( XclExpXmlStream& rStrm ) // Sheets where IsExportTab is not true are not exported, so use mnXclTab // (1 based) to get the sheetid as of the exported document's perspective. SCTAB nXlsxTab = mnXclTab - 1; - OUString sName = maOrigName; - if (sName.indexOf(' ') != -1) - { - sName = sName.replace(' ', '_'); - SAL_WARN("sc.filter", - "'" << maOrigName << "' is an invalid name, using '" << sName << "' instead."); - } - else - { - // If the name is a valid reference then add underscore to the name - if ((ScAddress().Parse(sName, GetDoc(), ::formula::FormulaGrammar::CONV_XL_A1) - != ScRefFlags::ZERO) - || (ScRange().Parse(sName, GetDoc(), ::formula::FormulaGrammar::CONV_XL_R1C1) - != ScRefFlags::ZERO)) - sName = "_" + sName; - } + OUString sName = ScCompiler::SanitizeDefinedName(maOrigName, GetDoc()); // Regenerate symbol for external references if (ScTokenArray* pScTokArr = GetScTokenArray()) { formula::FormulaTokenArrayPlainIterator aIter(*pScTokArr); formula::FormulaToken* t = aIter.First(); - while (t && !t->IsExternalRef()) - t = aIter.Next(); + while (t) + { + if (t->GetType() == formula::svExternal) + { + formula::FormulaToken* pNext = aIter.PeekNext(); - if (t) - msSymbol = XclXmlUtils::ToOUString(GetCompileFormulaContext(), GetPos(), pScTokArr); + /* Excel import generates svExternal tokens for invalid names and + for external/invalid function calls. For undefined names, new defined + name is created (XclExpFmlaCompImpl::ProcessExternal), this defined + name should follow OOXML conventions */ + if (!pNext || (pNext->GetOpCode() != ocOpen)) + { + msSymbol = ScCompiler::SanitizeDefinedName(t->GetExternal(), GetDoc()); + break; + } + } + else if (t->IsExternalRef()) + { + msSymbol = XclXmlUtils::ToOUString(GetCompileFormulaContext(), GetPos(), pScTokArr); + break; + } + t = aIter.Next(); + } } rWorkbook->startElement( XML_definedName,
