tdf#173389 sw: add compound-based “smart” hyphenation for readability
As a natural extension of the "smart" hyphenation, following traditional typography, prefer readability of the hyphenated compound words and better word spacing in the following way: * when it's possible to hyphenate the same compound word within the custom word spacing range, choose compound splitting or hyphenate just before the suffix or inside the suffix, not inside a compound constituent. * limit plain, i.e. non-compound-based smart hyphenation, when it can results in better word spacing without losing readability: do not disable hyphenation within the custom word spacing range, if the hyphenation splits the compound word between its constituents; * if also shrinking results in a non-compound splitting hyphenation inside the second or third etc. constituent of a compound word, always shrink the line to show bigger part of that non-first constituent (within the custom word spacing range). For these, extend lingucomponent HYPH_COMPOUND_MIN_LEADING and HYPH_COMPOUND_MIN_TRAILING API to support disabling hyphenation of non-compound words, also disabling hyphenation inside the first constituent (but not inside the suffix or just before the suffix). This allows to recognize compound words during line breaking. Details: Hyphenator::hyphenate(). The initial development enables compound-based "smart" hyphenation only for the Hungarian language, yet. The patch contains minor fixes of the compound-based Hungarian hyphenation, resulting better compound-based "smart" hyphenation with recognition of verb prefixes and leg-/legesleg- superlative prefixes. (Note: this fixed also COMPOUND_MIN_LEADING/TRAILING settings for the hyphenation of the compound words with dictionary-based hyphenation (only hy:, without pa: Hunspell dictionary fields, i.e. without compound splitting heuristics of Hunspell). Change-Id: I26f31329cdea20f03dbde7742587089eb5df5ae2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210423 Reviewed-by: László Németh <nemeth@numbertext.org> Tested-by: Jenkins
This commit is contained in:
@@ -471,6 +471,7 @@ Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const OUString& aWo
|
||||
// also skip hyphenation inside compound constituents right before constituent boundaries in compound words
|
||||
// or always skip hyphenation inside compound constituents, if minCompoundTrail == 0
|
||||
// if both minCompoundLead and minCompoundTrail == 0, don't hyphenate inside suffixes, too
|
||||
// if both minCompoundLead and minCompoundTrail >= 99, don't hyphenate inside suffixes, too
|
||||
if ( bCompoundHyphenation && (nCompoundHyphenationPos > -1 || minCompoundLead == 0 || minCompoundTrail > 2 || minCompoundTrail == 0 ) &&
|
||||
// check minimal distance from the left constituent boundary
|
||||
( ( minCompoundLead > 2 && i - nCompoundHyphenationPos < minCompoundLead ) ||
|
||||
@@ -478,7 +479,8 @@ Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const OUString& aWo
|
||||
( minCompoundTrail > 2 && i - nCompoundHyphenationPos < minCompoundTrail ) ||
|
||||
// special value: zero compound limit means forbidden hyphenation
|
||||
// inside (stems of) compound constituents
|
||||
minCompoundLead == 0 || minCompoundTrail == 0 ) )
|
||||
minCompoundLead == 0 || minCompoundTrail == 0 ||
|
||||
minCompoundLead >= 99 || minCompoundTrail >= 99 ) )
|
||||
{
|
||||
uno::Reference< XLinguServiceManager2 > xLngSvcMgr( GetLngSvcMgr_Impl() );
|
||||
uno::Reference< XSpellChecker1 > xSpell;
|
||||
@@ -566,21 +568,58 @@ Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const OUString& aWo
|
||||
break;
|
||||
}
|
||||
|
||||
// handle verb and leg-, legesleg- prefixes (Hungarian-only)
|
||||
sal_Int32 nPrefixLen = 0;
|
||||
if ( LANGUAGE_HUNGARIAN == nLanguage && sStems.isEmpty() )
|
||||
{
|
||||
if ( aWord.startsWith(u"leg") &&
|
||||
morph.indexOf(u"ip:leg_") > -1 )
|
||||
{
|
||||
sStems = aWord.replaceAt(3, 0, u"||");
|
||||
nPrefixLen = 5;
|
||||
}
|
||||
else if ( aWord.startsWith(u"legesleg") &&
|
||||
morph.indexOf(u"ip:legesleg_") > -1 )
|
||||
{
|
||||
sStems = aWord.replaceAt(0, 8, u"leges||leg||");
|
||||
nPrefixLen = 12;
|
||||
}
|
||||
|
||||
if ( sStems.isEmpty() )
|
||||
{
|
||||
static constexpr OUString sVERB_PREFIX = u"ip:PREF sp:"_ustr;
|
||||
nPa = morph.indexOf(sVERB_PREFIX);
|
||||
if ( nPa > -1 )
|
||||
{
|
||||
nPa += sVERB_PREFIX.getLength();
|
||||
OUString sVerbPrefix = morph.getToken(0, ' ', nPa);
|
||||
nPrefixLen = sVerbPrefix.getLength();
|
||||
if ( aWord.startsWith(sVerbPrefix) )
|
||||
sStems = aWord.replaceAt(nPrefixLen++, 0, u"|");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only hy:, but not pa:
|
||||
if ( sStems.isEmpty() )
|
||||
if ( sStems.isEmpty() || nPrefixLen > 0 )
|
||||
{
|
||||
// check hy: (pre-defined hyphenation)
|
||||
sal_Int32 nHy = morph.indexOf(" hy:");
|
||||
if (nHy > -1)
|
||||
{
|
||||
sStems = morph.getToken(1, ' ', nHy).copy(3);
|
||||
if ( sStems.indexOf('|') == -1 && sStems.indexOf('-') == -1 )
|
||||
OUString sHy(morph.getToken(1, ' ', nHy).copy(3));
|
||||
if ( sHy.indexOf('|') == -1 && sHy.indexOf('-') == -1 )
|
||||
{
|
||||
if ( sal_Int32 nBreak = o3tl::toInt32(sStems) )
|
||||
if ( sal_Int32 nBreak = o3tl::toInt32(sHy) )
|
||||
{
|
||||
if ( nBreak < aWord.getLength() )
|
||||
sStems += OUString::Concat(aWord.subView(0, nBreak)) + u"|" +
|
||||
aWord.subView(nBreak);
|
||||
if ( sStems.isEmpty() )
|
||||
sStems = aWord;
|
||||
else
|
||||
// correction for Hungarian (e.g. "leg||meg|lepőbb")
|
||||
nBreak += nPrefixLen;
|
||||
if ( nBreak < sStems.getLength() )
|
||||
sStems = OUString::Concat(sStems.subView(0, nBreak)) + u"|" +
|
||||
sStems.subView(nBreak);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -589,7 +628,23 @@ Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const OUString& aWo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// handle string separated by |, e.g "program hy:pro|gram"
|
||||
//
|
||||
// hyphenation at special lead/trail combinations over compound splitting
|
||||
//
|
||||
// +: hyphenation, -: no hyphenation
|
||||
//
|
||||
// lead | trail || non-compound | first constituent | suffix |
|
||||
// -----------------------------------------------------------
|
||||
// 0 | 0 || + | + | - |
|
||||
// 0 | x || + | + | + |
|
||||
// x | 0 || + | + | + |
|
||||
// x | x || + | + | + |
|
||||
// 0 | 99<= || - | - | + |
|
||||
// 99<= | 0 || - | - | + |
|
||||
// 99<= | 99<= || - | - | - |
|
||||
//
|
||||
if ( sStems.indexOf('|') > -1 )
|
||||
{
|
||||
sal_Int32 nLetters = 0; // count not separator characters
|
||||
@@ -613,13 +668,15 @@ Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const OUString& aWo
|
||||
// too close to the left break point or special case?
|
||||
( i - nCompoundHyphenationPos < minCompoundLead || minCompoundLead == 0 ) &&
|
||||
// there is a stem boundary before the actual break point
|
||||
nSepPos > -1 &&
|
||||
// or special values: disable hyphenation in the first constituent, too
|
||||
( nSepPos > -1 || minCompoundLead == 99 || minCompoundTrail == 99 ) &&
|
||||
// and the break point is within a stem, i.e. not in the
|
||||
// suffix of the last stem or both minCompoundLead and
|
||||
// minCompoundTrail == 0, i.e. hyphenation is allowed only
|
||||
// at compound constituent boundaries
|
||||
( i < aWord.getLength() - nSuffixLen - 1 ||
|
||||
( minCompoundLead == 0 && minCompoundTrail == 0) ) &&
|
||||
( minCompoundLead == 0 && minCompoundTrail == 0 ) ||
|
||||
( minCompoundLead >= 99 && minCompoundTrail >= 99 ) ) &&
|
||||
// and it is not another stem boundary
|
||||
j + 1 < sStems.getLength() &&
|
||||
( sStems[j] != u'|' ||
|
||||
@@ -630,7 +687,7 @@ Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const OUString& aWo
|
||||
continue;
|
||||
}
|
||||
// skip break points near before the stem boundaries
|
||||
// (hit is not mandatory, only compondhit, because the
|
||||
// (hit is not mandatory, only compoundhit, because the
|
||||
// compound boundary can be after the lead limit)
|
||||
if (!hit &&
|
||||
// too close to the right break point?
|
||||
@@ -640,7 +697,8 @@ Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const OUString& aWo
|
||||
// minCompoundTrail == 0, i.e. hyphenation is allowed only
|
||||
// at compound constituent boundaries
|
||||
( i < aWord.getLength() - nSuffixLen - 1 ||
|
||||
( minCompoundLead == 0 && minCompoundTrail == 0) ) &&
|
||||
( minCompoundLead == 0 && minCompoundTrail == 0) ||
|
||||
( minCompoundLead >= 99 && minCompoundTrail >= 99 ) ) &&
|
||||
// and it is a stem boundary
|
||||
j + 1 < sStems.getLength() && sStems[j+1] == u'|' &&
|
||||
// the previous break point is not a boundary
|
||||
@@ -654,8 +712,15 @@ Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const OUString& aWo
|
||||
bPrevCompound = nSepPos == j || nSepPos == j - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not a compound word
|
||||
|
||||
// special value: 99<= : if it is not a compound word, do not hyphenate it
|
||||
if ( minCompoundLead >= 99 || minCompoundTrail >= 99 )
|
||||
return nullptr;
|
||||
|
||||
bCompoundHyphenation = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
// no SPELLML support, no morphological analysis
|
||||
|
||||
Binary file not shown.
@@ -9,6 +9,9 @@
|
||||
|
||||
#include <swmodeltestbase.hxx>
|
||||
#include <com/sun/star/linguistic2/XHyphenator.hpp>
|
||||
#include <com/sun/star/linguistic2/XSpellAlternatives.hpp>
|
||||
#include <com/sun/star/linguistic2/XSpellChecker1.hpp>
|
||||
#include <i18nlangtag/languagetag.hxx>
|
||||
#include <vcl/metaact.hxx>
|
||||
#include <editeng/unolingu.hxx>
|
||||
|
||||
@@ -848,6 +851,520 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf172674_less_hyphenation)
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt/SwParaPortion/SwLineLayout/SwHyphPortion", 3);
|
||||
}
|
||||
|
||||
// check smart hyphenation and compound-based smart hyphenation
|
||||
CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf119908_smart_hyphenation_hu)
|
||||
{
|
||||
uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
|
||||
if (!xHyphenator->hasLocale(lang::Locale(u"hu"_ustr, u"HU"_ustr, OUString())))
|
||||
return;
|
||||
|
||||
// compound-based hyphenation needs Hunspell morphological analysis
|
||||
// (and an up-to-date Hungarian dictionary for full testing, which
|
||||
// contains compound splitting information. E.g. dictionary version 1.7
|
||||
// didn't contain data for the words "valamint" and "elismerésének")
|
||||
uno::Reference<linguistic2::XSpellChecker1> xSpell = LinguMgr::GetSpellChecker();
|
||||
LanguageType eLang
|
||||
= LanguageTag::convertToLanguageType(lang::Locale(u"hu"_ustr, u"HU"_ustr, OUString()));
|
||||
if (!xSpell.is() || !xSpell->hasLanguage(static_cast<sal_uInt16>(eLang)))
|
||||
return;
|
||||
|
||||
std::vector<std::pair<OUString, OUString>> aCompound = {
|
||||
{ u"jogorvoslatért"_ustr, "hy:3" }, { u"valamint"_ustr, "hy:4" },
|
||||
{ u"alapfokon"_ustr, "pa:fokon" }, { u"jótéteményekben"_ustr, "hy:2" },
|
||||
{ u"elismerésének"_ustr, "hy:2" },
|
||||
};
|
||||
|
||||
// tests supported by the actual spelling dictionary
|
||||
bool aTests[5];
|
||||
|
||||
// check Hunspell-based morphological analysis & hyphenation
|
||||
bool bHunspell = xSpell->isValid("<?xml?>", static_cast<sal_uInt16>(eLang),
|
||||
uno::Sequence<beans::PropertyValue>());
|
||||
if (bHunspell)
|
||||
{
|
||||
// check dictionary support of compound splitting
|
||||
for (size_t i = 0; i < aCompound.size(); ++i)
|
||||
{
|
||||
aTests[i] = false;
|
||||
// get morphological analysis of the test word
|
||||
uno::Reference<css::linguistic2::XSpellAlternatives> xTmpRes = xSpell->spell(
|
||||
"<?xml?><query type='analyze'><word>" + aCompound[i].first + "</word></query>",
|
||||
static_cast<sal_uInt16>(eLang), uno::Sequence<beans::PropertyValue>());
|
||||
if (xTmpRes.is())
|
||||
{
|
||||
uno::Sequence<OUString> seq = xTmpRes->getAlternatives();
|
||||
if (seq.hasElements())
|
||||
{
|
||||
sal_Int32 nEndOfFirstAnalysis = seq[0].indexOf("</a>");
|
||||
// use only the first analysis, like the lingucomponent implementation
|
||||
OUString morph(seq[0].copy(0, nEndOfFirstAnalysis));
|
||||
|
||||
// check compound data
|
||||
aTests[i] = morph.indexOf(aCompound[i].second) > -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createSwDoc("tdf119908_smart_hyphenation_hu.odt");
|
||||
// Ensure that all text portions are calculated before testing.
|
||||
SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
|
||||
|
||||
CPPUNIT_ASSERT(pViewShell);
|
||||
pViewShell->Reformat();
|
||||
|
||||
xmlDocUniquePtr pXmlDoc = parseLayoutDump();
|
||||
|
||||
// 53 hyphenations on 5 pages using compond-based smart hyphenation
|
||||
// This was 67 without smart hyphenation and 53 without compound-based smart hyphenation
|
||||
// (using only the plain or interoperable smart hyphenation).
|
||||
|
||||
// 13 hyphenations on page 1
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[3]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Tekintettel arra, hogy az emberiség családja minden egyes tagja méltóságának, vala");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
// "smart" hyphenation: disabled hyphenation of "emberiség" by shrinking
|
||||
// This was "emberi-ség"
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Tekintettel arra, hogy az emberi jogok el nem ismerése és semmibevevése az emberiség ");
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Tekintettel annak fontosságára, hogy az emberi jogokat a jog uralma védelmezze, ne");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"hogy az ember végső szükségében a zsarnokság és az elnyomás elleni lázadásra kény");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout[2]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Tekintettel arra, hogy igen lényeges a nemzetek közötti baráti kapcsolatok kifejlődésé");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
// "smart" hyphenation: disabled hyphenation of "tettek" by shrinking
|
||||
// This was "tet-tek"
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[7]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Tekintettel arra, hogy az Alapokmányban az Egyesült Nemzetek népei újból hitet tettek ");
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[7]/SwParaPortion/SwLineLayout[3]", "portion",
|
||||
u"egyenjogúsága mellett, valamint kinyilvánították azt az elhatározásukat, hogy elősegí");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[7]/SwParaPortion/SwLineLayout[3]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Tekintettel arra, hogy a tagállamok kötelezték magukat arra, hogy az Egyesült Nemze");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"A KÖZGYŰLÉS kinyilvánítja az EMBERI JOGOK EGYETEMES NYILATKOZA");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
// "smart" hyphenation: disabled hyphenation of "törekednie" by shifting
|
||||
// This was "tö-rekednie"
|
||||
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"TÁT, mint azt a közös eszményt, amelynek elérésére minden népnek és nemzetnek ");
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[11]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Minden emberi lény szabadnak születik, és egyenlő méltósága és joga van. Az embe");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"nyelvre, vallásra, politikai vagy más véleményre, nemzeti vagy társadalmi szárma");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[2]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[3]", "portion",
|
||||
u"zásra, vagyonra, születésre vagy más körülményre vonatkozó mindennemű megkülön");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[3]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[4]", "portion",
|
||||
u"böztetés nélkül mindenkit megilletnek. Ezenfelül nem lehet semmiféle megkülönböz");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[4]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[5]", "portion",
|
||||
u"tetést tenni annak az országnak vagy területnek a politikai, jogi vagy nemzetközi hely");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[5]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[7]", "portion",
|
||||
u"terület független, gyámság alatt áll, nem autonóm vagy szuverenitása bármely vonat");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[7]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt/SwParaPortion/SwLineLayout/SwHyphPortion", 13);
|
||||
|
||||
// delete page 1 to update hyphenation on the next page
|
||||
|
||||
SwWrtShell* const pWrtShell = getSwDocShell()->GetWrtShell();
|
||||
pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 2868, /*bBasicCall=*/false);
|
||||
pWrtShell->Delete();
|
||||
|
||||
// 13 hyphenations on page 2
|
||||
|
||||
pViewShell->Reformat();
|
||||
pXmlDoc = parseLayoutDump();
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Senkit sem lehet rabszolgaságban vagy szolgaságban tartani; a rabszolgaság és a rab");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Senkit sem lehet kínvallatásnak, avagy kegyetlen, embertelen vagy lealacsonyító bá");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"A törvény előtt mindenki egyenlő, és minden megkülönböztetés nélkül joga van a tör");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[3]", "portion",
|
||||
u"sértő minden megkülönböztetéssel és minden ilyen megkülönböztetésre irányuló fel");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[3]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
// compound-based "smart" hyphenation of the compound "jog|orvoslat"
|
||||
// This was hyphenated as "jogor-voslat" previously, now "jog-orvoslat"
|
||||
|
||||
if (bHunspell && aTests[0])
|
||||
{
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"alapvető jogokat sértő eljárások ellen az illetékes hazai bíróságokhoz "
|
||||
u"tényleges jog");
|
||||
}
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[2]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"bíróság méltányosan és nyilvánosan tárgyalja, s ez határozzon egyrészt jogai és köte");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[2]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[16]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Minden büntetendő cselekménnyel vádolt személyt ártatlannak kell vélelmezni mind");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[16]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[16]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"addig, amíg bűnösségét nyilvánosan lefolytatott perben, a védelméhez szükséges vala");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[16]/SwParaPortion/SwLineLayout[2]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[17]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Senkit sem szabad elítélni oly cselekményért vagy mulasztásért, amely elkövetése pil");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[17]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[17]/SwParaPortion/SwLineLayout[3]", "portion",
|
||||
u"Ugyancsak nem szabad súlyosabb büntetést kiszabni, mint amely a büntetendő cselek");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[17]/SwParaPortion/SwLineLayout[3]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[19]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Senkinek magánéletébe, családi ügyeibe, otthonába vagy levelezésébe nem szabad ön");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[19]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
// "smart" hyphenation: disabled hyphenation of "minden" by shrinking
|
||||
// This was "Min-den"
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[19]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"kényesen beavatkozni, sem pedig becsületében vagy jó hírnevében megsérteni. Minden ");
|
||||
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[21]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Az államon belül minden személynek joga van szabadon mozogni és lakóhelyét sza");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[21]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
// compound-based "smart" hyphenation of the compound "vala|mint"
|
||||
// This was hyphenated as "va-lamint" previously)
|
||||
|
||||
if (bHunspell && aTests[1])
|
||||
{
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[22]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Minden személynek joga van minden országot, ideértve saját hazáját is, "
|
||||
u"elhagyni, vala");
|
||||
}
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[22]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt/SwParaPortion/SwLineLayout/SwHyphPortion", 13);
|
||||
|
||||
// delete page 2 to update hyphenation on the next page
|
||||
|
||||
pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2297, /*bBasicCall=*/false);
|
||||
pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 2297, /*bBasicCall=*/false);
|
||||
pWrtShell->Delete();
|
||||
|
||||
// 10 hyphenations on page 3
|
||||
|
||||
pViewShell->Reformat();
|
||||
pXmlDoc = parseLayoutDump();
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Erre a jogra nem lehet hivatkozni közönséges bűncselekmény miatti, kellőképpen meg");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"alapozott üldözés, sem pedig az Egyesült Nemzetek céljaival és elveivel ellentétes te");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[2]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Senkit sem lehet sem állampolgárságától, sem állampolgársága megváltoztatásának jo");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Mind a férfinak, mind a nőnek a nagykorúság elérésétől kezdve joga van fajon, nem");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"zetiségen vagy valláson alapuló korlátozás nélkül házasságot kötni és családot alapí");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[2]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Minden személynek joga van a tulajdonhoz, mind egyénileg, mind másokkal együtte");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
// "smart" hyphenation: disabled hyphenation of "valamint" by shifting
|
||||
// This was "va-lamint"
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"a jog magában foglalja a vallás és a meggyőződés megváltoztatásának szabadságát, ");
|
||||
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Minden személynek joga van a vélemény és a kifejezés szabadságához, amely magá");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"ban foglalja azt a jogot, hogy véleménye miatt ne szenvedjen zaklatást, és hogy hatá");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[2]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
// "smart" hyphenation: disabled hyphenation of "információkat" by shrinking
|
||||
// This was "információ-kat"
|
||||
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[3]", "portion",
|
||||
u"rokra való tekintet nélkül kutathasson, tájékozódhasson és terjeszthessen "
|
||||
u"információkat ");
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[22]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"rendszeres, az általános és egyenlő szavazati jogot és a titkos szavazást biztosító vá");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[22]/SwParaPortion/SwLineLayout[2]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[24]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Minden személynek mint a társadalom tagjának joga van a szociális biztonsághoz, to");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[24]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt/SwParaPortion/SwLineLayout/SwHyphPortion", 10);
|
||||
|
||||
// delete page 3 to update hyphenation on the next page
|
||||
|
||||
pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2566, /*bBasicCall=*/false);
|
||||
pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 2566, /*bBasicCall=*/false);
|
||||
pWrtShell->Delete();
|
||||
|
||||
// 12 hyphenations on page 4
|
||||
|
||||
pViewShell->Reformat();
|
||||
pXmlDoc = parseLayoutDump();
|
||||
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Minden személynek joga van a munkához, a munka szabad megválasztásához, a mél");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Mindenkinek joga van egyenlő munkáért egyenlő bért kapni, bárminemű megkülön");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Minden személynek joga van a pihenéshez, a szabadidőhöz, beleértve a munkaidő ész");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Minden személynek joga van saját maga és családja egészségének és jólétének bizto");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[3]", "portion",
|
||||
u"ellátáshoz, valamint a szükséges szociális szolgáltatásokhoz, továbbá joga van a szoci");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[3]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[11]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"gyermek, akár házasságból, akár házasságon kívül született, azonos szociális védelem");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/SwParaPortion/SwLineLayout[2]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
// compound-based "smart" hyphenation of the compound "alap|fokon"
|
||||
// This was hyphenated as "alapfo-kon" previously, now "alap-fokon"
|
||||
|
||||
if (bHunspell && aTests[2])
|
||||
{
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Minden személynek joga van az oktatáshoz. Az oktatásnak legalábbis elemi és alap");
|
||||
}
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"A nevelésnek az emberi személyiség teljes kibontakoztatására, valamint az emberi jo");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[4]", "portion",
|
||||
u"közötti megértést, türelmet és barátságot, valamint támogatnia kell az Egyesült Nem");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[4]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"A szülőket elsőbbségi jog illeti meg a gyermekeiknek adandó oktatás megválasztásá");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
// compound-based "smart" hyphenation of the compound "jó|téteményekben"
|
||||
// This wasn't hyphenated previously, now "jó-téteményekben"
|
||||
|
||||
if (bHunspell && aTests[3])
|
||||
{
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[17]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"a művészetek élvezéséhez, valamint a tudomány haladásában és az abból származó jó");
|
||||
assertXPath(pXmlDoc,
|
||||
"/root/page[1]/body/txt[17]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", 1);
|
||||
}
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[18]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Mindenkinek joga van minden általa alkotott tudományos, irodalmi és művészeti ter");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[18]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt/SwParaPortion/SwLineLayout/SwHyphPortion", 13);
|
||||
|
||||
// delete page 4 to update hyphenation on the next page
|
||||
|
||||
pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2766, /*bBasicCall=*/false);
|
||||
pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 2766, /*bBasicCall=*/false);
|
||||
pWrtShell->Delete();
|
||||
|
||||
// 5 hyphenations on page 5
|
||||
|
||||
pViewShell->Reformat();
|
||||
pXmlDoc = parseLayoutDump();
|
||||
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Minden személynek joga van ahhoz, hogy mind a társadalmi, mind a nemzetközi vi");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"szonyok tekintetében olyan rendszer uralkodjék, amelyben a jelen Nyilatkozatban ki");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[2]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Jogainak gyakorlásában és szabadságainak élvezetében mindenki csak olyan korláto");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
// compound-based "smart" hyphenation of the compound "el|ismerésének"
|
||||
// This wasn't hyphenated by the plain, i.e. not compound-based "smart" hyphenation previously.
|
||||
|
||||
if (bHunspell && aTests[4])
|
||||
{
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[2]", "portion",
|
||||
u"zásoknak lehet alávetve, amelyeket a törvény kizárólag mások jogai és "
|
||||
u"szabadságai el");
|
||||
assertXPath(pXmlDoc,
|
||||
"/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", 1);
|
||||
}
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"Ezeket a jogokat és szabadságokat semmi esetre sem lehet az Egyesült Nemzetek cél");
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout[1]/SwHyphPortion",
|
||||
1);
|
||||
|
||||
// "smart" hyphenation: disabled hyphenation of "állam" by shifting
|
||||
// This was "ál-lam"
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc, "/root/page[1]/body/txt[7]/SwParaPortion/SwLineLayout[1]", "portion",
|
||||
u"A jelen Nyilatkozat egyetlen rendelkezése sem értelmezhető úgy, hogy az valamely ");
|
||||
|
||||
if (bHunspell && aTests[4])
|
||||
assertXPath(pXmlDoc, "/root/page[1]/body/txt/SwParaPortion/SwLineLayout/SwHyphPortion", 5);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf158333)
|
||||
{
|
||||
createSwDoc("tdf130088.docx");
|
||||
|
||||
+161
-12
@@ -17,7 +17,9 @@
|
||||
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
||||
*/
|
||||
|
||||
#include <com/sun/star/beans/PropertyValue.hpp>
|
||||
#include <com/sun/star/i18n/ScriptType.hpp>
|
||||
#include <com/sun/star/linguistic2/XHyphenatedWord.hpp>
|
||||
#include <com/sun/star/uno/Reference.hxx>
|
||||
#include <editeng/unolingu.hxx>
|
||||
#include <i18nlangtag/mslangid.hxx>
|
||||
@@ -38,6 +40,7 @@
|
||||
#include <pam.hxx>
|
||||
#include <doc.hxx>
|
||||
#include <o3tl/temporary.hxx>
|
||||
#include <unotools/linguprops.hxx>
|
||||
#include <xmloff/odffields.hxx>
|
||||
#include <viewopt.hxx>
|
||||
|
||||
@@ -312,6 +315,73 @@ void SwTextPortion::BreakCut( SwTextFormatInfo &rInf, const SwTextGuess &rGuess
|
||||
}
|
||||
}
|
||||
|
||||
// is it a hyphenation of a compound word, which splits the compound word between the
|
||||
// compound constituents OR it hyphenates the compound word just before or inside its suffix?
|
||||
bool SwTextPortion::IsCompoundSplit( SwTextFormatInfo &rInf, const SwTextGuess &rGuess,
|
||||
bool* pbHasSplitBefore )
|
||||
{
|
||||
if ( !rGuess.HyphWord().is() )
|
||||
return false;
|
||||
|
||||
LanguageType aLang = rInf.GetTextFrame()->GetLangOfChar(rInf.GetIdx(), 1, true);
|
||||
|
||||
// Hungarian-only yet
|
||||
if ( LANGUAGE_HUNGARIAN != aLang )
|
||||
return false;
|
||||
|
||||
LanguageTag aLanguageTag(aLang);
|
||||
|
||||
uno::Reference< linguistic2::XHyphenator > xHyph;
|
||||
xHyph = ::GetHyphenator();
|
||||
|
||||
::css::uno::Sequence< ::css::beans::PropertyValue > aProperties(2);
|
||||
::css::beans::PropertyValue *pVal = aProperties.getArray();
|
||||
|
||||
pVal[0].Name = UPN_HYPH_COMPOUND_MIN_LEADING;
|
||||
pVal[0].Handle = UPH_HYPH_COMPOUND_MIN_LEADING;
|
||||
// 0: compound splitting: do not hyphenate inside second, third etc.
|
||||
// constituent of compound words
|
||||
pVal[0].Value <<= sal_Int16(0);
|
||||
|
||||
pVal[1].Name = UPN_HYPH_COMPOUND_MIN_TRAILING;
|
||||
pVal[1].Handle = UPH_HYPH_COMPOUND_MIN_TRAILING;
|
||||
// 99<=: force compound splitting to recognize and handle compound words
|
||||
// * do not hyphenate non-compound words
|
||||
// * do not hyphenate the first constituent of compound words
|
||||
// * but still hyphenate terminating suffix of compound words,
|
||||
// including hyphenation just before the suffix, i.e. after the
|
||||
// stem of the last constituent (condition: LEADING != TRAILING)
|
||||
pVal[1].Value <<= sal_Int16(99);
|
||||
|
||||
css::uno::Reference< css::linguistic2::XHyphenatedWord >
|
||||
xHyphWord = xHyph->hyphenate( rGuess.HyphWord()->getWord(),
|
||||
aLanguageTag.getLocale(),
|
||||
rGuess.HyphWord()->getHyphenationPos() + 1,
|
||||
aProperties );
|
||||
|
||||
// compound splitting resulted the same break, so the word is really a compound word
|
||||
// and the break point split compound constituents or it breaks the compound word
|
||||
// 1) just before its suffix or 2) inside its suffix
|
||||
if ( xHyphWord.is() &&
|
||||
xHyphWord->getHyphenationPos() == rGuess.HyphWord()->getHyphenationPos() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// when IsCompoundSplit() is false, pbHasSplitBefore's return value is true, if
|
||||
// * the word is a compound word AND the break point
|
||||
// * is not in a compound splitting position OR just before or inside the suffix AND
|
||||
// * not inside the first constituent
|
||||
// otherwise is false.
|
||||
// Note: This value is used to optimize the hyphenation of compound words, trying to
|
||||
// show more from the first constituent(s) and the broken one before the hyphenation,
|
||||
// if it's not possible to skip the hyphenation completely.
|
||||
if ( pbHasSplitBefore )
|
||||
*pbHasSplitBefore = xHyphWord.is();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SwTextPortion::BreakUnderflow( SwTextFormatInfo &rInf )
|
||||
{
|
||||
Truncate();
|
||||
@@ -628,6 +698,7 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
|
||||
if ( bWordSpacing )
|
||||
{
|
||||
std::optional<SwTextGuess> pGuess2(std::in_place);
|
||||
std::optional<SwTextGuess> pGuess3(std::in_place);
|
||||
|
||||
// hyphenation slider, when 0.0 < fLevel < 0.5,
|
||||
// enlarge both desired and maximum word spacing:
|
||||
@@ -644,12 +715,27 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
|
||||
? 0
|
||||
: (0.5 - fLevel) * 200;
|
||||
|
||||
// FIXME no need for pGuess2, if the desired word space is 100%
|
||||
bool bFull2 = !pGuess2->Guess( *this, rInf, Height(), nSpacesInLine,
|
||||
nWeightedSpacing, nSpaceWidth, nExtraWordSpacing );
|
||||
|
||||
// perhaps it's possible to skip hyphenation
|
||||
bool bOrigHyphenated2 = pGuess2->HyphWord().is() &&
|
||||
pGuess2->BreakPos() > rInf.GetLineStart();
|
||||
|
||||
// possible compound splitting in the same word before the hyphenation
|
||||
if ( bOrigHyphenated2 && pGuess2->HyphWord()->getHyphenationPos() >= 4 )
|
||||
pGuess3->Guess( *this, rInf, Height(), nSpacesInLine,
|
||||
// fix this heuristics!!!
|
||||
(nWeightedSpacing+aAdjustItem.GetPropWordSpacingMaximum())/2, nSpaceWidth, nExtraWordSpacing );
|
||||
|
||||
bool bOrigHyphenated3 = pGuess3->HyphWord().is() &&
|
||||
pGuess3->BreakPos() > rInf.GetLineStart();
|
||||
|
||||
// perhaps there is a compound splitting in the same word before the plain hyphenation
|
||||
bool bPossibleCompoundSplit = bOrigHyphenated3 &&
|
||||
pGuess3->HyphWord()->getHyphenationPos() < pGuess2->HyphWord()->getHyphenationPos() &&
|
||||
pGuess3->HyphWord()->getWord().equals(pGuess2->HyphWord()->getWord());
|
||||
|
||||
if ( !bOrigHyphenated2 &&
|
||||
// try to remove hyphenation in interoperability mode or
|
||||
// allow desired word spacing
|
||||
@@ -661,7 +747,8 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
|
||||
sal_Int32 nSpacesInLine2 = rInf.GetLineSpaceCount( pGuess2->BreakPos() );
|
||||
float fSpaceNormal2 = nSpacesInLine2 > 0
|
||||
? ( rInf.GetLineWidth() - ( rInf.GetBreakWidth() -
|
||||
nSpacesInLine2 * nSpaceWidth/10.0
|
||||
nSpacesInLine2 * nSpaceWidth/10.0 +
|
||||
( bOrigHyphenated2 ? nHyphenWidth : 0)
|
||||
) ) / nSpacesInLine2
|
||||
// no remaining space, only the space after the single word
|
||||
: 1;
|
||||
@@ -672,8 +759,12 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
|
||||
// desired one
|
||||
// FIXME if the desired word spacing is not 100%, and maximum word spacing
|
||||
// is not the same, but minimum word spacing is, the fallback is still 100%
|
||||
if ( z1NotWeighted <= ( nSpacingMaximum + nExtraWordSpacing )/100.0 ||
|
||||
aAdjustItem.GetPropWordSpacing() == nSpacingMaximum )
|
||||
if ( ( z1NotWeighted <= ( nSpacingMaximum + nExtraWordSpacing )/100.0 ||
|
||||
aAdjustItem.GetPropWordSpacing() == nSpacingMaximum ) &&
|
||||
// skip hyphenation only if it's not compound splitting
|
||||
( !bOrigHyphenated || !IsCompoundSplit(rInf, *pGuess) ) &&
|
||||
// skip hyphenation for another hyphenation only, if that is a compound splitting
|
||||
( !bOrigHyphenated2 || IsCompoundSplit(rInf, *pGuess2) ) )
|
||||
{
|
||||
pGuess.emplace();
|
||||
pGuess = std::move(pGuess2);
|
||||
@@ -683,6 +774,46 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
|
||||
bFull = bFull2;
|
||||
}
|
||||
}
|
||||
|
||||
// check compound
|
||||
if ( bOrigHyphenated && bPossibleCompoundSplit &&
|
||||
// try to remove hyphenation in interoperability mode or
|
||||
// allow desired word spacing
|
||||
( ( bOrigHyphenated && fLevel <= 0.5 ) || fLevel > 0.5 ||
|
||||
aAdjustItem.GetPropWordSpacing() == nSpacingMaximum ) &&
|
||||
( aAdjustItem.GetPropLetterSpacingMinimum() < 0 ||
|
||||
rInf.GetBreakWidth() <= rInf.GetLineWidth() ) )
|
||||
{
|
||||
sal_Int32 nSpacesInLine3 = rInf.GetLineSpaceCount( pGuess3->BreakPos() );
|
||||
float fSpaceNormal3 = nSpacesInLine3 > 0
|
||||
? ( rInf.GetLineWidth() - ( rInf.GetBreakWidth() -
|
||||
nSpacesInLine3 * nSpaceWidth/10.0 +
|
||||
( bOrigHyphenated3 ? nHyphenWidth : 0)
|
||||
) ) / nSpacesInLine3
|
||||
// no remaining space, only the space after the single word
|
||||
: 1;
|
||||
float z1NotWeighted = ( nSpaceWidth/10.0 +
|
||||
( fSpaceNormal3 - nSpaceWidth/10.0) ) / (nSpaceWidth/10.0);
|
||||
// skip hyphenation/desired spacing, if spacing doesn't exceed the maximum
|
||||
// word spacing, except if maximum word spacing is not different from the
|
||||
// desired one
|
||||
// FIXME if the desired word spacing is not 100%, and maximum word spacing
|
||||
// is not the same, but minimum word spacing is, the fallback is still 100%
|
||||
if ( ( z1NotWeighted <= ( nSpacingMaximum + nExtraWordSpacing )/100.0 ||
|
||||
aAdjustItem.GetPropWordSpacing() == nSpacingMaximum ) &&
|
||||
// skip hyphenation only if it's not compound splitting
|
||||
( !bOrigHyphenated || !IsCompoundSplit(rInf, *pGuess) ) &&
|
||||
// skip hyphenation for another hyphenation only, if that is a compound splitting
|
||||
( !bOrigHyphenated3 || IsCompoundSplit(rInf, *pGuess3) ) )
|
||||
{
|
||||
pGuess.emplace();
|
||||
pGuess = std::move(pGuess3);
|
||||
SetSpacing(rInf, *pGuess, nSpacesInLine3, nSpaceWidth);
|
||||
fSpaceNormal = fSpaceNormal3;
|
||||
bOrigHyphenated = bOrigHyphenated3;
|
||||
bFull = bFull2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sal_Int32 nSpacesInLineShrink = 0;
|
||||
@@ -781,22 +912,40 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
|
||||
float z1 = (nSpaceWidth/10.0+((fSpaceNormal-nSpaceWidth/10.0)*fExpansionWeight))/(nSpaceWidth/10.0);
|
||||
float z1NotWeighted = ( nSpaceWidth/10.0 +
|
||||
( fSpaceNormal - nSpaceWidth/10.0) ) / (nSpaceWidth/10.0);
|
||||
// true, if the pGuess2 hyphenation is inside the second or third etc. compound constituent
|
||||
bool bCompoundSplitBefore = false;
|
||||
// always shrink at average and lower hyphenation level,
|
||||
// if it removes hyphenation
|
||||
bool bRemoveHyphenation = bOrigHyphenated && !bShrunkHyphenated && fLevel <= 0.5;
|
||||
// if it removes a non-compound word splitting hyphenation
|
||||
bool bRemoveHyphenation = bOrigHyphenated && fLevel <= 0.5 &&
|
||||
( !bShrunkHyphenated || IsCompoundSplit(rInf, *pGuess2, &bCompoundSplitBefore) ||
|
||||
// if shrinking results in a non-compound splitting hyphenation
|
||||
// inside the second or third etc. constituent of a compound word,
|
||||
// always shrink the line to show bigger part of that non-first constituent
|
||||
// (no need to check pGuess for bCompoundSplitBefore, too, because shrinking
|
||||
// doesn't skip a compound splitting position between pGuess and pGuess2)
|
||||
( bCompoundSplitBefore && !IsCompoundSplit(rInf, *pGuess) ) );
|
||||
|
||||
// Prefer shrinking, if 1) this is a portion OR,
|
||||
// TODO shrink line portions only if needed
|
||||
if ( bIsPortion ||
|
||||
// 2) NOT shrinking would result very large spacing ( >150% ) OR
|
||||
// 3) shrinking removes hyphenation OR ...
|
||||
// 3) shrinking removes (worse) hyphenation OR ...
|
||||
z1NotWeighted > nSpacingMaximum/100.0 || bRemoveHyphenation ||
|
||||
// ... 4) * shrinking results better (weighted) spacing AND
|
||||
// * doesn't result in an extra hyphenation
|
||||
( z1 >= z0 && !( !bOrigHyphenated && bShrunkHyphenated ) ) ||
|
||||
// or allow more and more hyphenation choosing better and better spacing
|
||||
// * doesn't result in an extra hyphenation AND
|
||||
// * no losing of compound splitting
|
||||
( z1 >= z0 && !( !bOrigHyphenated && bShrunkHyphenated ) &&
|
||||
!( bOrigHyphenated && bShrunkHyphenated && IsCompoundSplit(rInf, *pGuess) &&
|
||||
!IsCompoundSplit(rInf, *pGuess2) ) ) ||
|
||||
// 5) or allow more and more hyphenation choosing better and better spacing
|
||||
// when shrinking results in an extra hyphenation
|
||||
( z1 >= z0 && !bOrigHyphenated && bShrunkHyphenated &&
|
||||
fLevel > 0.5 && z1 * ( fLevel - 0.5 ) >= z0 * ( 1 - fLevel ) ) )
|
||||
( z1 >= z0 && !bOrigHyphenated && bShrunkHyphenated && (
|
||||
( fLevel > 0.5 && z1 * ( fLevel - 0.5 ) >= z0 * ( 1 - fLevel ) ) ||
|
||||
// except compound splitting, which is equally good,
|
||||
// than no hyphenation, so always choose that,
|
||||
// if it results in better spacing
|
||||
IsCompoundSplit(rInf, *pGuess2) )
|
||||
) )
|
||||
{
|
||||
pGuess = std::move(pGuess2);
|
||||
SetSpacing(rInf, *pGuess, nSpacesInLineShrink, nSpaceWidth);
|
||||
|
||||
@@ -26,6 +26,11 @@ class SwTextGuess;
|
||||
class SwTextPortion : public SwLinePortion
|
||||
{
|
||||
void BreakCut( SwTextFormatInfo &rInf, const SwTextGuess &rGuess );
|
||||
// is this hyphenation is good enough for a compound word, i.e.
|
||||
// it splits a compound word between compound constituents or
|
||||
// it hyphenates the compound word just before or inside its suffix
|
||||
static bool IsCompoundSplit( SwTextFormatInfo &rInf, const SwTextGuess &rGuess,
|
||||
bool* pbCompoundSplitBefore = nullptr );
|
||||
void BreakUnderflow( SwTextFormatInfo &rInf );
|
||||
void SetSpacing( SwTextFormatInfo &rInf, SwTextGuess const &rGuess,
|
||||
const sal_Int32 nSpaces, const sal_Int16 nWidthOf10Spaces );
|
||||
|
||||
Reference in New Issue
Block a user