include/oox/drawingml/color.hxx | 4 oox/source/drawingml/color.cxx | 21 ++ sc/qa/unit/subsequent_export_test4.cxx | 6 sc/source/filter/inc/condformatbuffer.hxx | 34 ++- sc/source/filter/inc/condformatcontext.hxx | 10 - sc/source/filter/inc/numberformatsbuffer.hxx | 6 sc/source/filter/inc/stylesbuffer.hxx | 25 ++ sc/source/filter/oox/condformatbuffer.cxx | 257 ++++++++++++++++++++++----- sc/source/filter/oox/condformatcontext.cxx | 29 +-- sc/source/filter/oox/stylesbuffer.cxx | 61 ++++++ 10 files changed, 379 insertions(+), 74 deletions(-)
New commits: commit e2cc1f8a6bd27907800416a5396942a0c7ca56ce Author: Noel Grandin <noel.gran...@collabora.co.uk> AuthorDate: Fri Feb 28 10:51:44 2025 +0200 Commit: Noel Grandin <noel.gran...@collabora.co.uk> CommitDate: Fri Feb 28 13:16:46 2025 +0100 tdf#134864 speedup load of pathological conditional formats in XLS This associated file had 300k conditional formatting entries, which reduced to about 10 when de-duplicated. Rework the logic so instead of deduplicating rule model entries, we dedplicate at a slightly higher level, and make the comparison check more thoroughly, which greatly increases the number of duplicates we find. Takes the load time from 38sec to 6sec on my machine. Tweak one of the unit tests, because we now consolidate some of the conditional formatting entries in that test file. Change-Id: Iff2c6afb1c9959576aa00a38d0a8e5d4a48b0fd5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182356 Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> Tested-by: Jenkins diff --git a/include/oox/drawingml/color.hxx b/include/oox/drawingml/color.hxx index dc8be778f665..7bafe0f8083e 100644 --- a/include/oox/drawingml/color.hxx +++ b/include/oox/drawingml/color.hxx @@ -130,6 +130,8 @@ public: model::ComplexColor getComplexColor() const; + bool operator==(const Color&) const; + private: /** Internal helper for getColor(). */ void setResolvedRgb( ::Color nRgb ) const; @@ -161,6 +163,8 @@ private: sal_Int32 mnValue; explicit Transformation( sal_Int32 nToken, sal_Int32 nValue ) : mnToken( nToken ), mnValue( nValue ) {} + + bool operator==(const Transformation&) const noexcept = default; }; mutable ColorMode meMode; /// Current color mode. diff --git a/oox/source/drawingml/color.cxx b/oox/source/drawingml/color.cxx index 8961618a10f7..b4a6f3ff36a7 100644 --- a/oox/source/drawingml/color.cxx +++ b/oox/source/drawingml/color.cxx @@ -299,6 +299,27 @@ Color::Color() : { } +bool Color::operator==(const Color& rhs) const +{ + if (meMode != rhs.meMode) + return false; + if (maTransforms != rhs.maTransforms) + return false; + if (mnC1 != rhs.mnC1) + return false; + if (mnC2 != rhs.mnC2) + return false; + if (mnC3 != rhs.mnC3) + return false; + if (mnAlpha != rhs.mnAlpha) + return false; + if (msSchemeName != rhs.msSchemeName) + return false; + if (meThemeColorType != rhs.meThemeColorType) + return false; + return true; +} + ::Color Color::getDmlPresetColor( sal_Int32 nToken, ::Color nDefaultRgb ) { /* Do not pass nDefaultRgb to ContainerHelper::getVectorElement(), to be diff --git a/sc/qa/unit/subsequent_export_test4.cxx b/sc/qa/unit/subsequent_export_test4.cxx index a85a6e39d7ca..10cb1c9db8a5 100644 --- a/sc/qa/unit/subsequent_export_test4.cxx +++ b/sc/qa/unit/subsequent_export_test4.cxx @@ -1624,11 +1624,15 @@ CPPUNIT_TEST_FIXTURE(ScExportTest4, testTdf148820) xmlDocUniquePtr pSheet = parseExport(u"xl/worksheets/sheet1.xml"_ustr); CPPUNIT_ASSERT(pSheet); + CPPUNIT_ASSERT_EQUAL(u"5"_ustr, + getXPathContent(pSheet, "count(/x:worksheet/x:conditionalFormatting)")); + CPPUNIT_ASSERT_EQUAL( + u"5"_ustr, getXPathContent(pSheet, "count(/x:worksheet/x:conditionalFormatting/x:cfRule)")); sal_Int32 nDxfIdCondFormatFirst = getXPath(pSheet, "/x:worksheet/x:conditionalFormatting[1]/x:cfRule", "dxfId").toInt32() + 1; sal_Int32 nDxfIdCondFormatLast - = getXPath(pSheet, "/x:worksheet/x:conditionalFormatting[20]/x:cfRule", "dxfId").toInt32() + = getXPath(pSheet, "/x:worksheet/x:conditionalFormatting[5]/x:cfRule", "dxfId").toInt32() + 1; xmlDocUniquePtr pStyles = parseExport(u"xl/styles.xml"_ustr); diff --git a/sc/source/filter/inc/condformatbuffer.hxx b/sc/source/filter/inc/condformatbuffer.hxx index ef60145a11fe..47f0080c2464 100644 --- a/sc/source/filter/inc/condformatbuffer.hxx +++ b/sc/source/filter/inc/condformatbuffer.hxx @@ -91,6 +91,8 @@ struct ColorScaleRuleModelEntry mbPercentile(false), mbNum(false), mbGreaterThanOrEqual(true) {} + + bool operator==(const ColorScaleRuleModelEntry &) const = default; }; class ColorScaleRule final : public WorksheetHelper @@ -103,6 +105,9 @@ public: void AddEntries( ScColorScaleFormat* pFormat, ScDocument* pDoc, const ScAddress& rAddr ); + const std::vector< ColorScaleRuleModelEntry > & getModelEntries() const { return maColorScaleRuleEntries; } + sal_uInt32 getCfvo() const { return mnCfvo; } + sal_uInt32 getCol() const { return mnCol; } private: std::vector< ColorScaleRuleModelEntry > maColorScaleRuleEntries; @@ -147,10 +152,16 @@ private: bool mbCustom; }; -/** Represents a single rule in a conditional formatting. */ +/** Represents a single rule in a conditional formatting. + Unlike other objects, we hold this by unique_ptr. + We cannot use shared_ptr like the other objects, since + it wants to have a pointer to its parent, + and its parent might get deduplicated and deleted. +*/ class CondFormatRule final : public WorksheetHelper { friend class CondFormatBuffer; +friend struct CondFormatEquals; public: explicit CondFormatRule( const CondFormat& rCondFormat, ScConditionalFormat* pFormat ); @@ -171,10 +182,14 @@ public: /** Returns the priority of this rule. */ sal_Int32 getPriority() const { return maModel.mnPriority; } + const CondFormatRuleModel & getRuleModel() const { return maModel; } + ColorScaleRule* getColorScale(); DataBarRule* getDataBar(); IconSetRule* getIconSet(); + const CondFormat& getParentCondFormat() const { return mrCondFormat; } + private: const CondFormat& mrCondFormat; CondFormatRuleModel maModel; @@ -185,8 +200,6 @@ private: std::unique_ptr<IconSetRule> mpIconSet; }; -typedef std::shared_ptr< CondFormatRule > CondFormatRuleRef; - /** Model for a conditional formatting object. */ struct CondFormatModel { @@ -197,11 +210,15 @@ struct CondFormatModel }; class CondFormatBuffer; +struct CondFormatHash; +struct CondFormatEquals; /** Represents a conditional formatting object with a list of affected cell ranges. */ class CondFormat final : public WorksheetHelper { friend class CondFormatBuffer; +friend struct CondFormatHash; +friend struct CondFormatEquals; public: explicit CondFormat( const WorksheetHelper& rHelper ); ~CondFormat(); @@ -209,7 +226,7 @@ public: /** Imports settings from the conditionalFormatting element. */ void importConditionalFormatting( const AttributeList& rAttribs ); /** Imports a conditional formatting rule from the cfRule element. */ - CondFormatRuleRef importCfRule( const AttributeList& rAttribs ); + std::unique_ptr<CondFormatRule> importCfRule( const AttributeList& rAttribs ); /** Imports settings from the CONDFORMATTING record. */ void importCondFormatting( SequenceInputStream& rStrm ); @@ -223,12 +240,12 @@ public: const ScRangeList& getRanges() const { return maModel.maRanges; } void setReadyForFinalize() { mbReadyForFinalize = true; } - void insertRule( CondFormatRuleRef const & xRule ); + void insertRule( std::unique_ptr<CondFormatRule> xRule ); private: - CondFormatRuleRef createRule(); + std::unique_ptr<CondFormatRule> createRule(); private: - typedef RefMap< sal_Int32, CondFormatRule > CondFormatRuleMap; + typedef std::map< sal_Int32, std::unique_ptr<CondFormatRule> > CondFormatRuleMap; CondFormatModel maModel; /// Model of this conditional formatting. CondFormatRuleMap maRules; /// Maps formatting rules by priority. @@ -316,15 +333,16 @@ public: static sal_Int32 convertToApiOperator( sal_Int32 nToken ); static ScConditionMode convertToInternalOperator( sal_Int32 nToken ); void finalizeImport(); - bool insertRule(CondFormatRef const & xCondFmt, CondFormatRuleRef const & xRule); private: CondFormatRef createCondFormat(); void updateImport(const ScDataBarFormatData* pTarget); + void deduplicateCondFormats(); private: typedef RefVector< CondFormat > CondFormatVec; typedef RefVector< ExtCfDataBarRule > ExtCfDataBarRuleVec; + CondFormatVec maCondFormats; /// All conditional formatting in a sheet. ExtCfDataBarRuleVec maCfRules; /// All external conditional formatting rules in a sheet. std::vector< std::unique_ptr<ExtCfCondFormat> > maExtCondFormats; diff --git a/sc/source/filter/inc/condformatcontext.hxx b/sc/source/filter/inc/condformatcontext.hxx index 351d2541b8ff..e406be4ddfac 100644 --- a/sc/source/filter/inc/condformatcontext.hxx +++ b/sc/source/filter/inc/condformatcontext.hxx @@ -29,25 +29,25 @@ class CondFormatContext; class ColorScaleContext final : public WorksheetContextBase { public: - explicit ColorScaleContext( CondFormatContext& rFragment, CondFormatRuleRef xRule ); + explicit ColorScaleContext( CondFormatContext& rFragment, CondFormatRule& rRule ); virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override; virtual void onStartElement( const AttributeList& rAttribs ) override; private: - CondFormatRuleRef mxRule; + CondFormatRule& mrRule; }; class DataBarContext final : public WorksheetContextBase { public: - explicit DataBarContext( CondFormatContext& rFormat, CondFormatRuleRef xRule ); + explicit DataBarContext( CondFormatContext& rFormat, CondFormatRule& rRule ); virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override; virtual void onStartElement( const AttributeList& rAttribs ) override; private: - CondFormatRuleRef mxRule; + CondFormatRule& mrRule; }; class IconSetContext final : public WorksheetContextBase @@ -81,7 +81,7 @@ private: virtual void onEndRecord() override; CondFormatRef mxCondFmt; - CondFormatRuleRef mxRule; + std::unique_ptr<CondFormatRule> mxRule; }; } // namespace oox::xls diff --git a/sc/source/filter/inc/numberformatsbuffer.hxx b/sc/source/filter/inc/numberformatsbuffer.hxx index 10ec0de1ce9e..bbd0d4fff20f 100644 --- a/sc/source/filter/inc/numberformatsbuffer.hxx +++ b/sc/source/filter/inc/numberformatsbuffer.hxx @@ -40,6 +40,8 @@ struct NumFmtModel sal_Int16 mnPredefId; explicit NumFmtModel(); + + bool operator==(const NumFmtModel&) const = default; }; /** Contains all API number format attributes. */ @@ -51,7 +53,7 @@ struct ApiNumFmtData }; /** Contains all data for a number format code. */ -class NumberFormat : public WorkbookHelper +class NumberFormat final : public WorkbookHelper { public: explicit NumberFormat( const WorkbookHelper& rHelper ); @@ -73,6 +75,8 @@ public: const css::lang::Locale& rFromLocale ); sal_uInt32 fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const; + const NumFmtModel & getModel() const { return maModel; } + private: NumFmtModel maModel; ApiNumFmtData maApiData; diff --git a/sc/source/filter/inc/stylesbuffer.hxx b/sc/source/filter/inc/stylesbuffer.hxx index 95ec8b8806ff..224ed4ad92b3 100644 --- a/sc/source/filter/inc/stylesbuffer.hxx +++ b/sc/source/filter/inc/stylesbuffer.hxx @@ -154,6 +154,8 @@ struct FontModel explicit FontModel(); + bool operator==(const FontModel&) const; + void setBiff12Scheme( sal_uInt8 nScheme ); void setBiffHeight( sal_uInt16 nHeight ); void setBiffWeight( sal_uInt16 nWeight ); @@ -279,6 +281,8 @@ struct AlignmentModel explicit AlignmentModel(); + bool operator==(const AlignmentModel&) const = default; + /** Sets horizontal alignment from the passed BIFF data. */ void setBiffHorAlign( sal_uInt8 nHorAlign ); /** Sets vertical alignment from the passed BIFF data. */ @@ -339,6 +343,8 @@ struct ProtectionModel bool mbLocked; /// True = locked against editing. bool mbHidden; /// True = formula is hidden. + bool operator==(const ProtectionModel&) const = default; + explicit ProtectionModel(); }; @@ -374,6 +380,8 @@ public: /** Returns the converted API protection data struct. */ const ApiProtectionData& getApiData() const { return maApiData; } + const ProtectionModel& getModelData() const { return maModel; } + void fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const; private: ProtectionModel maModel; /// Protection model data. @@ -392,6 +400,8 @@ struct BorderLineModel explicit BorderLineModel( bool bDxf ); + bool operator==(const BorderLineModel&) const; + /** Sets the passed BIFF line style. */ void setBiffStyle( sal_Int32 nLineStyle ); }; @@ -408,6 +418,8 @@ struct BorderModel bool mbDiagBLtoTR; /// True = bottom-left to top-right on. explicit BorderModel( bool bDxf ); + + bool operator==(const BorderModel&) const = default; }; /** Contains API attributes of a complete cell border. */ @@ -457,6 +469,8 @@ public: /** Returns the converted API border data struct. */ const ApiBorderData& getApiData() const { return maApiData; } + const BorderModel& getModelData() const { return maModel; } + void fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const; private: @@ -489,6 +503,8 @@ struct PatternFillModel explicit PatternFillModel( bool bDxf ); + bool operator==(const PatternFillModel&) const; + /** Sets the passed BIFF pattern identifier. */ void setBiffPattern( sal_Int32 nPattern ); }; @@ -512,6 +528,8 @@ struct GradientFillModel void readGradient( SequenceInputStream& rStrm ); /** Reads BIFF12 gradient stop settings from a FILL or DXF record. */ void readGradientStop( SequenceInputStream& rStrm, bool bDxf ); + + bool operator==(const GradientFillModel&) const = default; }; /** Contains API fill attributes. */ @@ -561,10 +579,14 @@ public: void fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const; -private: typedef std::shared_ptr< PatternFillModel > PatternModelRef; typedef std::shared_ptr< GradientFillModel > GradientModelRef; + const PatternModelRef & getPatternModel() const { return mxPatternModel; } + const GradientModelRef & getGradientModel() const {return mxGradientModel; } + +private: + PatternModelRef mxPatternModel; GradientModelRef mxGradientModel; ApiSolidFillData maApiData; @@ -664,6 +686,7 @@ typedef std::shared_ptr< Xf > XfRef; class Dxf : public WorkbookHelper { +friend struct CondFormatEquals; public: explicit Dxf( const WorkbookHelper& rHelper ); diff --git a/sc/source/filter/oox/condformatbuffer.cxx b/sc/source/filter/oox/condformatbuffer.cxx index d40b16a6e84c..59a613878507 100644 --- a/sc/source/filter/oox/condformatbuffer.cxx +++ b/sc/source/filter/oox/condformatbuffer.cxx @@ -1122,9 +1122,9 @@ void CondFormat::importConditionalFormatting( const AttributeList& rAttribs ) mpFormat = new ScConditionalFormat(0, &getScDocument()); } -CondFormatRuleRef CondFormat::importCfRule( const AttributeList& rAttribs ) +std::unique_ptr<CondFormatRule> CondFormat::importCfRule( const AttributeList& rAttribs ) { - CondFormatRuleRef xRule = createRule(); + std::unique_ptr<CondFormatRule> xRule = createRule(); xRule->importCfRule( rAttribs ); return xRule; } @@ -1140,9 +1140,9 @@ void CondFormat::importCondFormatting( SequenceInputStream& rStrm ) void CondFormat::importCfRule( SequenceInputStream& rStrm ) { - CondFormatRuleRef xRule = createRule(); + std::unique_ptr<CondFormatRule> xRule = createRule(); xRule->importCfRule( rStrm ); - insertRule( xRule ); + insertRule( std::move(xRule) ); } void CondFormat::finalizeImport() @@ -1152,7 +1152,8 @@ void CondFormat::finalizeImport() return; ScDocument& rDoc = getScDocument(); mpFormat->SetRange(maModel.maRanges); - maRules.forEachMem( &CondFormatRule::finalizeImport ); + for (auto & rPair : maRules) + rPair.second->finalizeImport(); if (mpFormat->size() > 0) { @@ -1164,17 +1165,18 @@ void CondFormat::finalizeImport() } } -CondFormatRuleRef CondFormat::createRule() +std::unique_ptr<CondFormatRule> CondFormat::createRule() { - return std::make_shared<CondFormatRule>( *this, mpFormat ); + return std::make_unique<CondFormatRule>( *this, mpFormat ); } -void CondFormat::insertRule( CondFormatRuleRef const & xRule ) +void CondFormat::insertRule( std::unique_ptr<CondFormatRule> xRule ) { - if( xRule && (xRule->getPriority() > 0) ) + if( xRule->getPriority() > 0 ) { + assert(&xRule->getParentCondFormat() == this); OSL_ENSURE( maRules.find( xRule->getPriority() ) == maRules.end(), "CondFormat::insertRule - multiple rules with equal priority" ); - maRules[ xRule->getPriority() ] = xRule; + maRules[ xRule->getPriority() ] = std::move(xRule); } } @@ -1229,34 +1231,10 @@ void CondFormatBuffer::updateImport(const ScDataBarFormatData* pTarget) } } -bool CondFormatBuffer::insertRule(CondFormatRef const & xCondFmt, CondFormatRuleRef const & xRule) -{ - CondFormatRef xFoundFmt; - ScRangeList aRanges = xCondFmt->getRanges(); - - for (auto& rCondFmt : maCondFormats) - { - if (xCondFmt == rCondFmt) - continue; - - if (aRanges == rCondFmt->getRanges()) - { - xFoundFmt = rCondFmt; - break; - } - } - - if (xFoundFmt) - { - xRule->mpFormat = xFoundFmt->mpFormat; - xFoundFmt->insertRule(xRule); - } - - return static_cast<bool>(xFoundFmt); -} - void CondFormatBuffer::finalizeImport() { + deduplicateCondFormats(); + std::unordered_set<size_t> aDoneExtCFs; typedef std::unordered_map<ScRangeList, CondFormat*, ScRangeListHasher> RangeMap; RangeMap aRangeMap; @@ -1281,7 +1259,7 @@ void CondFormatBuffer::finalizeImport() size_t nEntryIdx = 0; for (const auto& rxEntry : rEntries) { - CondFormatRuleRef xRule = rCondFormat.createRule(); + std::unique_ptr<CondFormatRule> xRule = rCondFormat.createRule(); if (ScDataBarFormat *pData = dynamic_cast<ScDataBarFormat*>(rxEntry.get())) updateImport(pData->GetDataBarData()); ScFormatEntry* pNewEntry = rxEntry->Clone(pDoc); @@ -1289,7 +1267,7 @@ void CondFormatBuffer::finalizeImport() if (nPriority == -1) nPriority = mnNonPrioritizedRuleNextPriority++; xRule->setFormatEntry(nPriority, pNewEntry); - rCondFormat.insertRule(xRule); + rCondFormat.insertRule(std::move(xRule)); ++nEntryIdx; } @@ -1321,10 +1299,9 @@ void CondFormatBuffer::finalizeImport() } for( const auto& rxCondFormat : maCondFormats ) - { - if ( rxCondFormat) + if (rxCondFormat) rxCondFormat->finalizeImport(); - } + for ( const auto& rxCfRule : maCfRules ) { if ( rxCfRule ) @@ -1366,6 +1343,204 @@ void CondFormatBuffer::finalizeImport() gnStyleIdx = 0; // Resets <extlst> <cfRule> style index. } +/// Used for deduplicating +struct CondFormatHash +{ + size_t operator()(const CondFormatRef& x) const { return hashCode(*x); } + +private: + static size_t hashCode(const CondFormat& r) + { + std::size_t seed(0); + // note that we deliberately skip the maRanges field, because, if necessary, we will merge + // new entries into that field. + o3tl::hash_combine(seed, r.maModel.mbPivot); + for (const auto & rPair : r.maRules) + o3tl::hash_combine(seed, hashCode(*rPair.second)); + return seed; + } + static size_t hashCode(const CondFormatRule& r) + { + std::size_t seed(0); + o3tl::hash_combine(seed, hashCode(r.getRuleModel())); + return seed; + } + static size_t hashCode(const CondFormatRuleModel& r) + { + std::size_t seed(0); + o3tl::hash_combine(seed, r.maText); + // we skip mnPriority, see comment in CondFormatEquals + o3tl::hash_combine(seed, r.mnType); + o3tl::hash_combine(seed, r.mnOperator); + o3tl::hash_combine(seed, r.mnTimePeriod); + o3tl::hash_combine(seed, r.mnRank); + o3tl::hash_combine(seed, r.mnStdDev); + // o3tl::hash_combine(seed, r.mnDxfId); if I hash this, need to hash the contents + o3tl::hash_combine(seed, r.mbStopIfTrue); + o3tl::hash_combine(seed, r.mbBottom); + o3tl::hash_combine(seed, r.mbPercent); + o3tl::hash_combine(seed, r.mbAboveAverage); + o3tl::hash_combine(seed, r.mbEqualAverage); + return seed; + } +}; + +/// Used for deduplicating +struct CondFormatEquals +{ + const StylesBuffer& mrStyles; + + CondFormatEquals(const StylesBuffer& rStyles) : mrStyles(rStyles) {} + + bool operator()(const CondFormatRef& lhs, const CondFormatRef& rhs) const + { + if (lhs.get() == rhs.get()) + return true; + // note that we deliberately skip the maRanges field, because, if necessary, we will merge + // new entries into that field. + if (lhs->maModel.mbPivot != rhs->maModel.mbPivot) + return false; + auto it1 = lhs->maRules.begin(); + auto it2 = rhs->maRules.begin(); + while (it1 != lhs->maRules.end() && it2 != rhs->maRules.end()) + { + if (!equals(it1->second, it2->second)) + return false; + ++it1; + ++it2; + } + return it1 == lhs->maRules.end() && it2 == rhs->maRules.end(); + } +private: + bool equals(const std::unique_ptr<CondFormatRule>& lhs, const std::unique_ptr<CondFormatRule>& rhs) const + { + if (lhs.get() == rhs.get()) + return true; + if (!equals(lhs->getRuleModel(), rhs->getRuleModel())) + return false; + if (bool(lhs->mpColor) != bool(rhs->mpColor)) + return false; + if (lhs->mpColor) + { + if (lhs->mpColor->getModelEntries() != rhs->mpColor->getModelEntries()) + return false; + if (lhs->mpColor->getCfvo() != rhs->mpColor->getCfvo()) + return false; + if (lhs->mpColor->getCol() != rhs->mpColor->getCol()) + return false; + } + // todo: I'm not bothering to properly check the following fields + // because I ran out of enthusiasm, so we wont detect duplicates if they + // contain data here. + if (lhs->mpDataBar.get() != rhs->mpDataBar.get()) + return false; + if (lhs->mpIconSet.get() != rhs->mpIconSet.get()) + return false; + return true; + } + bool equals(const CondFormatRuleModel& lhs, const CondFormatRuleModel& rhs) const + { + if (lhs.maText != rhs.maText) + return false; + // we skip mnPriority, because that is implicitly compared by the ordering of these objects + if (lhs.mnType != rhs.mnType) + return false; + if (lhs.mnOperator != rhs.mnOperator) + return false; + if (lhs.mnTimePeriod != rhs.mnTimePeriod) + return false; + if (lhs.mnRank != rhs.mnRank) + return false; + if (lhs.mnStdDev != rhs.mnStdDev) + return false; + if (!equalsDxf(lhs.mnDxfId, rhs.mnDxfId)) + return false; + if (lhs.mbStopIfTrue != rhs.mbStopIfTrue) + return false; + if (lhs.mbBottom != rhs.mbBottom) + return false; + if (lhs.mbPercent != rhs.mbPercent) + return false; + if (lhs.mbAboveAverage != rhs.mbAboveAverage) + return false; + if (lhs.mbEqualAverage != rhs.mbEqualAverage) + return false; + return true; + } + bool equalsDxf(sal_Int32 lhsDxfId, sal_Int32 rhsDxfId) const + { + if (lhsDxfId == rhsDxfId) + return true; + DxfRef pLhsDxf = mrStyles.getDxf(lhsDxfId); + DxfRef pRhsDxf = mrStyles.getDxf(rhsDxfId); + if (bool(pLhsDxf) != bool(pRhsDxf)) + return false; + if (pLhsDxf && !equals(*pLhsDxf, *pRhsDxf)) + return false; + return true; + } + static bool equals(const Dxf& lhs, const Dxf& rhs) + { + if (bool(lhs.mxFont) != bool(rhs.mxFont)) + return false; + if (lhs.mxFont && lhs.mxFont->getModel() != rhs.mxFont->getModel()) + return false; + if (bool(lhs.mxNumFmt) != bool(rhs.mxNumFmt)) + return false; + if (lhs.mxNumFmt && lhs.mxNumFmt->getModel() != rhs.mxNumFmt->getModel()) + return false; + if (bool(lhs.mxAlignment) != bool(rhs.mxAlignment)) + return false; + if (lhs.mxAlignment && lhs.mxAlignment->getModel() != rhs.mxAlignment->getModel()) + return false; + if (bool(lhs.mxProtection) != bool(rhs.mxProtection)) + return false; + if (lhs.mxProtection && lhs.mxProtection->getModelData() != rhs.mxProtection->getModelData()) + return false; + if (bool(lhs.mxBorder) != bool(rhs.mxBorder)) + return false; + if (lhs.mxBorder && lhs.mxBorder->getModelData() != rhs.mxBorder->getModelData()) + return false; + if (bool(lhs.mxFill) != bool(rhs.mxFill)) + return false; + if (lhs.mxFill) + { + if (bool(lhs.mxFill->getPatternModel()) != bool(rhs.mxFill->getPatternModel())) + return false; + if (lhs.mxFill->getPatternModel() && *lhs.mxFill->getPatternModel() != *rhs.mxFill->getPatternModel()) + return false; + if (bool(lhs.mxFill->getGradientModel()) != bool(rhs.mxFill->getGradientModel())) + return false; + if (lhs.mxFill->getGradientModel() && *lhs.mxFill->getGradientModel() != *rhs.mxFill->getGradientModel()) + return false; + } + return true; + } +}; + +// Excel will sometimes produce files with 100k of duplicate conditional formatting +// entries, but only 10 unique ones, which will make LO freeze because our ScPatternAttr +// de-duplication is quite expensive. So rather do it here, where it is considerably cheaper. +void CondFormatBuffer::deduplicateCondFormats() +{ + // remove duplicates as we go + std::unordered_set<CondFormatRef, CondFormatHash, CondFormatEquals> aDeduped{5, CondFormatHash(), CondFormatEquals{getStyles()}}; + for( auto it = maCondFormats.begin(); it != maCondFormats.end(); ) + { + auto pair = aDeduped.insert(*it); + // If the insert did not succeed, we have a duplicate, and we need to merge the ranges of this item + // into the existing ranges list. + if (!pair.second) + { + for (const auto & rRange : (*it)->maModel.maRanges) + (*pair.first)->maModel.maRanges.push_back(rRange); + it = maCondFormats.erase(it); + } + else + ++it; + } +} + CondFormatRef CondFormatBuffer::importCondFormatting( SequenceInputStream& rStrm ) { CondFormatRef xCondFmt = createCondFormat(); diff --git a/sc/source/filter/oox/condformatcontext.cxx b/sc/source/filter/oox/condformatcontext.cxx index 5da1f721ffc6..6f3f988d8abe 100644 --- a/sc/source/filter/oox/condformatcontext.cxx +++ b/sc/source/filter/oox/condformatcontext.cxx @@ -29,9 +29,9 @@ namespace oox::xls { using ::oox::core::ContextHandlerRef; -ColorScaleContext::ColorScaleContext( CondFormatContext& rFragment, CondFormatRuleRef xRule ) : +ColorScaleContext::ColorScaleContext( CondFormatContext& rFragment, CondFormatRule& rRule ) : WorksheetContextBase( rFragment ), - mxRule(std::move( xRule )) + mrRule( rRule ) { } @@ -57,17 +57,17 @@ void ColorScaleContext::onStartElement( const AttributeList& rAttribs ) switch( getCurrentElement() ) { case XLS_TOKEN( cfvo ): - mxRule->getColorScale()->importCfvo( rAttribs ); + mrRule.getColorScale()->importCfvo( rAttribs ); break; case XLS_TOKEN( color ): - mxRule->getColorScale()->importColor( rAttribs ); + mrRule.getColorScale()->importColor( rAttribs ); break; } } -DataBarContext::DataBarContext( CondFormatContext& rFragment, CondFormatRuleRef xRule ) : +DataBarContext::DataBarContext( CondFormatContext& rFragment, CondFormatRule& rRule ) : WorksheetContextBase( rFragment ), - mxRule(std::move( xRule )) + mrRule( rRule ) { } @@ -93,13 +93,13 @@ void DataBarContext::onStartElement( const AttributeList& rAttribs ) switch( getCurrentElement() ) { case XLS_TOKEN( dataBar ): - mxRule->getDataBar()->importAttribs( rAttribs ); + mrRule.getDataBar()->importAttribs( rAttribs ); break; case XLS_TOKEN( cfvo ): - mxRule->getDataBar()->importCfvo( rAttribs ); + mrRule.getDataBar()->importCfvo( rAttribs ); break; case XLS_TOKEN( color ): - mxRule->getDataBar()->importColor( rAttribs ); + mrRule.getDataBar()->importColor( rAttribs ); break; } } @@ -181,9 +181,9 @@ ContextHandlerRef CondFormatContext::onCreateContext( sal_Int32 nElement, const if (nElement == XLS_TOKEN( formula )) return this; else if (nElement == XLS_TOKEN( colorScale ) ) - return new ColorScaleContext( *this, mxRule ); + return new ColorScaleContext( *this, *mxRule ); else if (nElement == XLS_TOKEN( dataBar ) ) - return new DataBarContext( *this, mxRule ); + return new DataBarContext( *this, *mxRule ); else if (nElement == XLS_TOKEN( iconSet ) ) return new IconSetContext(*this, mxRule->getIconSet()); else if (nElement == XLS_TOKEN( extLst ) ) @@ -204,12 +204,7 @@ void CondFormatContext::onEndElement() break; case XLS_TOKEN( cfRule ): if (mxCondFmt && mxRule) - { - ScRangeList aRanges = mxCondFmt->getRanges(); - if ((aRanges.size() == 1 && aRanges.GetCellCount() == 1) || - !getCondFormats().insertRule(mxCondFmt, mxRule)) - mxCondFmt->insertRule(mxRule); - } + mxCondFmt->insertRule(std::move(mxRule)); break; } } diff --git a/sc/source/filter/oox/stylesbuffer.cxx b/sc/source/filter/oox/stylesbuffer.cxx index ab0af4cd2149..c3af8de44034 100644 --- a/sc/source/filter/oox/stylesbuffer.cxx +++ b/sc/source/filter/oox/stylesbuffer.cxx @@ -500,6 +500,37 @@ void FontModel::setBiffEscapement( sal_uInt16 nEscapement ) mnEscapement = STATIC_ARRAY_SELECT( spnEscapes, nEscapement, XML_baseline ); } +bool FontModel::operator==(const FontModel& rhs) const +{ + if (maName != rhs.maName) + return false; + if (maColor != rhs.maColor) + return false; + if (mnScheme != rhs.mnScheme) + return false; + if (mnFamily != rhs.mnFamily) + return false; + if (mnCharSet != rhs.mnCharSet) + return false; + if (mfHeight != rhs.mfHeight) + return false; + if (mnUnderline != rhs.mnUnderline) + return false; + if (mnEscapement != rhs.mnEscapement) + return false; + if (mbBold != rhs.mbBold) + return false; + if (mbItalic != rhs.mbItalic) + return false; + if (mbStrikeout != rhs.mbStrikeout) + return false; + if (mbOutline != rhs.mbOutline) + return false; + if (mbShadow != rhs.mbShadow) + return false; + return true; +} + ApiFontUsedFlags::ApiFontUsedFlags( bool bAllUsed ) : mbNameUsed( bAllUsed ), mbColorUsed( bAllUsed ), @@ -1442,6 +1473,17 @@ void BorderLineModel::setBiffStyle( sal_Int32 nLineStyle ) mnStyle = STATIC_ARRAY_SELECT( spnStyleIds, nLineStyle, XML_none ); } +bool BorderLineModel::operator==(const BorderLineModel& rhs) const +{ + if (maColor != rhs.maColor) + return false; + if (mnStyle != rhs.mnStyle) + return false; + if (mbUsed != rhs.mbUsed) + return false; + return true; +} + BorderModel::BorderModel( bool bDxf ) : maLeft( bDxf ), maRight( bDxf ), @@ -1699,6 +1741,25 @@ void PatternFillModel::setBiffPattern( sal_Int32 nPattern ) mnPattern = STATIC_ARRAY_SELECT( spnPatternIds, nPattern, XML_none ); } +bool PatternFillModel::operator==(const PatternFillModel& rhs) const +{ + if (maPatternColor != rhs.maPatternColor) + return false; + if (maFilterPatternColor != rhs.maFilterPatternColor) + return false; + if (maFillColor != rhs.maFillColor) + return false; + if (mnPattern != rhs.mnPattern) + return false; + if (mbPattColorUsed != rhs.mbPattColorUsed) + return false; + if (mbFillColorUsed != rhs.mbFillColorUsed) + return false; + if (mbPatternUsed != rhs.mbPatternUsed) + return false; + return true; +} + GradientFillModel::GradientFillModel() : mnType( XML_linear ), mfAngle( 0.0 ),