vcl: test BreakLinesWithIterator with hyphens

Change-Id: Ied5e688b9eec19c2f1b3d1289cc4a6605703c3e4
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/157904
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
This commit is contained in:
Chris Sherlock 2023-10-01 18:08:49 +11:00 committed by Tomaž Vajngerl
parent 9157281deb
commit 938d3b35b8
4 changed files with 96 additions and 40 deletions

View file

@ -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))

View file

@ -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,

View file

@ -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

View file

@ -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);
}
if (lcl_ShouldBreakWord(nLineWidth, nWidth, nStyle))
std::tie(nBreakPos, nLineWidth) = BreakLines(nWidth, rStr, xHyph, xBI, bHyphenate, nLineWidth, nPos, nBreakPos);
if ( nLineWidth > nMaxLineWidth )
nMaxLineWidth = nLineWidth;