diff options
author | Balazs Santha <santha.balazs@simonyi.bme.hu> | 2021-02-26 08:06:03 -0500 |
---|---|---|
committer | László Németh <nemeth@numbertext.org> | 2021-03-24 15:08:18 +0100 |
commit | e463d239555d3a4dc61797eeb8c638b6442112a3 (patch) | |
tree | d736577b07a0b5cf968b4c30b2fc5de7c6ddb72c /sw | |
parent | 4f3fb443cea4364b2ec8cb56515ec9bdfe6f5391 (diff) |
tdf#140731: sw transliteration: avoid too many redlines
As a workaround for the performance regression
from commit 2d3c77e9b10f20091ef338e262ba7756eb280ce9
(tdf#109266 sw change tracking: track transliteration),
switch off redlining to avoid ~freezing, if a single
transliteration could result too many (>~500) redlines.
A single transliteration creates n redlines
for n paragraphs of the selected text, except in
the case of transliterating to title case, where it
creates n redlines for n words. It's very easy
to freeze Writer, because Writer's slowing down with
n redlines is described by an O(n²) (quadratic) time
complexity. Eg. in an experiment, title casing
~660 words was 6 sec, but ~3000 words was 85 sec,
regarding to creating 660 vs 3000 redlines.
Note: this is a partial revert of commit
2d3c77e9b10f20091ef338e262ba7756eb280ce9, if the
selection contains more than 500 paragraphs (or in the
case transliterating to title case, ~500 words).
Change-Id: Iad98943cc9e1ed64aa9779e49ee3e941abad02ac
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111637
Tested-by: Jenkins
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
Diffstat (limited to 'sw')
-rw-r--r-- | sw/inc/ndtxt.hxx | 2 | ||||
-rw-r--r-- | sw/source/core/doc/DocumentContentOperationsManager.cxx | 55 | ||||
-rw-r--r-- | sw/source/core/txtnode/txtedt.cxx | 6 |
3 files changed, 54 insertions, 9 deletions
diff --git a/sw/inc/ndtxt.hxx b/sw/inc/ndtxt.hxx index 5dbbbc721f37..50986f3daca7 100644 --- a/sw/inc/ndtxt.hxx +++ b/sw/inc/ndtxt.hxx @@ -746,7 +746,7 @@ public: /// change text to Upper/Lower/Hiragana/Katakana/... void TransliterateText( utl::TransliterationWrapper& rTrans, sal_Int32 nStart, sal_Int32 nEnd, - SwUndoTransliterate* pUndo ); + SwUndoTransliterate* pUndo, bool bUseRedlining = false ); /// count words in given range - returns true if we refreshed out count bool CountWords( SwDocStat& rStat, sal_Int32 nStart, sal_Int32 nEnd ) const; diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx index b9846d852a2a..e66ed8e4a673 100644 --- a/sw/source/core/doc/DocumentContentOperationsManager.cxx +++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx @@ -74,6 +74,8 @@ #include <sal/log.hxx> #include <unotools/charclass.hxx> #include <unotools/configmgr.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <i18nutil/transliteration.hxx> #include <sfx2/Metadatable.hxx> #include <sot/exchange.hxx> #include <svl/stritem.hxx> @@ -2821,6 +2823,44 @@ void DocumentContentOperationsManager::TransliterateText( } } + bool bUseRedlining = m_rDoc.getIDocumentRedlineAccess().IsRedlineOn(); + // as a workaround for a known performance problem, switch off redlining + // to avoid freezing, if transliteration could result too many redlines + if ( bUseRedlining ) + { + const sal_uLong nMaxRedlines = 500; + const bool bIsTitleCase = rTrans.getType() == TransliterationFlags::TITLE_CASE; + sal_uLong nAffectedNodes = 0; + sal_uLong nAffectedChars = nEndCnt; + SwNodeIndex aIdx( pStt->nNode ); + for( ; aIdx.GetIndex() <= nEndNd; ++aIdx ) + { + SwTextNode* pAffectedNode = aIdx.GetNode().GetTextNode(); + + // don't count not text nodes or empty text nodes + if( !pAffectedNode || pAffectedNode->GetText().isEmpty() ) + continue; + + nAffectedNodes++; + + // count characters of the node (the last - maybe partially + // selected - node was counted at initialization of nAffectedChars) + if( aIdx.GetIndex() < nEndNd ) + nAffectedChars += pAffectedNode->GetText().getLength(); + + // transliteration creates n redlines for n nodes, except in the + // case of title case, where it creates n redlines for n words + if( nAffectedNodes > nMaxRedlines || + // estimate word count based on the character count, where + // 6 = average English word length is ~5 letters + space + ( bIsTitleCase && (nAffectedChars - nSttCnt)/6 > nMaxRedlines ) ) + { + bUseRedlining = false; + break; + } + } + } + if( nSttNd != nEndNd ) // is more than one text node involved? { // iterate over all effected text nodes, the first and the last one @@ -2831,8 +2871,10 @@ void DocumentContentOperationsManager::TransliterateText( { ++aIdx; if( pTNd ) + { pTNd->TransliterateText( - rTrans, nSttCnt, pTNd->GetText().getLength(), pUndo.get()); + rTrans, nSttCnt, pTNd->GetText().getLength(), pUndo.get(), bUseRedlining); + } } for( ; aIdx.GetIndex() < nEndNd; ++aIdx ) @@ -2841,16 +2883,19 @@ void DocumentContentOperationsManager::TransliterateText( if (pTNd) { pTNd->TransliterateText( - rTrans, 0, pTNd->GetText().getLength(), pUndo.get()); + rTrans, 0, pTNd->GetText().getLength(), pUndo.get(), bUseRedlining); } } if( nEndCnt && nullptr != ( pTNd = pEnd->nNode.GetNode().GetTextNode() )) - pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo.get() ); + { + pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo.get(), bUseRedlining ); + } } else if( pTNd && nSttCnt < nEndCnt ) - pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo.get() ); - + { + pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo.get(), bUseRedlining ); + } if( pUndo && pUndo->HasData() ) { m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo)); diff --git a/sw/source/core/txtnode/txtedt.cxx b/sw/source/core/txtnode/txtedt.cxx index ba25c5fccba1..e734e471060b 100644 --- a/sw/source/core/txtnode/txtedt.cxx +++ b/sw/source/core/txtnode/txtedt.cxx @@ -1684,7 +1684,7 @@ namespace void SwTextNode::TransliterateText( utl::TransliterationWrapper& rTrans, sal_Int32 nStt, sal_Int32 nEnd, - SwUndoTransliterate* pUndo ) + SwUndoTransliterate* pUndo, bool bUseRedlining ) { if (nStt >= nEnd) return; @@ -1908,7 +1908,7 @@ void SwTextNode::TransliterateText( // now apply the changes from end to start to leave the offsets of the // yet unchanged text parts remain the same. size_t nSum(0); - bool bIsRedlineOn(GetDoc().getIDocumentRedlineAccess().IsRedlineOn()); + for (size_t i = 0; i < aChanges.size(); ++i) { // check this here since AddChanges cannot be moved below // call to ReplaceTextOnly @@ -1922,7 +1922,7 @@ void SwTextNode::TransliterateText( return; } - if ( bIsRedlineOn ) + if ( bUseRedlining ) { // create SwPaM with mark & point spanning the attributed text //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different |