diff options
author | Noel Grandin <noel.grandin@collabora.co.uk> | 2021-10-26 09:15:17 +0200 |
---|---|---|
committer | Noel Grandin <noel.grandin@collabora.co.uk> | 2021-10-26 10:31:44 +0200 |
commit | 4fc1b3fb659be916167518b49ffe8193e9033f30 (patch) | |
tree | b3e8dae82f9546d46352c903987b0b8710d5c7a4 | |
parent | 9142f66ec1c78292dd2b8863c622dfb705dd2db2 (diff) |
flatten ImplGetTextLines
Change-Id: Iefbbcb186b2811748abf49546351a2c68e8af462
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/124155
Tested-by: Noel Grandin <noel.grandin@collabora.co.uk>
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
-rw-r--r-- | include/vcl/outdev.hxx | 17 | ||||
-rw-r--r-- | vcl/source/outdev/text.cxx | 358 |
2 files changed, 202 insertions, 173 deletions
diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx index 6f85e0d51fef..ce988ab60719 100644 --- a/include/vcl/outdev.hxx +++ b/include/vcl/outdev.hxx @@ -130,6 +130,12 @@ namespace com::sun::star::rendering { class XCanvas; class XSpriteCanvas; } +namespace com::sun::star::linguistic2 { + class XHyphenator; +} +namespace com::sun::star::i18n { + class XBreakIterator; +} #if defined UNX #define GLYPH_FONT_HEIGHT 128 @@ -1076,7 +1082,16 @@ protected: SAL_DLLPRIVATE void ImplInitTextLineSize(); SAL_DLLPRIVATE void ImplInitAboveTextLineSize(); static - SAL_DLLPRIVATE tools::Long ImplGetTextLines( ImplMultiTextLineInfo& rLineInfo, tools::Long nWidth, const OUString& rStr, DrawTextFlags nStyle, const vcl::ITextLayout& _rLayout ); + SAL_DLLPRIVATE tools::Long ImplGetTextLines( ImplMultiTextLineInfo& rLineInfo, tools::Long nWidth, const OUString& rStr, DrawTextFlags nStyle, const vcl::ITextLayout& _rLayout ); + static + SAL_DLLPRIVATE sal_Int32 ImplBreakLinesWithIterator(const tools::Long nWidth, const OUString& rStr, const vcl::ITextLayout& _rLayout, + const css::uno::Reference<css::linguistic2::XHyphenator>& xHyph, + const css::uno::Reference<css::i18n::XBreakIterator>& xBI, + const bool bHyphenate, + const sal_Int32 nPos, sal_Int32 nBreakPos); + static + SAL_DLLPRIVATE sal_Int32 ImplBreakLinesSimple( const tools::Long nWidth, const OUString& rStr, + const vcl::ITextLayout& _rLayout, const sal_Int32 nPos, sal_Int32 nBreakPos, tools::Long& nLineWidth ); SAL_DLLPRIVATE float approximate_char_width() const; virtual bool shouldDrawWavePixelAsRect(tools::Long nLineWidth) const; diff --git a/vcl/source/outdev/text.cxx b/vcl/source/outdev/text.cxx index 0b37b8fc743f..58e0b0e6a8b9 100644 --- a/vcl/source/outdev/text.cxx +++ b/vcl/source/outdev/text.cxx @@ -487,191 +487,62 @@ tools::Long OutputDevice::ImplGetTextLines( ImplMultiTextLineInfo& rLineInfo, if ( nWidth <= 0 ) nWidth = 1; - tools::Long nMaxLineWidth = 0; rLineInfo.Clear(); - if (!rStr.isEmpty()) - { - const bool bHyphenate = (nStyle & DrawTextFlags::WordBreakHyphenation) == DrawTextFlags::WordBreakHyphenation; - css::uno::Reference< css::linguistic2::XHyphenator > xHyph; - if (bHyphenate) - { - // get service provider - css::uno::Reference<css::uno::XComponentContext> xContext(comphelper::getProcessComponentContext()); - css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLinguMgr = css::linguistic2::LinguServiceManager::create(xContext); - xHyph = xLinguMgr->getHyphenator(); - } - - css::uno::Reference<css::i18n::XBreakIterator> xBI; - sal_Int32 nPos = 0; - sal_Int32 nLen = rStr.getLength(); - while ( nPos < nLen ) - { - sal_Int32 nBreakPos = nPos; - - while ( ( nBreakPos < nLen ) && ( rStr[ nBreakPos ] != '\r' ) && ( rStr[ nBreakPos ] != '\n' ) ) - nBreakPos++; - - tools::Long nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos ); - if ( ( nLineWidth > nWidth ) && ( nStyle & DrawTextFlags::WordBreak ) ) - { - if ( !xBI.is() ) - xBI = vcl::unohelper::CreateBreakIterator(); + if (rStr.isEmpty()) + return 0; - if ( xBI.is() ) - { - const css::lang::Locale& rDefLocale(Application::GetSettings().GetUILanguageTag().getLocale()); - sal_Int32 nSoftBreak = _rLayout.GetTextBreak( rStr, nWidth, nPos, nBreakPos - nPos ); - if (nSoftBreak == -1) - { - nSoftBreak = nPos; - } - SAL_WARN_IF( nSoftBreak >= nBreakPos, "vcl", "Break?!" ); - css::i18n::LineBreakHyphenationOptions aHyphOptions( xHyph, css::uno::Sequence <css::beans::PropertyValue>(), 1 ); - css::i18n::LineBreakUserOptions aUserOptions; - css::i18n::LineBreakResults aLBR = xBI->getLineBreak( rStr, nSoftBreak, rDefLocale, nPos, aHyphOptions, aUserOptions ); - nBreakPos = aLBR.breakIndex; - if ( nBreakPos <= nPos ) - nBreakPos = nSoftBreak; - if ( bHyphenate ) - { - // Whether hyphen or not: Put the word after the hyphen through - // word boundary. + tools::Long nMaxLineWidth = 0; + const bool bHyphenate = (nStyle & DrawTextFlags::WordBreakHyphenation) == DrawTextFlags::WordBreakHyphenation; + css::uno::Reference< css::linguistic2::XHyphenator > xHyph; + if (bHyphenate) + { + // get service provider + css::uno::Reference<css::uno::XComponentContext> xContext(comphelper::getProcessComponentContext()); + css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLinguMgr = css::linguistic2::LinguServiceManager::create(xContext); + xHyph = xLinguMgr->getHyphenator(); + } - // nMaxBreakPos the last char that fits into the line - // nBreakPos is the word's start + css::uno::Reference<css::i18n::XBreakIterator> xBI; + sal_Int32 nPos = 0; + sal_Int32 nLen = rStr.getLength(); + while ( nPos < nLen ) + { + sal_Int32 nBreakPos = nPos; - // We run into a problem if the doc is so narrow, that a word - // is broken into more than two lines ... - if ( xHyph.is() ) - { - css::i18n::Boundary aBoundary = xBI->getWordBoundary( rStr, nBreakPos, rDefLocale, css::i18n::WordType::DICTIONARY_WORD, true ); - sal_Int32 nWordStart = nPos; - sal_Int32 nWordEnd = aBoundary.endPos; - SAL_WARN_IF( nWordEnd <= nWordStart, "vcl", "ImpBreakLine: Start >= End?" ); + while ( ( nBreakPos < nLen ) && ( rStr[ nBreakPos ] != '\r' ) && ( rStr[ nBreakPos ] != '\n' ) ) + nBreakPos++; - sal_Int32 nWordLen = nWordEnd - nWordStart; - if ( ( nWordEnd >= nSoftBreak ) && ( nWordLen > 3 ) ) - { - // #104415# May happen, because getLineBreak may differ from getWordBoundary with DICTIONARY_WORD - // SAL_WARN_IF( nWordEnd < nMaxBreakPos, "vcl", "Hyph: Break?" ); - OUString aWord = rStr.copy( nWordStart, nWordLen ); - sal_Int32 nMinTrail = nWordEnd-nSoftBreak+1; //+1: Before the "broken off" char - css::uno::Reference< css::linguistic2::XHyphenatedWord > xHyphWord; - if (xHyph.is()) - xHyphWord = xHyph->hyphenate( aWord, rDefLocale, aWord.getLength() - nMinTrail, css::uno::Sequence< css::beans::PropertyValue >() ); - if (xHyphWord.is()) - { - bool bAlternate = xHyphWord->isAlternativeSpelling(); - sal_Int32 _nWordLen = 1 + xHyphWord->getHyphenPos(); - - if ( ( _nWordLen >= 2 ) && ( (nWordStart+_nWordLen) >= 2 ) ) - { - if ( !bAlternate ) - { - nBreakPos = nWordStart + _nWordLen; - } - else - { - OUString aAlt( xHyphWord->getHyphenatedWord() ); - - // We can have two cases: - // 1) "packen" turns into "pak-ken" - // 2) "Schiffahrt" turns into "Schiff-fahrt" - - // In case 1 we need to replace a char - // In case 2 we add a char - - // Correct recognition is made harder by words such as - // "Schiffahrtsbrennesseln", as the Hyphenator splits all - // positions of the word and comes up with "Schifffahrtsbrennnesseln" - // Thus, we cannot infer the aWord from the AlternativeWord's - // index. - // TODO: The whole junk will be made easier by a function in - // the Hyphenator, as soon as AMA adds it. - sal_Int32 nAltStart = _nWordLen - 1; - sal_Int32 nTxtStart = nAltStart - (aAlt.getLength() - aWord.getLength()); - sal_Int32 nTxtEnd = nTxtStart; - sal_Int32 nAltEnd = nAltStart; - - // The area between nStart and nEnd is the difference - // between AlternativeString and OriginalString - while( nTxtEnd < aWord.getLength() && nAltEnd < aAlt.getLength() && - aWord[nTxtEnd] != aAlt[nAltEnd] ) - { - ++nTxtEnd; - ++nAltEnd; - } - - // If a char was added, we notice it now: - if( nAltEnd > nTxtEnd && nAltStart == nAltEnd && - aWord[ nTxtEnd ] == aAlt[nAltEnd] ) - { - ++nAltEnd; - ++nTxtStart; - ++nTxtEnd; - } - - SAL_WARN_IF( ( nAltEnd - nAltStart ) != 1, "vcl", "Alternate: Wrong assumption!" ); - - sal_Unicode cAlternateReplChar = 0; - if ( nTxtEnd > nTxtStart ) - cAlternateReplChar = aAlt[ nAltStart ]; - - nBreakPos = nWordStart + nTxtStart; - if ( cAlternateReplChar ) - nBreakPos++; - } - } - } - } - } - } - nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos ); - } - else - { - // fallback to something really simple - sal_Int32 nSpacePos = rStr.getLength(); - tools::Long nW = 0; - do - { - nSpacePos = rStr.lastIndexOf( ' ', nSpacePos ); - if( nSpacePos != -1 ) - { - if( nSpacePos > nPos ) - nSpacePos--; - nW = _rLayout.GetTextWidth( rStr, nPos, nSpacePos-nPos ); - } - } while( nW > nWidth ); + tools::Long nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos ); + if ( ( nLineWidth > nWidth ) && ( nStyle & DrawTextFlags::WordBreak ) ) + { + if ( !xBI.is() ) + xBI = vcl::unohelper::CreateBreakIterator(); - if( nSpacePos != -1 ) - { - nBreakPos = nSpacePos; - nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos ); - if( nBreakPos < rStr.getLength()-1 ) - nBreakPos++; - } - } - } + if ( xBI.is() ) + nBreakPos = ImplBreakLinesWithIterator(nWidth, rStr, _rLayout, xHyph, xBI, bHyphenate, nPos, nBreakPos); + else + // fallback to something really simple + nBreakPos = ImplBreakLinesSimple(nWidth, rStr, _rLayout, nPos, nBreakPos, nLineWidth); + } - if ( nLineWidth > nMaxLineWidth ) - nMaxLineWidth = nLineWidth; + if ( nLineWidth > nMaxLineWidth ) + nMaxLineWidth = nLineWidth; - rLineInfo.AddLine( ImplTextLineInfo( nLineWidth, nPos, nBreakPos-nPos ) ); + rLineInfo.AddLine( ImplTextLineInfo( nLineWidth, nPos, nBreakPos-nPos ) ); - if ( nBreakPos == nPos ) - nBreakPos++; - nPos = nBreakPos; + if ( nBreakPos == nPos ) + nBreakPos++; + nPos = nBreakPos; - if ( nPos < nLen && ( ( rStr[ nPos ] == '\r' ) || ( rStr[ nPos ] == '\n' ) ) ) - { + if ( nPos < nLen && ( ( rStr[ nPos ] == '\r' ) || ( rStr[ nPos ] == '\n' ) ) ) + { + nPos++; + // CR/LF? + if ( ( nPos < nLen ) && ( rStr[ nPos ] == '\n' ) && ( rStr[ nPos-1 ] == '\r' ) ) nPos++; - // CR/LF? - if ( ( nPos < nLen ) && ( rStr[ nPos ] == '\n' ) && ( rStr[ nPos-1 ] == '\r' ) ) - nPos++; - } } } + #ifdef DBG_UTIL for ( sal_Int32 nL = 0; nL < rLineInfo.Count(); nL++ ) { @@ -685,6 +556,149 @@ tools::Long OutputDevice::ImplGetTextLines( ImplMultiTextLineInfo& rLineInfo, return nMaxLineWidth; } +sal_Int32 OutputDevice::ImplBreakLinesWithIterator(const tools::Long nWidth, const OUString& rStr, const vcl::ITextLayout& _rLayout, + const css::uno::Reference< css::linguistic2::XHyphenator >& xHyph, + const css::uno::Reference<css::i18n::XBreakIterator>& xBI, + const bool bHyphenate, + const sal_Int32 nPos, sal_Int32 nBreakPos) +{ + const css::lang::Locale& rDefLocale(Application::GetSettings().GetUILanguageTag().getLocale()); + sal_Int32 nSoftBreak = _rLayout.GetTextBreak( rStr, nWidth, nPos, nBreakPos - nPos ); + if (nSoftBreak == -1) + { + nSoftBreak = nPos; + } + SAL_WARN_IF( nSoftBreak >= nBreakPos, "vcl", "Break?!" ); + css::i18n::LineBreakHyphenationOptions aHyphOptions( xHyph, css::uno::Sequence <css::beans::PropertyValue>(), 1 ); + css::i18n::LineBreakUserOptions aUserOptions; + css::i18n::LineBreakResults aLBR = xBI->getLineBreak( rStr, nSoftBreak, rDefLocale, nPos, aHyphOptions, aUserOptions ); + nBreakPos = aLBR.breakIndex; + if ( nBreakPos <= nPos ) + nBreakPos = nSoftBreak; + if ( !bHyphenate ) + return nBreakPos; + + // Whether hyphen or not: Put the word after the hyphen through + // word boundary. + + // nMaxBreakPos the last char that fits into the line + // nBreakPos is the word's start + + // We run into a problem if the doc is so narrow, that a word + // is broken into more than two lines ... + if ( !xHyph.is() ) + return nBreakPos; + + css::i18n::Boundary aBoundary = xBI->getWordBoundary( rStr, nBreakPos, rDefLocale, css::i18n::WordType::DICTIONARY_WORD, true ); + sal_Int32 nWordStart = nPos; + sal_Int32 nWordEnd = aBoundary.endPos; + SAL_WARN_IF( nWordEnd <= nWordStart, "vcl", "ImpBreakLine: Start >= End?" ); + + sal_Int32 nWordLen = nWordEnd - nWordStart; + if ( ( nWordEnd < nSoftBreak ) || ( nWordLen <= 3 ) ) + return nBreakPos; + + // #104415# May happen, because getLineBreak may differ from getWordBoundary with DICTIONARY_WORD + // SAL_WARN_IF( nWordEnd < nMaxBreakPos, "vcl", "Hyph: Break?" ); + OUString aWord = rStr.copy( nWordStart, nWordLen ); + sal_Int32 nMinTrail = nWordEnd-nSoftBreak+1; //+1: Before the "broken off" char + css::uno::Reference< css::linguistic2::XHyphenatedWord > xHyphWord; + if (xHyph.is()) + xHyphWord = xHyph->hyphenate( aWord, rDefLocale, aWord.getLength() - nMinTrail, css::uno::Sequence< css::beans::PropertyValue >() ); + if (!xHyphWord.is()) + return nBreakPos; + + bool bAlternate = xHyphWord->isAlternativeSpelling(); + sal_Int32 _nWordLen = 1 + xHyphWord->getHyphenPos(); + + if ( ( _nWordLen < 2 ) || ( (nWordStart+_nWordLen) < 2 ) ) + return nBreakPos; + + if ( bAlternate ) + { + nBreakPos = nWordStart + _nWordLen; + return nBreakPos; + } + + + OUString aAlt( xHyphWord->getHyphenatedWord() ); + + // We can have two cases: + // 1) "packen" turns into "pak-ken" + // 2) "Schiffahrt" turns into "Schiff-fahrt" + + // In case 1 we need to replace a char + // In case 2 we add a char + + // Correct recognition is made harder by words such as + // "Schiffahrtsbrennesseln", as the Hyphenator splits all + // positions of the word and comes up with "Schifffahrtsbrennnesseln" + // Thus, we cannot infer the aWord from the AlternativeWord's + // index. + // TODO: The whole junk will be made easier by a function in + // the Hyphenator, as soon as AMA adds it. + sal_Int32 nAltStart = _nWordLen - 1; + sal_Int32 nTxtStart = nAltStart - (aAlt.getLength() - aWord.getLength()); + sal_Int32 nTxtEnd = nTxtStart; + sal_Int32 nAltEnd = nAltStart; + + // The area between nStart and nEnd is the difference + // between AlternativeString and OriginalString + while( nTxtEnd < aWord.getLength() && nAltEnd < aAlt.getLength() && + aWord[nTxtEnd] != aAlt[nAltEnd] ) + { + ++nTxtEnd; + ++nAltEnd; + } + + // If a char was added, we notice it now: + if( nAltEnd > nTxtEnd && nAltStart == nAltEnd && + aWord[ nTxtEnd ] == aAlt[nAltEnd] ) + { + ++nAltEnd; + ++nTxtStart; + ++nTxtEnd; + } + + SAL_WARN_IF( ( nAltEnd - nAltStart ) != 1, "vcl", "Alternate: Wrong assumption!" ); + + sal_Unicode cAlternateReplChar = 0; + if ( nTxtEnd > nTxtStart ) + cAlternateReplChar = aAlt[ nAltStart ]; + + nBreakPos = nWordStart + nTxtStart; + if ( cAlternateReplChar ) + nBreakPos++; + return nBreakPos; +} + +sal_Int32 OutputDevice::ImplBreakLinesSimple( const tools::Long nWidth, const OUString& rStr, + const vcl::ITextLayout& _rLayout, const sal_Int32 nPos, sal_Int32 nBreakPos, tools::Long& nLineWidth ) +{ + sal_Int32 nSpacePos = rStr.getLength(); + tools::Long nW = 0; + do + { + nSpacePos = rStr.lastIndexOf( ' ', nSpacePos ); + if( nSpacePos != -1 ) + { + if( nSpacePos > nPos ) + nSpacePos--; + nW = _rLayout.GetTextWidth( rStr, nPos, nSpacePos-nPos ); + } + } while( nW > nWidth ); + + if( nSpacePos != -1 ) + { + nBreakPos = nSpacePos; + nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos ); + if( nBreakPos < rStr.getLength()-1 ) + nBreakPos++; + } + return nBreakPos; +} + + void OutputDevice::SetTextColor( const Color& rColor ) { |