vcl/CppunitTest_vcl_textlayout.mk | 1 vcl/inc/textlayout.hxx | 10 ++--- vcl/qa/cppunit/textlayout.cxx | 64 +++++++++++++++++++++++++++++++++++--- vcl/source/text/textlayout.cxx | 61 +++++++++++++++++------------------- 4 files changed, 96 insertions(+), 40 deletions(-)
New commits: commit 938d3b35b83093de4e310d32de5137f6bdbcf22b Author: Chris Sherlock <chris.sherloc...@gmail.com> AuthorDate: Sun Oct 1 18:08:49 2023 +1100 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Thu Dec 28 02:33:50 2023 +0100 vcl: test BreakLinesWithIterator with hyphens Change-Id: Ied5e688b9eec19c2f1b3d1289cc4a6605703c3e4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/157904 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> diff --git a/vcl/CppunitTest_vcl_textlayout.mk b/vcl/CppunitTest_vcl_textlayout.mk index d7ac49a9e6df..57d5177f2a19 100644 --- a/vcl/CppunitTest_vcl_textlayout.mk +++ b/vcl/CppunitTest_vcl_textlayout.mk @@ -55,6 +55,7 @@ $(eval $(call gb_CppunitTest_use_components,vcl_textlayout,\ configmgr/source/configmgr \ i18npool/util/i18npool \ ucb/source/core/ucb1 \ + linguistic/source/lng \ )) $(eval $(call gb_CppunitTest_use_configuration,vcl_textlayout)) diff --git a/vcl/inc/textlayout.hxx b/vcl/inc/textlayout.hxx index 58f3bb33469b..d05259475d18 100644 --- a/vcl/inc/textlayout.hxx +++ b/vcl/inc/textlayout.hxx @@ -52,14 +52,14 @@ namespace vcl public: OUString GetEllipsisString(OUString const& rOrigStr, tools::Long nMaxWidth, DrawTextFlags nStyle); - sal_Int32 BreakLinesWithIterator(const tools::Long nWidth, OUString const& rStr, + std::tuple<sal_Int32, sal_Int32> BreakLines(const tools::Long nWidth, OUString const& rStr, css::uno::Reference< css::linguistic2::XHyphenator > const& xHyph, - css::uno::Reference<css::i18n::XBreakIterator> const& xBI, - const bool bHyphenate, + css::uno::Reference<css::i18n::XBreakIterator>& xBI, + const bool bHyphenate, const tools::Long nOrigLineWidth, const sal_Int32 nPos, const sal_Int32 nLen); - sal_Int32 BreakLinesSimple(const tools::Long nWidth, OUString const& rStr, - const sal_Int32 nPos, sal_Int32 nBreakPos, tools::Long& nLineWidth); + std::tuple<sal_Int32, sal_Int32> BreakLinesSimple(const tools::Long nWidth, OUString const& rStr, + const sal_Int32 nPos, sal_Int32 nBreakPos, const tools::Long nOrigLineWidth); tools::Long GetTextLines(tools::Rectangle const& rRect, const tools::Long nTextHeight, ImplMultiTextLineInfo& rLineInfo, diff --git a/vcl/qa/cppunit/textlayout.cxx b/vcl/qa/cppunit/textlayout.cxx index 147826f19842..3ace6a8b836d 100644 --- a/vcl/qa/cppunit/textlayout.cxx +++ b/vcl/qa/cppunit/textlayout.cxx @@ -12,6 +12,8 @@ #include <test/bootstrapfixture.hxx> +#include <comphelper/processfactory.hxx> + #include <vcl/unohelp.hxx> #include <vcl/virdev.hxx> @@ -28,7 +30,7 @@ public: #if HAVE_MORE_FONTS -CPPUNIT_TEST_FIXTURE(VclTextLayoutTest, testBreakLinesWithIterator_invalid_softbreak) +CPPUNIT_TEST_FIXTURE(VclTextLayoutTest, testBreakLines_invalid_softbreak) { ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA); device->SetOutputSizePixel(Size(1000, 1000)); @@ -46,9 +48,63 @@ CPPUNIT_TEST_FIXTURE(VclTextLayoutTest, testBreakLinesWithIterator_invalid_softb const auto nTextLen = 13; - CPPUNIT_ASSERT_EQUAL( - static_cast<sal_Int32>(13), - aTextLayout.BreakLinesWithIterator(nTextWidth, sTestStr, xHyph, xBI, false, nTextLen, 15)); + auto[nBreakPos, nLineWidth] + = aTextLayout.BreakLines(nTextWidth, sTestStr, xHyph, xBI, false, nTextWidth, nTextLen, 15); + + const sal_Int32 nExpectedBreakPos = 13; + CPPUNIT_ASSERT_EQUAL(nExpectedBreakPos, nBreakPos); +} + +CPPUNIT_TEST_FIXTURE(VclTextLayoutTest, testBreakLines_hyphens) +{ + ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA); + device->SetOutputSizePixel(Size(1000, 1000)); + device->SetFont(vcl::Font("DejaVu Sans", "Book", Size(0, 11))); + + vcl::DefaultTextLayout aTextLayout(*device); + + const OUString sTestStr = u"textline text-moretext"_ustr; + const auto nTextWidth = device->GetTextWidth("textline text-moretex"); + + css::uno::Reference<css::uno::XComponentContext> xContext( + comphelper::getProcessComponentContext()); + css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLinguMgr + = css::linguistic2::LinguServiceManager::create(xContext); + + css::uno::Reference<css::linguistic2::XHyphenator> xHyph = xLinguMgr->getHyphenator(); + css::uno::Reference<css::i18n::XBreakIterator> xBI = vcl::unohelper::CreateBreakIterator(); + + auto[nBreakPos, nLineWidth] + = aTextLayout.BreakLines(nTextWidth, sTestStr, xHyph, xBI, true, nTextWidth, 13, 12); + + const sal_Int32 nExpectedBreakPos = 13; + CPPUNIT_ASSERT_EQUAL(nExpectedBreakPos, nBreakPos); +} + +CPPUNIT_TEST_FIXTURE(VclTextLayoutTest, testBreakLines_hyphen_word_under_two_chars) +{ + ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA); + device->SetOutputSizePixel(Size(1000, 1000)); + device->SetFont(vcl::Font("DejaVu Sans", "Book", Size(0, 11))); + + vcl::DefaultTextLayout aTextLayout(*device); + + const OUString sTestStr = u"textline text-moretext"_ustr; + const auto nTextWidth = device->GetTextWidth("te-moretex"); + + css::uno::Reference<css::uno::XComponentContext> xContext( + comphelper::getProcessComponentContext()); + css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLinguMgr + = css::linguistic2::LinguServiceManager::create(xContext); + + css::uno::Reference<css::linguistic2::XHyphenator> xHyph = xLinguMgr->getHyphenator(); + css::uno::Reference<css::i18n::XBreakIterator> xBI = vcl::unohelper::CreateBreakIterator(); + + auto[nBreakPos, nLineWidth] + = aTextLayout.BreakLines(nTextWidth, sTestStr, xHyph, xBI, true, nTextWidth, 2, 10); + + const sal_Int32 nExpectedBreakPos = 2; + CPPUNIT_ASSERT_EQUAL(nExpectedBreakPos, nBreakPos); } #endif diff --git a/vcl/source/text/textlayout.cxx b/vcl/source/text/textlayout.cxx index 095c087d5377..e90c56b6355a 100644 --- a/vcl/source/text/textlayout.cxx +++ b/vcl/source/text/textlayout.cxx @@ -218,12 +218,18 @@ namespace vcl return aStr; } - sal_Int32 TextLayoutCommon::BreakLinesWithIterator(const tools::Long nWidth, OUString const& rStr, + std::tuple<sal_Int32, sal_Int32> TextLayoutCommon::BreakLines(const tools::Long nWidth, OUString const& rStr, css::uno::Reference< css::linguistic2::XHyphenator > const& xHyph, - css::uno::Reference<css::i18n::XBreakIterator> const& xBI, - const bool bHyphenate, + css::uno::Reference<css::i18n::XBreakIterator>& xBI, + const bool bHyphenate, const tools::Long nOrigLineWidth, const sal_Int32 nPos, const sal_Int32 nLen) { + if (!xBI.is()) + xBI = vcl::unohelper::CreateBreakIterator(); + + if (!xBI.is()) + return BreakLinesSimple(nWidth, rStr, nPos, nLen, nOrigLineWidth); + const css::lang::Locale& rDefLocale(Application::GetSettings().GetUILanguageTag().getLocale()); sal_Int32 nSoftBreak = GetTextBreak(rStr, nWidth, nPos, nLen - nPos); @@ -241,16 +247,14 @@ namespace vcl if (nBreakPos <= nPos) nBreakPos = nSoftBreak; - if (!bHyphenate) - return nBreakPos; + if (!bHyphenate || !xHyph.is()) + return { nBreakPos, GetTextWidth(rStr, nPos, nBreakPos - nPos) }; // Whether hyphen or not: Put the word after the hyphen through // the word boundary. // 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; @@ -258,27 +262,28 @@ namespace vcl SAL_WARN_IF(nWordEnd <= nWordStart, "vcl", "Start >= End?"); sal_Int32 nWordLen = nWordEnd - nWordStart; - if ( ( nWordEnd < nSoftBreak ) || ( nWordLen <= 3 ) ) - return nBreakPos; + if ((nWordEnd < nSoftBreak) || (nWordLen <= 3)) + return { nBreakPos, GetTextWidth(rStr, nPos, nBreakPos - nPos) }; 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; + return { nBreakPos, GetTextWidth(rStr, nPos, nBreakPos - nPos) }; bool bAlternate = xHyphWord->isAlternativeSpelling(); sal_Int32 _nWordLen = 1 + xHyphWord->getHyphenPos(); - if ( ( _nWordLen < 2 ) || ( (nWordStart+_nWordLen) < 2 ) ) - return nBreakPos; + if ((_nWordLen < 2 ) || ( (nWordStart + _nWordLen) < 2)) + return { nBreakPos, GetTextWidth(rStr, nPos, nBreakPos - nPos) }; - if ( bAlternate ) + if (bAlternate) { nBreakPos = nWordStart + _nWordLen; - return nBreakPos; + return { nBreakPos, GetTextWidth(rStr, nPos, nBreakPos - nPos) }; } OUString aAlt( xHyphWord->getHyphenatedWord() ); @@ -329,14 +334,18 @@ namespace vcl nBreakPos = nWordStart + nTxtStart; if ( cAlternateReplChar ) nBreakPos++; - return nBreakPos; + + return { nBreakPos, GetTextWidth(rStr, nPos, nBreakPos - nPos) }; } - sal_Int32 TextLayoutCommon::BreakLinesSimple(const tools::Long nWidth, OUString const& rStr, - const sal_Int32 nPos, sal_Int32 nBreakPos, tools::Long& nLineWidth) + std::tuple<sal_Int32, sal_Int32> TextLayoutCommon::BreakLinesSimple(const tools::Long nWidth, OUString const& rStr, + const sal_Int32 nPos, const sal_Int32 nLen, const tools::Long nOrigLineWidth) { + sal_Int32 nBreakPos = nLen; + tools::Long nLineWidth = nOrigLineWidth; sal_Int32 nSpacePos = rStr.getLength(); tools::Long nW = 0; + do { nSpacePos = rStr.lastIndexOf( ' ', nSpacePos ); @@ -355,7 +364,8 @@ namespace vcl if( nBreakPos < rStr.getLength()-1 ) nBreakPos++; } - return nBreakPos; + + return { nBreakPos, nLineWidth }; } namespace @@ -413,20 +423,9 @@ namespace vcl sal_Int32 nBreakPos = lcl_GetEndOfLine(rStr, nPos, nLen); tools::Long nLineWidth = GetTextWidth(rStr, nPos, nBreakPos-nPos); + if (lcl_ShouldBreakWord(nLineWidth, nWidth, nStyle)) - { - if (!xBI.is()) - xBI = vcl::unohelper::CreateBreakIterator(); - - if (xBI.is()) - { - nBreakPos = BreakLinesWithIterator(nWidth, rStr, xHyph, xBI, bHyphenate, nPos, nBreakPos); - nLineWidth = GetTextWidth(rStr, nPos, nBreakPos - nPos); - } - else - // fallback to something really simple - nBreakPos = BreakLinesSimple(nWidth, rStr, nPos, nBreakPos, nLineWidth); - } + std::tie(nBreakPos, nLineWidth) = BreakLines(nWidth, rStr, xHyph, xBI, bHyphenate, nLineWidth, nPos, nBreakPos); if ( nLineWidth > nMaxLineWidth ) nMaxLineWidth = nLineWidth;