sc/source/filter/excel/excform.cxx | 88 ++++++++++++++++++++++-------------- sc/source/filter/excel/impop.cxx | 77 +++++++++++++++++++++++++++++-- sc/source/filter/excel/namebuff.cxx | 3 - sc/source/filter/excel/read.cxx | 10 ++-- sc/source/filter/inc/excform.hxx | 4 + sc/source/filter/inc/imp_op.hxx | 25 ++++++++-- 6 files changed, 157 insertions(+), 50 deletions(-)
New commits: commit eb14250bac55b36aafdba0c024316017fa217889 Author: Kohei Yoshida <[email protected]> Date: Wed Sep 18 18:42:44 2013 -0400 Fix the build. Change-Id: I3956f5992ac245e0ec72f82567c835398d8c3eb2 diff --git a/sc/source/filter/excel/excform.cxx b/sc/source/filter/excel/excform.cxx index 65de3f3..edd4918 100644 --- a/sc/source/filter/excel/excform.cxx +++ b/sc/source/filter/excel/excform.cxx @@ -130,7 +130,7 @@ void ImportExcel::Formula( if (pLast && pLast->mpCell && pLast->mnRow == (aScPos.Row()-1)) { ScFormulaCell* pCell = new ScFormulaCell(pD, aScPos, xGroup); - rDoc.getDoc().EnsureTable(aScPos.Tab()); + pD->EnsureTable(aScPos.Tab()); bool bInserted = pD->SetGroupFormulaCell(aScPos, pCell); if (!bInserted) { diff --git a/sc/source/filter/excel/impop.cxx b/sc/source/filter/excel/impop.cxx index cb62e22..f94e144 100644 --- a/sc/source/filter/excel/impop.cxx +++ b/sc/source/filter/excel/impop.cxx @@ -887,12 +887,11 @@ void ImportExcel::Shrfmla( void ) ScFormulaCellGroupRef xGroup = pExcRoot->pShrfmlaBuff->Find(aPos); if (xGroup) { - ScDocumentImport& rDoc = GetDocImport(); - xGroup->compileCode(rDoc.getDoc(), aPos, formula::FormulaGrammar::GRAM_DEFAULT); + xGroup->compileCode(*pD, aPos, formula::FormulaGrammar::GRAM_DEFAULT); ScFormulaCell* pCell = new ScFormulaCell(pD, aPos, xGroup); - rDoc.getDoc().EnsureTable(aPos.Tab()); - rDoc.setFormulaCell(aPos, pCell); + pD->EnsureTable(aPos.Tab()); + pD->SetGroupFormulaCell(aPos, pCell); pCell->SetNeedNumberFormat(false); if (!rtl::math::isNan(mpLastFormula->mfValue)) pCell->SetResultDouble(mpLastFormula->mfValue); commit f0ac5908cc86274b8058eb7393c4d3b940a9df34 Author: Kohei Yoshida <[email protected]> Date: Wed Sep 18 17:40:31 2013 -0400 Work around another Excel bug with incorrect shared formula range. Take the start row position from the preceding formula record, rather than believing what's in the shared formula record. The latter can be wrong, and can be wrong often. Conflicts: sc/source/filter/excel/excform.cxx sc/source/filter/excel/impop.cxx sc/source/filter/inc/imp_op.hxx Change-Id: I3a4da110727a7719e5f8cb3e6250c0e1bef04c64 diff --git a/sc/source/filter/excel/excform.cxx b/sc/source/filter/excel/excform.cxx index 82a3ae5..65de3f3 100644 --- a/sc/source/filter/excel/excform.cxx +++ b/sc/source/filter/excel/excform.cxx @@ -101,6 +101,9 @@ void ImportExcel::Formula4() void ImportExcel::Formula( const XclAddress& rXclPos, sal_uInt16 nXF, sal_uInt16 nFormLen, double fCurVal, bool bShrFmla) { + if (!nFormLen) + return; + ScAddress aScPos( ScAddress::UNINITIALIZED ); if (!GetAddressConverter().ConvertAddress(aScPos, rXclPos, GetCurrScTab(), true)) // Conversion failed. @@ -114,27 +117,42 @@ void ImportExcel::Formula( if (bShrFmla) { // This is a shared formula. Get the token array from the shared formula pool. - ScFormulaCellGroupRef xGroup = pFormConv->GetSharedFormula(maStrm, aScPos.Col(), nFormLen); - if (xGroup) + SCCOL nSharedCol; + SCROW nSharedRow; + if (pFormConv->ReadSharedFormulaPosition(maStrm, nSharedCol, nSharedRow)) { - if (xGroup->mnStart == aScPos.Row()) - // Generate code for the top cell only. - xGroup->compileCode(*pD, aScPos, formula::FormulaGrammar::GRAM_DEFAULT); - - ScFormulaCell* pCell = new ScFormulaCell(pD, aScPos, xGroup); - pD->EnsureTable(aScPos.Tab()); - bool bInserted = pD->SetGroupFormulaCell(aScPos, pCell); - if (!bInserted) + ScAddress aRefPos(aScPos.Col(), nSharedRow, GetCurrScTab()); + ScFormulaCellGroupRef xGroup = pFormConv->GetSharedFormula(aRefPos); + if (xGroup) { - delete pCell; - return; - } - xGroup->mnLength = aScPos.Row() - xGroup->mnStart + 1; - pCell->SetNeedNumberFormat(false); - if (!rtl::math::isNan(fCurVal)) - pCell->SetResultDouble(fCurVal); + // Make sure the this one follows immediately below another shared formula cell. + LastFormula* pLast = GetLastFormula(aScPos.Col()); + if (pLast && pLast->mpCell && pLast->mnRow == (aScPos.Row()-1)) + { + ScFormulaCell* pCell = new ScFormulaCell(pD, aScPos, xGroup); + rDoc.getDoc().EnsureTable(aScPos.Tab()); + bool bInserted = pD->SetGroupFormulaCell(aScPos, pCell); + if (!bInserted) + { + delete pCell; + return; + } + xGroup->mnLength = aScPos.Row() - xGroup->mnStart + 1; + pCell->SetNeedNumberFormat(false); + if (!rtl::math::isNan(fCurVal)) + pCell->SetResultDouble(fCurVal); - GetXFRangeBuffer().SetXF(aScPos, nXF); + GetXFRangeBuffer().SetXF(aScPos, nXF); + SetLastFormula(aScPos.Col(), aScPos.Row(), fCurVal, nXF, pCell); + } + } + else + { + // Shared formula not found even though it's clearly a shared formula. + // The cell will be created in the following shared formula + // record. + SetLastFormula(aScPos.Col(), aScPos.Row(), fCurVal, nXF, NULL); + } return; } } @@ -153,6 +171,7 @@ void ImportExcel::Formula( delete pCell; return; } + SetLastFormula(aScPos.Col(), aScPos.Row(), fCurVal, nXF, pCell); } else { @@ -1687,30 +1706,30 @@ const ScTokenArray* ExcelToSc::GetBoolErr( XclBoolError eType ) return pErgebnis; } -ScFormulaCellGroupRef ExcelToSc::GetSharedFormula( XclImpStream& aIn, SCCOL nCol, sal_Size nFormulaLen ) +bool ExcelToSc::ReadSharedFormulaPosition( XclImpStream& rStrm, SCCOL& rCol, SCROW& rRow ) { - if (!nFormulaLen) - return ScFormulaCellGroupRef(); - - aIn.PushPosition(); + rStrm.PushPosition(); sal_uInt8 nOp; - aIn >> nOp; + rStrm >> nOp; if (nOp != 0x01) // must be PtgExp token. { - aIn.PopPosition(); - return ScFormulaCellGroupRef(); + rStrm.PopPosition(); + return false; } - sal_uInt16 nLeftCol, nRow; - aIn >> nRow >> nLeftCol; - - ScAddress aRefPos(nCol, nRow, GetCurrScTab()); - ScFormulaCellGroupRef xGroup = GetOldRoot().pShrfmlaBuff->Find(aRefPos); + sal_uInt16 nRow, nCol; + rStrm >> nRow >> nCol; + rStrm.PopPosition(); + rCol = nCol; + rRow = nRow; + return true; +} - aIn.PopPosition(); - aIn.Ignore(nFormulaLen); +ScFormulaCellGroupRef ExcelToSc::GetSharedFormula( const ScAddress& rRefPos ) +{ + ScFormulaCellGroupRef xGroup = GetOldRoot().pShrfmlaBuff->Find(rRefPos); return xGroup; } diff --git a/sc/source/filter/excel/impop.cxx b/sc/source/filter/excel/impop.cxx index 28ddd9f..cb62e22 100644 --- a/sc/source/filter/excel/impop.cxx +++ b/sc/source/filter/excel/impop.cxx @@ -113,8 +113,10 @@ ImportExcel::ImportExcel( XclImpRootData& rImpData, SvStream& rStrm ): maStrm( rStrm, GetRoot() ), aIn( maStrm ), maScOleSize( ScAddress::INITIALIZE_INVALID ), + mpLastFormula(NULL), mnLastRefIdx( 0 ), mnIxfeIndex( 0 ), + mnLastRecId(0), mbBiff2HasXfs( false ), mbBiff2HasXfsValid( false ) { @@ -163,6 +165,31 @@ ImportExcel::~ImportExcel( void ) delete pFormConv; } +void ImportExcel::SetLastFormula( SCCOL nCol, SCROW nRow, double fVal, sal_uInt16 nXF, ScFormulaCell* pCell ) +{ + LastFormulaMapType::iterator it = maLastFormulaCells.find(nCol); + if (it == maLastFormulaCells.end()) + { + std::pair<LastFormulaMapType::iterator, bool> r = + maLastFormulaCells.insert( + LastFormulaMapType::value_type(nCol, LastFormula())); + it = r.first; + } + + it->second.mnCol = nCol; + it->second.mnRow = nRow; + it->second.mpCell = pCell; + it->second.mfValue = fVal; + it->second.mnXF = nXF; + + mpLastFormula = &it->second; +} + +ImportExcel::LastFormula* ImportExcel::GetLastFormula( SCCOL nCol ) +{ + LastFormulaMapType::iterator it = maLastFormulaCells.find(nCol); + return it == maLastFormulaCells.end() ? NULL : &it->second; +} void ImportExcel::ReadFileSharing() { @@ -813,6 +840,21 @@ void ImportExcel::Standardwidth( void ) void ImportExcel::Shrfmla( void ) { + switch (mnLastRecId) + { + case EXC_ID2_FORMULA: + case EXC_ID3_FORMULA: + case EXC_ID4_FORMULA: + // This record MUST immediately follow a FORMULA record. + break; + default: + return; + } + + if (!mpLastFormula) + // The last FORMULA record should have left this data. + return; + sal_uInt16 nFirstRow, nLastRow, nLenExpr; sal_uInt8 nFirstCol, nLastCol; @@ -827,13 +869,37 @@ void ImportExcel::Shrfmla( void ) pFormConv->Reset(); pFormConv->Convert( pErgebnis, maStrm, nLenExpr, true, FT_SharedFormula ); - OSL_ENSURE( pErgebnis, "+ImportExcel::Shrfmla(): ScTokenArray is NULL!" ); - pExcRoot->pShrfmlaBuff->Store( ScRange( static_cast<SCCOL>(nFirstCol), - static_cast<SCROW>(nFirstRow), GetCurrScTab(), - static_cast<SCCOL>(nLastCol), static_cast<SCROW>(nLastRow), - GetCurrScTab()), *pErgebnis ); + // The range in this record can be erroneous especially the row range. + // Use the row from the last FORMULA record as the start row. The end row + // will be adjusted by the formula cells that follow. + SCCOL nCol1 = nFirstCol; + SCCOL nCol2 = nLastCol; + SCROW nRow1 = mpLastFormula->mnRow; + + pExcRoot->pShrfmlaBuff->Store( + ScRange(nCol1, nRow1, GetCurrScTab(), nCol2, nRow1, GetCurrScTab()), *pErgebnis); + + // Create formula cell for the last formula record. + + ScAddress aPos(nCol1, nRow1, GetCurrScTab()); + ScFormulaCellGroupRef xGroup = pExcRoot->pShrfmlaBuff->Find(aPos); + if (xGroup) + { + ScDocumentImport& rDoc = GetDocImport(); + xGroup->compileCode(rDoc.getDoc(), aPos, formula::FormulaGrammar::GRAM_DEFAULT); + + ScFormulaCell* pCell = new ScFormulaCell(pD, aPos, xGroup); + rDoc.getDoc().EnsureTable(aPos.Tab()); + rDoc.setFormulaCell(aPos, pCell); + pCell->SetNeedNumberFormat(false); + if (!rtl::math::isNan(mpLastFormula->mfValue)) + pCell->SetResultDouble(mpLastFormula->mfValue); + + GetXFRangeBuffer().SetXF(aPos, mpLastFormula->mnXF); + mpLastFormula->mpCell = pCell; + } } @@ -1179,6 +1245,8 @@ void ImportExcel::NeueTabelle( void ) } pExcRoot->pShrfmlaBuff->Clear(); + maLastFormulaCells.clear(); + mpLastFormula = NULL; InitializeTable( nTab ); diff --git a/sc/source/filter/excel/read.cxx b/sc/source/filter/excel/read.cxx index 01629f5..fcf3aa0 100644 --- a/sc/source/filter/excel/read.cxx +++ b/sc/source/filter/excel/read.cxx @@ -629,7 +629,6 @@ FltError ImportExcel::Read( void ) case 0x0223: Externname34(); break; // EXTERNNAME [ 34 ] case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345] case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break; - case 0x04BC: Shrfmla(); break; // SHRFMLA [ 5] } } } @@ -653,6 +652,7 @@ FltError ImportExcel::Read( void ) case EXC_ID2_FORMULA: case EXC_ID3_FORMULA: case EXC_ID4_FORMULA: Formula25(); break; + case EXC_ID_SHRFMLA: Shrfmla(); break; case 0x0A: Eof(); eAkt = Z_Biff5E; break; case 0x14: case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break; @@ -828,7 +828,9 @@ FltError ImportExcel8::Read( void ) std::vector<OUString> aCodeNames; std::vector < SCTAB > nTabsWithNoCodeName; - while( eAkt != EXC_STATE_END ) + sal_uInt16 nRecId = 0; + + for (; eAkt != EXC_STATE_END; mnLastRecId = nRecId) { if( eAkt == EXC_STATE_BEFORE_SHEET ) { @@ -932,7 +934,7 @@ FltError ImportExcel8::Read( void ) if( eAkt != EXC_STATE_SHEET_PRE && eAkt != EXC_STATE_GLOBALS_PRE ) pProgress->ProgressAbs( nProgressBaseSize + aIn.GetSvStreamPos() - nProgressBasePos ); - sal_uInt16 nRecId = aIn.GetRecId(); + nRecId = aIn.GetRecId(); /* #i39464# Ignore records between USERSVIEWBEGIN and USERSVIEWEND completely (user specific view settings). Otherwise view settings @@ -1140,7 +1142,6 @@ FltError ImportExcel8::Read( void ) case EXC_ID2_ARRAY: case EXC_ID3_ARRAY: Array34(); break; // ARRAY [ 34 ] case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345 ] - case EXC_ID_SHRFMLA: Shrfmla(); break; // SHRFMLA [ 5 ] case 0x0867: SheetProtection(); break; // SHEETPROTECTION } } @@ -1174,6 +1175,7 @@ FltError ImportExcel8::Read( void ) case EXC_ID2_FORMULA: case EXC_ID3_FORMULA: case EXC_ID4_FORMULA: Formula25(); break; + case EXC_ID_SHRFMLA: Shrfmla(); break; case 0x000C: Calccount(); break; // CALCCOUNT case 0x0010: Delta(); break; // DELTA case 0x0011: Iteration(); break; // ITERATION diff --git a/sc/source/filter/inc/excform.hxx b/sc/source/filter/inc/excform.hxx index 0e2004c..11254a0 100644 --- a/sc/source/filter/inc/excform.hxx +++ b/sc/source/filter/inc/excform.hxx @@ -63,7 +63,9 @@ public: void GetDummy( const ScTokenArray*& ); const ScTokenArray* GetBoolErr( XclBoolError ); - ScFormulaCellGroupRef GetSharedFormula( XclImpStream& rStrm, SCCOL nCol, sal_Size nFormulaLen ); + + bool ReadSharedFormulaPosition( XclImpStream& rStrm, SCCOL& rCol, SCROW& rRow ); + ScFormulaCellGroupRef GetSharedFormula( const ScAddress& rRefPos ); static void SetError( ScFormulaCell& rCell, const ConvErr eErr ); diff --git a/sc/source/filter/inc/imp_op.hxx b/sc/source/filter/inc/imp_op.hxx index 8e004f3..9a770e3 100644 --- a/sc/source/filter/inc/imp_op.hxx +++ b/sc/source/filter/inc/imp_op.hxx @@ -32,7 +32,7 @@ #include "excdefs.hxx" #include <boost/shared_ptr.hpp> #include <boost/ptr_container/ptr_vector.hpp> - +#include <boost/unordered_map.hpp> class SvStream; @@ -79,6 +79,16 @@ private: class ImportExcel : public ImportTyp, protected XclImpRoot { protected: + struct LastFormula + { + SCCOL mnCol; + SCROW mnRow; + double mfValue; + sal_uInt16 mnXF; + ScFormulaCell* mpCell; + }; + typedef boost::unordered_map<SCCOL, LastFormula> LastFormulaMapType; + static const double fExcToTwips; // Umrechnung 1/256 Zeichen -> Twips RootData* pExcRoot; @@ -99,17 +109,24 @@ protected: typedef boost::ptr_vector< XclImpOutlineDataBuffer > XclImpOutlineListBuffer; XclImpOutlineListBuffer* pOutlineListBuffer; + LastFormulaMapType maLastFormulaCells; // Keep track of last formula cells in each column. + LastFormula* mpLastFormula; + sal_Int16 mnLastRefIdx; sal_uInt16 mnIxfeIndex; /// Current XF identifier from IXFE record. - bool mbBiff2HasXfs; /// Select XF formatting or direct formatting in BIFF2. - bool mbBiff2HasXfsValid; /// False = mbBiff2HasXfs is undetermined yet. + sal_uInt16 mnLastRecId; SCTAB nBdshtTab; // Counter fuer Boundsheet - ScFormulaCell* pLastFormCell; // fuer String-Records sal_Bool bTabTruncated; // wenn Bereichsueberschreitung zum // Abschneiden von Zellen fuehrt + bool mbBiff2HasXfs:1; /// Select XF formatting or direct formatting in BIFF2. + bool mbBiff2HasXfsValid:1; /// False = mbBiff2HasXfs is undetermined yet. + + void SetLastFormula( SCCOL nCol, SCROW nRow, double fVal, sal_uInt16 nXF, ScFormulaCell* pCell ); + LastFormula* GetLastFormula( SCCOL nCol ); + // Record-Funktionen void ReadFileSharing(); commit ae10dc17fa517527392d00f90d7faa7788dec51d Author: Kohei Yoshida <[email protected]> Date: Wed Sep 11 01:04:18 2013 -0400 Handle import of shared formula from xls more gracefully. Many xls documents contain shared formula entries that don't follow the spec. We still need to be able to handle them. Change-Id: Ia7d01101a6759967d9ebb27b6540dcb67607d13f diff --git a/sc/source/filter/excel/excform.cxx b/sc/source/filter/excel/excform.cxx index 56a18f8..82a3ae5 100644 --- a/sc/source/filter/excel/excform.cxx +++ b/sc/source/filter/excel/excform.cxx @@ -115,27 +115,28 @@ void ImportExcel::Formula( { // This is a shared formula. Get the token array from the shared formula pool. ScFormulaCellGroupRef xGroup = pFormConv->GetSharedFormula(maStrm, aScPos.Col(), nFormLen); - if (!xGroup) - return; - - if (xGroup->mnStart == aScPos.Row()) - // Generate code for the top cell only. - xGroup->compileCode(*pD, aScPos, formula::FormulaGrammar::GRAM_DEFAULT); - - ScFormulaCell* pCell = new ScFormulaCell(pD, aScPos, xGroup); - pD->EnsureTable(aScPos.Tab()); - bool bInserted = pD->SetGroupFormulaCell(aScPos, pCell); - if (!bInserted) + if (xGroup) { - delete pCell; + if (xGroup->mnStart == aScPos.Row()) + // Generate code for the top cell only. + xGroup->compileCode(*pD, aScPos, formula::FormulaGrammar::GRAM_DEFAULT); + + ScFormulaCell* pCell = new ScFormulaCell(pD, aScPos, xGroup); + pD->EnsureTable(aScPos.Tab()); + bool bInserted = pD->SetGroupFormulaCell(aScPos, pCell); + if (!bInserted) + { + delete pCell; + return; + } + xGroup->mnLength = aScPos.Row() - xGroup->mnStart + 1; + pCell->SetNeedNumberFormat(false); + if (!rtl::math::isNan(fCurVal)) + pCell->SetResultDouble(fCurVal); + + GetXFRangeBuffer().SetXF(aScPos, nXF); return; } - pCell->SetNeedNumberFormat(false); - if (!rtl::math::isNan(fCurVal)) - pCell->SetResultDouble(fCurVal); - - GetXFRangeBuffer().SetXF(aScPos, nXF); - return; } ConvErr eErr = pFormConv->Convert( pResult, maStrm, nFormLen, true, FT_CellFormula); diff --git a/sc/source/filter/excel/namebuff.cxx b/sc/source/filter/excel/namebuff.cxx index 4e47c09..4c75d64 100644 --- a/sc/source/filter/excel/namebuff.cxx +++ b/sc/source/filter/excel/namebuff.cxx @@ -79,7 +79,6 @@ void SharedFormulaBuffer::Clear() void SharedFormulaBuffer::Store( const ScRange& rRange, const ScTokenArray& rArray ) { - SCROW nGroupLen = rRange.aEnd.Row() - rRange.aStart.Row() + 1; for (SCCOL i = rRange.aStart.Col(); i <= rRange.aEnd.Col(); ++i) { // Create one group per column. @@ -88,7 +87,7 @@ void SharedFormulaBuffer::Store( const ScRange& rRange, const ScTokenArray& rArr ScFormulaCellGroupRef xNewGroup(new ScFormulaCellGroup); xNewGroup->mnStart = rRange.aStart.Row(); - xNewGroup->mnLength = nGroupLen; + xNewGroup->mnLength = 1; xNewGroup->setCode(rArray); maFormulaGroups.insert(FormulaGroupsType::value_type(aPos, xNewGroup)); } _______________________________________________ Libreoffice-commits mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
