diff options
author | Noel Grandin <noel.grandin@collabora.co.uk> | 2018-04-04 11:17:15 +0200 |
---|---|---|
committer | Noel Grandin <noel.grandin@collabora.co.uk> | 2018-04-04 13:28:52 +0200 |
commit | e278df1a14c5cb5dbb7add5d6ed5dd52da131e92 (patch) | |
tree | 10ca8af6f78a576fb9c11ffbeffd02a30e3fb5c6 | |
parent | 891e41fac81fbd8d5cdb277b26639abfd25a7143 (diff) |
tdf#108608 Draw file unresponsive on large text pasted into textbox
We have O(n^2) algorithm here.
The stack trace looks like:
ImpEditEngine::CalcTextWidth
EditEngine::CalcTextWidth
Outliner::CalcTextSize
SvxOutlinerForwarder::GetParaBounds
SvxAccessibleTextAdapter::GetParaBounds
accessibility::AccessibleEditableTextPara::getBounds
...
OutlinerView::PasteSpecial
where AccessibleEditableTextPara::getBounds iterates over all
paragraphs, and so does ImpEditEngine::CalcTextWidth.
To solve this, push the logic down from
SvxOutlinerForwarder::GetParaBounds, to new logic in EditEngine and
ImpEditEngine, where we can optimise the width calculation.
Note that this means that the width returned for a specific paragraph is
no longer the maximum width of all paragraphs, lets hope that does not
cause regressions.
Change-Id: I9f879d9a67b16a4aec08915328c99961b7313c2f
Reviewed-on: https://gerrit.libreoffice.org/52369
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
-rw-r--r-- | editeng/source/editeng/editeng.cxx | 27 | ||||
-rw-r--r-- | editeng/source/editeng/impedit.hxx | 1 | ||||
-rw-r--r-- | editeng/source/editeng/impedit2.cxx | 78 | ||||
-rw-r--r-- | editeng/source/outliner/outlin2.cxx | 5 | ||||
-rw-r--r-- | editeng/source/uno/unoforou.cxx | 19 | ||||
-rw-r--r-- | include/editeng/editeng.hxx | 1 | ||||
-rw-r--r-- | include/editeng/outliner.hxx | 1 |
7 files changed, 80 insertions, 52 deletions
diff --git a/editeng/source/editeng/editeng.cxx b/editeng/source/editeng/editeng.cxx index 6c6a0257993f..01bc8dadc7e2 100644 --- a/editeng/source/editeng/editeng.cxx +++ b/editeng/source/editeng/editeng.cxx @@ -609,14 +609,37 @@ sal_uInt32 EditEngine::GetLineHeight( sal_Int32 nParagraph ) return pImpEditEngine->GetLineHeight( nParagraph, 0 ); } -sal_uInt32 EditEngine::GetTextHeight( sal_Int32 nParagraph ) const +tools::Rectangle EditEngine::GetParaBounds( sal_Int32 nPara ) { + if ( !pImpEditEngine->IsFormatted() ) + pImpEditEngine->FormatDoc(); + + Point aPnt = GetDocPosTopLeft( nPara ); + + if( IsVertical() ) + { + sal_Int32 nTextHeight = pImpEditEngine->GetTextHeight(); + sal_Int32 nParaWidth = pImpEditEngine->CalcParaWidth( nPara, true ); + sal_uLong nParaHeight = pImpEditEngine->GetParaHeight( nPara ); + + return tools::Rectangle( nTextHeight - aPnt.Y() - nParaHeight, 0, nTextHeight - aPnt.Y(), nParaWidth ); + } + else + { + sal_Int32 nParaWidth = pImpEditEngine->CalcParaWidth( nPara, true ); + sal_uLong nParaHeight = pImpEditEngine->GetParaHeight( nPara ); + + return tools::Rectangle( 0, aPnt.Y(), nParaWidth, aPnt.Y() + nParaHeight ); + } +} +sal_uInt32 EditEngine::GetTextHeight( sal_Int32 nParagraph ) const +{ if ( !pImpEditEngine->IsFormatted() ) pImpEditEngine->FormatDoc(); sal_uInt32 nHeight = pImpEditEngine->GetParaHeight( nParagraph ); - return nHeight; + return nHeight; } OUString EditEngine::GetWord( sal_Int32 nPara, sal_Int32 nIndex ) diff --git a/editeng/source/editeng/impedit.hxx b/editeng/source/editeng/impedit.hxx index 864ba04e31ba..0d282605c428 100644 --- a/editeng/source/editeng/impedit.hxx +++ b/editeng/source/editeng/impedit.hxx @@ -834,6 +834,7 @@ public: sal_uInt32 GetTextHeight() const; sal_uInt32 GetTextHeightNTP() const; sal_uInt32 CalcTextWidth( bool bIgnoreExtraSpace ); + sal_uInt32 CalcParaWidth( sal_Int32 nParagraph, bool bIgnoreExtraSpace ); sal_uInt32 CalcLineWidth( ParaPortion* pPortion, EditLine* pLine, bool bIgnoreExtraSpace, bool bIgnoreTrailingWhiteSpaces = false ); sal_Int32 GetLineCount( sal_Int32 nParagraph ) const; sal_Int32 GetLineLen( sal_Int32 nParagraph, sal_Int32 nLine ) const; diff --git a/editeng/source/editeng/impedit2.cxx b/editeng/source/editeng/impedit2.cxx index 9d5658a68109..c58c76b4e8a5 100644 --- a/editeng/source/editeng/impedit2.cxx +++ b/editeng/source/editeng/impedit2.cxx @@ -3084,52 +3084,66 @@ sal_uInt32 ImpEditEngine::CalcTextWidth( bool bIgnoreExtraSpace ) if ( !IsFormatted() && !IsFormatting() ) FormatDoc(); - long nMaxWidth = 0; - long nCurWidth = 0; - + sal_uInt32 nMaxWidth = 0; // Over all the paragraphs ... sal_Int32 nParas = GetParaPortions().Count(); for ( sal_Int32 nPara = 0; nPara < nParas; nPara++ ) { - ParaPortion* pPortion = GetParaPortions()[nPara]; - if ( pPortion->IsVisible() ) - { - const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pPortion->GetNode() ); - sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pPortion->GetNode() ); + nMaxWidth = std::max(nMaxWidth, CalcParaWidth(nPara, bIgnoreExtraSpace)); + } + + return nMaxWidth; +} + +sal_uInt32 ImpEditEngine::CalcParaWidth( sal_Int32 nPara, bool bIgnoreExtraSpace ) +{ + // If still not formatted and not in the process. + // Will be brought in the formatting for AutoPageSize. + if ( !IsFormatted() && !IsFormatting() ) + FormatDoc(); + + long nMaxWidth = 0; + // Over all the paragraphs ... - // On the lines of the paragraph ... + ParaPortion* pPortion = GetParaPortions()[nPara]; + if ( pPortion->IsVisible() ) + { + const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pPortion->GetNode() ); + sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pPortion->GetNode() ); - sal_Int32 nLines = pPortion->GetLines().Count(); - for ( sal_Int32 nLine = 0; nLine < nLines; nLine++ ) + + // On the lines of the paragraph ... + + sal_Int32 nLines = pPortion->GetLines().Count(); + for ( sal_Int32 nLine = 0; nLine < nLines; nLine++ ) + { + EditLine& rLine = pPortion->GetLines()[nLine]; + // nCurWidth = pLine->GetStartPosX(); + // For Center- or Right- alignment it depends on the paper + // width, here not preferred. I general, it is best not leave it + // to StartPosX, also the right indents have to be taken into + // account! + long nCurWidth = GetXValue( rLRItem.GetTextLeft() + nSpaceBeforeAndMinLabelWidth ); + if ( nLine == 0 ) { - EditLine& rLine = pPortion->GetLines()[nLine]; - // nCurWidth = pLine->GetStartPosX(); - // For Center- or Right- alignment it depends on the paper - // width, here not preferred. I general, it is best not leave it - // to StartPosX, also the right indents have to be taken into - // account! - nCurWidth = GetXValue( rLRItem.GetTextLeft() + nSpaceBeforeAndMinLabelWidth ); - if ( nLine == 0 ) + long nFI = GetXValue( rLRItem.GetTextFirstLineOfst() ); + nCurWidth -= nFI; + if ( pPortion->GetBulletX() > nCurWidth ) { - long nFI = GetXValue( rLRItem.GetTextFirstLineOfst() ); - nCurWidth -= nFI; + nCurWidth += nFI; // LI? if ( pPortion->GetBulletX() > nCurWidth ) - { - nCurWidth += nFI; // LI? - if ( pPortion->GetBulletX() > nCurWidth ) - nCurWidth = pPortion->GetBulletX(); - } - } - nCurWidth += GetXValue( rLRItem.GetRight() ); - nCurWidth += CalcLineWidth( pPortion, &rLine, bIgnoreExtraSpace ); - if ( nCurWidth > nMaxWidth ) - { - nMaxWidth = nCurWidth; + nCurWidth = pPortion->GetBulletX(); } } + nCurWidth += GetXValue( rLRItem.GetRight() ); + nCurWidth += CalcLineWidth( pPortion, &rLine, bIgnoreExtraSpace ); + if ( nCurWidth > nMaxWidth ) + { + nMaxWidth = nCurWidth; + } } } diff --git a/editeng/source/outliner/outlin2.cxx b/editeng/source/outliner/outlin2.cxx index 78112d85dfac..96285309a635 100644 --- a/editeng/source/outliner/outlin2.cxx +++ b/editeng/source/outliner/outlin2.cxx @@ -419,6 +419,11 @@ sal_uLong Outliner::GetTextHeight( sal_Int32 nParagraph ) const return pEditEngine->GetTextHeight(nParagraph ); } +tools::Rectangle Outliner::GetParaBounds( sal_Int32 nParagraph ) const +{ + return pEditEngine->GetParaBounds(nParagraph ); +} + Point Outliner::GetDocPos( const Point& rPaperPos ) const { return pEditEngine->GetDocPos( rPaperPos ); diff --git a/editeng/source/uno/unoforou.cxx b/editeng/source/uno/unoforou.cxx index 04267165fe55..4abad85c0dff 100644 --- a/editeng/source/uno/unoforou.cxx +++ b/editeng/source/uno/unoforou.cxx @@ -338,24 +338,7 @@ tools::Rectangle SvxOutlinerForwarder::GetCharBounds( sal_Int32 nPara, sal_Int32 tools::Rectangle SvxOutlinerForwarder::GetParaBounds( sal_Int32 nPara ) const { - Point aPnt = rOutliner.GetDocPosTopLeft( nPara ); - Size aSize = rOutliner.CalcTextSize(); - - if( rOutliner.IsVertical() ) - { - // Hargl. Outliner's 'external' methods return the rotated - // dimensions, 'internal' methods like GetTextHeight( n ) - // don't rotate. - sal_uLong nWidth = rOutliner.GetTextHeight( nPara ); - - return tools::Rectangle( aSize.Width() - aPnt.Y() - nWidth, 0, aSize.Width() - aPnt.Y(), aSize.Height() ); - } - else - { - sal_uLong nHeight = rOutliner.GetTextHeight( nPara ); - - return tools::Rectangle( 0, aPnt.Y(), aSize.Width(), aPnt.Y() + nHeight ); - } + return rOutliner.GetParaBounds( nPara ); } MapMode SvxOutlinerForwarder::GetMapMode() const diff --git a/include/editeng/editeng.hxx b/include/editeng/editeng.hxx index 44663abcc28b..ee4eb7baeb31 100644 --- a/include/editeng/editeng.hxx +++ b/include/editeng/editeng.hxx @@ -286,6 +286,7 @@ public: void GetLineBoundaries( /*out*/sal_Int32& rStart, /*out*/sal_Int32& rEnd, sal_Int32 nParagraph, sal_Int32 nLine ) const; sal_Int32 GetLineNumberAtIndex( sal_Int32 nPara, sal_Int32 nIndex ) const; sal_uInt32 GetLineHeight( sal_Int32 nParagraph ); + tools::Rectangle GetParaBounds( sal_Int32 nPara ); ParagraphInfos GetParagraphInfos( sal_Int32 nPara ); sal_Int32 FindParagraph( long nDocPosY ); EPosition FindDocPosition( const Point& rDocPos ) const; diff --git a/include/editeng/outliner.hxx b/include/editeng/outliner.hxx index 5ef2a56ac05c..ee856b9e6b35 100644 --- a/include/editeng/outliner.hxx +++ b/include/editeng/outliner.hxx @@ -931,6 +931,7 @@ public: sal_uLong GetTextHeight() const; sal_uLong GetTextHeight( sal_Int32 nParagraph ) const; + tools::Rectangle GetParaBounds( sal_Int32 nParagraph ) const; Point GetDocPosTopLeft( sal_Int32 nParagraph ); Point GetDocPos( const Point& rPaperPos ) const; bool IsTextPos( const Point& rPaperPos, sal_uInt16 nBorder ); |