This is an automated email from the ASF dual-hosted git repository. jimjag pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit 17cfc6b974aad5ef7e249ab7a836e5c2f07b9187 Author: Jim Jagielski <[email protected]> AuthorDate: Tue Nov 3 07:05:46 2026 -0500 Read ODF 1.4 error cells in Calc and save a cell that holds only an error as a string Such a cell was saved as table:formula="of:#NULL!" with a float 0, which reloads as 0. --- main/sc/source/core/data/cell.cxx | 4 ++ main/sc/source/filter/xml/xmlcelli.cxx | 82 ++++++++++++++++++++++++++++++++-- main/sc/source/filter/xml/xmlcelli.hxx | 1 + main/sc/source/filter/xml/xmlexprt.cxx | 6 ++- 4 files changed, 88 insertions(+), 5 deletions(-) diff --git a/main/sc/source/core/data/cell.cxx b/main/sc/source/core/data/cell.cxx index 61dd7ab0c7..9dbf2f05f9 100644 --- a/main/sc/source/core/data/cell.cxx +++ b/main/sc/source/core/data/cell.cxx @@ -1002,6 +1002,10 @@ void ScFormulaCell::CompileXML( ScProgress& rProgress ) return ; } + // error cell read from office:value-type="error": no formula, recompiling would give #NAME? + if ( pCode->GetCodeError() && !pCode->GetLen() ) + return; + ScCompiler aComp( pDocument, aPos, *pCode); aComp.SetGrammar(eTempGrammar); String aFormula, aFormulaNmsp; diff --git a/main/sc/source/filter/xml/xmlcelli.cxx b/main/sc/source/filter/xml/xmlcelli.cxx index dcc8c1da4c..d7b0af5435 100644 --- a/main/sc/source/filter/xml/xmlcelli.cxx +++ b/main/sc/source/filter/xml/xmlcelli.cxx @@ -121,7 +121,8 @@ ScXMLTableRowCellContext::ScXMLTableRowCellContext( ScXMLImport& rImport, bHasTextImport(sal_False), bIsFirstTextImport(sal_False), bSolarMutexLocked(sal_False), - bFormulaTextResult(sal_False) + bFormulaTextResult(sal_False), + bIsError(sal_False) { rXMLImport.SetRemoveLastChar(sal_False); rXMLImport.GetTables().AddColumn(bTempIsCovered); @@ -167,6 +168,7 @@ ScXMLTableRowCellContext::ScXMLTableRowCellContext( ScXMLImport& rImport, break; case XML_TOK_TABLE_ROW_CELL_ATTR_VALUE_TYPE: nCellType = GetScImport().GetCellType(sValue); + bIsError = sValue.equalsAscii("error"); bIsEmpty = sal_False; break; case XML_TOK_TABLE_ROW_CELL_ATTR_VALUE: @@ -309,6 +311,33 @@ void ScXMLTableRowCellContext::SetCursorOnTextImport(const rtl::OUString& rOUTem } } +// Collects the plain text of a text:p in an error cell, including text nested in text:span and the like. +class ScXMLErrorTextContext : public SvXMLImportContext +{ + ScXMLTableRowCellContext* pCellContext; + ScXMLErrorTextContext* pParent; + rtl::OUStringBuffer aText; +public: + ScXMLErrorTextContext( SvXMLImport& rImport, sal_uInt16 nPrfx, const rtl::OUString& rLName, + ScXMLTableRowCellContext* pCell, ScXMLErrorTextContext* pTop ) : + SvXMLImportContext( rImport, nPrfx, rLName ), pCellContext( pCell ), pParent( pTop ) {} + + virtual SvXMLImportContext* CreateChildContext( sal_uInt16 nPrfx, const rtl::OUString& rLName, + const uno::Reference< xml::sax::XAttributeList >& ) + { + return new ScXMLErrorTextContext( GetImport(), nPrfx, rLName, NULL, pParent ? pParent : this ); + } + virtual void Characters( const rtl::OUString& rChars ) + { + (pParent ? pParent->aText : aText).append( rChars ); + } + virtual void EndElement() + { + if (pCellContext) + pCellContext->SetString( aText.makeStringAndClear() ); + } +}; + SvXMLImportContext *ScXMLTableRowCellContext::CreateChildContext( sal_uInt16 nPrefix, const ::rtl::OUString& rLName, const ::com::sun::star::uno::Reference< @@ -325,7 +354,9 @@ SvXMLImportContext *ScXMLTableRowCellContext::CreateChildContext( sal_uInt16 nPr bIsEmpty = sal_False; bTextP = sal_True; com::sun::star::table::CellAddress aCellPos = rXMLImport.GetTables().GetRealCellPos(); - if (((nCellType == util::NumberFormat::TEXT) || bFormulaTextResult) && + if (bIsError && !pOUFormula) + pContext = new ScXMLErrorTextContext(rXMLImport, nPrefix, rLName, this, NULL); + else if (((nCellType == util::NumberFormat::TEXT) || bFormulaTextResult) && !rXMLImport.GetTables().IsPartOfMatrix(aCellPos.Column, aCellPos.Row)) { if (!bHasTextImport) @@ -756,6 +787,34 @@ bool lcl_IsEmptyOrNote( ScDocument* pDoc, const table::CellAddress& rCurrentPos return ( !pCell || pCell->GetCellType() == CELLTYPE_NOTE ); } +static sal_uInt16 lcl_GetErrorCode( const rtl::OUString& rText ) +{ + static const struct { const sal_Char* pName; sal_uInt16 nCode; } aErrors[] = + { + { "#NULL!", errNoCode }, { "#DIV/0!", errDivisionByZero }, { "#VALUE!", errNoValue }, + { "#REF!", errNoRef }, { "#NAME?", errNoName }, { "#NUM!", errIllegalFPOperation }, + { "#N/A", NOTAVAILABLE } + }; + for (size_t i = 0; i < sizeof(aErrors)/sizeof(aErrors[0]); ++i) + { + if (rText.equalsAscii(aErrors[i].pName) || String(rText) == ScGlobal::GetErrorString(aErrors[i].nCode)) + return aErrors[i].nCode; + } + if (rText.matchAsciiL("Err:", 4) && rText.getLength() > 4) + { + sal_Int32 nCode = 0; + for (sal_Int32 i = 4; i < rText.getLength() && nCode <= NOTAVAILABLE; ++i) + { + if (rText[i] < '0' || rText[i] > '9') + return errNoValue; + nCode = nCode * 10 + (rText[i] - '0'); + } + if (nCode > 0 && nCode <= NOTAVAILABLE) + return static_cast<sal_uInt16>(nCode); + } + return errNoValue; +} + void ScXMLTableRowCellContext::EndElement() { if (!bHasSubTable) @@ -993,7 +1052,24 @@ void ScXMLTableRowCellContext::EndElement() break; default: { - DBG_ERROR("no cell type given"); + if (bIsError) + { + if (!rTables.IsPartOfMatrix(aCurrentPos.Column, aCurrentPos.Row)) + { + LockSolarMutex(); + ScAddress aScAddress; + ScUnoConversion::FillScAddress( aScAddress, aCurrentPos ); + ScDocument* pDoc = rXMLImport.GetDocument(); + ScFormulaCell* pErrCell = new ScFormulaCell( pDoc, aScAddress ); + pErrCell->SetErrCode( lcl_GetErrorCode( + (pOUTextValue && pOUTextValue->getLength()) ? *pOUTextValue : + pOUTextContent ? *pOUTextContent : rtl::OUString() ) ); + pDoc->PutCell( aScAddress, pErrCell ); + } + rXMLImport.ProgressBarIncrement(sal_False); + } + else + DBG_ERROR("no cell type given"); } break; } diff --git a/main/sc/source/filter/xml/xmlcelli.hxx b/main/sc/source/filter/xml/xmlcelli.hxx index 0736a852f1..fccb9bc553 100644 --- a/main/sc/source/filter/xml/xmlcelli.hxx +++ b/main/sc/source/filter/xml/xmlcelli.hxx @@ -72,6 +72,7 @@ class ScXMLTableRowCellContext : public SvXMLImportContext sal_Bool bIsFirstTextImport; sal_Bool bSolarMutexLocked; sal_Bool bFormulaTextResult; + sal_Bool bIsError; const ScXMLImport& GetScImport() const { return (const ScXMLImport&)GetImport(); } ScXMLImport& GetScImport() { return (ScXMLImport&)GetImport(); } diff --git a/main/sc/source/filter/xml/xmlexprt.cxx b/main/sc/source/filter/xml/xmlexprt.cxx index 06223b227c..1a5693d171 100644 --- a/main/sc/source/filter/xml/xmlexprt.cxx +++ b/main/sc/source/filter/xml/xmlexprt.cxx @@ -2981,7 +2981,9 @@ void ScXMLExport::WriteCell(ScMyCell& aCell, sal_Int32 nEqualCellCount) { rtl::OUStringBuffer sFormula; ScFormulaCell* pFormulaCell((ScFormulaCell*) pBaseCell); - if (!bIsMatrix || (bIsMatrix && bIsFirstMatrixCell)) + // an error without a formula (error cell, Excel import) has no valid formula to write + const bool bIsErrorOnly = pFormulaCell->GetCode()->GetCodeError() && !pFormulaCell->GetCode()->GetLen(); + if (!bIsErrorOnly && (!bIsMatrix || (bIsMatrix && bIsFirstMatrixCell))) { const formula::FormulaGrammar::Grammar eGrammar = pDoc->GetStorageGrammar(); sal_uInt16 nNamespacePrefix = (eGrammar == formula::FormulaGrammar::GRAM_ODFF ? XML_NAMESPACE_OF : XML_NAMESPACE_OOOC); @@ -2996,7 +2998,7 @@ void ScXMLExport::WriteCell(ScMyCell& aCell, sal_Int32 nEqualCellCount) AddAttribute(sAttrFormula, GetNamespaceMap().GetQNameByKey( nNamespacePrefix, sOUFormula.copy(1, sOUFormula.getLength() - 2), sal_False )); } } - if (pFormulaCell->IsValue()) + if (!bIsErrorOnly && pFormulaCell->IsValue()) { sal_Bool bIsStandard; rtl::OUString sCurrency;
