diff --git a/sw/qa/extras/layout/data/tdf163720.fodt b/sw/qa/extras/layout/data/tdf163720.fodt
index 7f0fccef95ef..28c38cf6a0c9 100644
--- a/sw/qa/extras/layout/data/tdf163720.fodt
+++ b/sw/qa/extras/layout/data/tdf163720.fodt
@@ -178,7 +178,7 @@
-
+
diff --git a/sw/qa/extras/layout/layout3.cxx b/sw/qa/extras/layout/layout3.cxx
index 15034a6fb8b3..23dd0dc51221 100644
--- a/sw/qa/extras/layout/layout3.cxx
+++ b/sw/qa/extras/layout/layout3.cxx
@@ -1054,7 +1054,8 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf126154)
u",,,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti ");
// also minimum word space: 80%, 100%, 100%
- // only a single line is hyphenated from the previous ones
+ // only a single line was hyphenated from the previous ones
+ // TODO: fix possible interoperability issues, allow optional limitation of hyphenation again
assertXPath(
pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[1]", "portion",
u",, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bulum ");
@@ -1067,10 +1068,10 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf126154)
u"Vesti bulum ");
assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[1]", "portion",
u",,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
- u"Vesti bulum ");
+ u"Vesti bu");
assertXPath(pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[1]", "portion",
u",,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
- u"Vesti bulum ");
+ u"Vesti bu");
assertXPath(
pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[1]", "portion",
u",,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bu");
@@ -1079,8 +1080,9 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf126154)
u",,,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti ");
// minimum, desired and maximum word spacing: 80%, 100%, 133%
- // no hyphenation in the same text: hyphenation of all the short words are limited
+ // no hyphenation in the same text: hyphenation of all the short words were limited
// by the minimum and maximum word spacing settings
+ // TODO: fix possible interoperability issues, allow optional limitation of hyphenation again
assertXPath(
pXmlDoc, "/root/page[1]/body/txt[18]/SwParaPortion/SwLineLayout[1]", "portion",
u",, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bulum ");
@@ -1093,10 +1095,10 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf126154)
u"Vesti bulum ");
assertXPath(pXmlDoc, "/root/page[1]/body/txt[21]/SwParaPortion/SwLineLayout[1]", "portion",
u",,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
- u"Vesti bulum ");
+ u"Vesti bu");
assertXPath(pXmlDoc, "/root/page[1]/body/txt[22]/SwParaPortion/SwLineLayout[1]", "portion",
u",,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
- u"Vesti bulum ");
+ u"Vesti bu");
assertXPath(
pXmlDoc, "/root/page[1]/body/txt[23]/SwParaPortion/SwLineLayout[1]", "portion",
u",,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti ");
@@ -1167,7 +1169,8 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf126154_minimum_shrinking)
u",,,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti ");
// also minimum word space: 80%, 100%, 100%
- // only a single line is hyphenated from the previous ones
+ // only a single line was yphenated from the previous ones
+ // TODO: fix possible interoperability issues, allow optional limitation of hyphenation again
assertXPath(
pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[1]", "portion",
u",, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bulum ");
@@ -1179,10 +1182,10 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf126154_minimum_shrinking)
u"Vesti bulum ");
assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[1]", "portion",
u",,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
- u"Vesti bulum ");
+ u"Vesti bu");
assertXPath(pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[1]", "portion",
u",,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
- u"Vesti bulum ");
+ u"Vesti bu");
assertXPath(
pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[1]", "portion",
u",,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bu");
@@ -1191,8 +1194,9 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf126154_minimum_shrinking)
u",,,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti ");
// minimum, desired and maximum word spacing: 80%, 100%, 133%
- // no hyphenation in the same text: hyphenation of all the short words are limited
+ // no hyphenation in the same text: hyphenation of all the short words were limited
// by the minimum and maximum word spacing settings
+ // TODO: fix possible interoperability issues, allow optional limitation of hyphenation again
assertXPath(
pXmlDoc, "/root/page[1]/body/txt[18]/SwParaPortion/SwLineLayout[1]", "portion",
u",, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti bulum ");
@@ -1204,10 +1208,10 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf126154_minimum_shrinking)
u"Vesti bulum ");
assertXPath(pXmlDoc, "/root/page[1]/body/txt[21]/SwParaPortion/SwLineLayout[1]", "portion",
u",,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
- u"Vesti bulum ");
+ u"Vesti bu");
assertXPath(pXmlDoc, "/root/page[1]/body/txt[22]/SwParaPortion/SwLineLayout[1]", "portion",
u",,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
- u"Vesti bulum ");
+ u"Vesti bu");
assertXPath(
pXmlDoc, "/root/page[1]/body/txt[23]/SwParaPortion/SwLineLayout[1]", "portion",
u",,,,,,, , , , , , , , Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vesti ");
@@ -1237,11 +1241,12 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf126154_portion)
u",,,,,,,,,,,,,,,,,,,,, , , , , , , , , , , , , , , , Lorem ipsum dolor sit amet, "
u"consectetur adipiscing elit. Vesti ");
- // prefer minimum word spacing, not maximum word spacing to disable hyphenation, if it's possible
- // between the minimum and maximum values. (minimum: 80%, desired: 100%, maximum word spacing: 133%)
+ // minimum: 80%, desired: 100%, maximum word spacing: 133%
+ // prefer maximum word spacing, not minimum word spacing, if the weighted word space
+ // is nearer to the desired word space
assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]", "portion",
u",,,,,,,,,,,,,,,,,,,,, , , , , , , , , , , , , , , , Lorem ipsum dolor sit amet, "
- u"consectetur adipiscing elit. Vesti bulum ");
+ u"consectetur adipiscing elit. Vesti ");
// paragraph end zone
// This was "... other celes" (not disabled hyphenation because of text portion)
diff --git a/sw/source/core/text/guess.cxx b/sw/source/core/text/guess.cxx
index d18336432118..694a74361553 100644
--- a/sw/source/core/text/guess.cxx
+++ b/sw/source/core/text/guess.cxx
@@ -158,7 +158,7 @@ bool SwTextGuess::maybeAdjustPositionsForBlockAdjust(tools::Long& rMaxSizeDiff,
// otherwise possible break or hyphenation position is determined
bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
const sal_uInt16 nPorHeight, sal_Int32 nSpacesInLine,
- sal_uInt16 nPropWordSpacing )
+ sal_uInt16 nPropWordSpacing, sal_Int16 nSpaceWidth )
{
m_nCutPos = rInf.GetIdx();
@@ -191,19 +191,13 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
if ( nSpacesInLine )
{
- static constexpr OUStringLiteral STR_BLANK = u" ";
- sal_Int16 nSpaceWidth = rInf.GetTextSize(STR_BLANK).Width();
- float fWordSpacing = nPropWordSpacing == SAL_MAX_UINT16
- ? 0.75 // MSO interoperability value
- // allow up to 25% shrinking of the spaces
- : nPropWordSpacing / 100.0;
- SwTwips nExtraSpace = nSpacesInLine * nSpaceWidth * (1.0 - fWordSpacing);
+ SwTwips nExtraSpace = nSpacesInLine * nSpaceWidth/10.0 * (1.0 - nPropWordSpacing / 100.0);
nLineWidth += nExtraSpace;
// convert maximum word spacing to hyphenation zone, if defined
if ( nPropWordSpacing == aAdjustItem.GetPropWordSpacing() )
{
SwTwips nMaxDif = aAdjustItem.GetPropWordSpacingMaximum() - nPropWordSpacing;
- nWordSpacingMaximumZone = nSpacesInLine * nSpaceWidth * nMaxDif / 100.0;
+ nWordSpacingMaximumZone = nSpacesInLine * nSpaceWidth/10.0 * nMaxDif / 100.0;
}
rInf.SetExtraSpace(nExtraSpace);
@@ -845,6 +839,7 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
rInf.GetTextSize(&rSI, rInf.GetIdx(), nPorLen, std::nullopt, nMaxComp, m_nBreakWidth,
nMaxSizeDiff, nExtraAscent, nExtraDescent, rInf.GetCachedVclData().get());
+ rInf.SetBreakWidth(m_nBreakWidth);
// save maximum width for later use
if ( nMaxSizeDiff )
rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff );
diff --git a/sw/source/core/text/guess.hxx b/sw/source/core/text/guess.hxx
index 54f38c7f1e90..5c1d0d387b16 100644
--- a/sw/source/core/text/guess.hxx
+++ b/sw/source/core/text/guess.hxx
@@ -45,7 +45,7 @@ public:
// true, if current portion still fits to current line
bool Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
const sal_uInt16 nHeight, sal_Int32 nSpacesInLine = 0,
- sal_uInt16 nPropWordSpacing = 100 );
+ sal_uInt16 nPropWordSpacing = 100, sal_Int16 nSpaceWidth = 0 );
bool AlternativeSpelling( const SwTextFormatInfo &rInf, const TextFrameIndex nPos );
SwHangingPortion* GetHangingPortion() const { return m_pHanging.get(); }
diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
index 692be2c2da41..3cb5a8226d7a 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -225,6 +225,7 @@ SwTextSizeInfo::SwTextSizeInfo()
, m_bSnapToGrid(false)
, m_nDirection(0)
, m_nExtraSpace(0)
+, m_nBreakWidth(0)
{}
SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew )
@@ -256,7 +257,8 @@ SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew )
m_bForbiddenChars( rNew.HasForbiddenChars() ),
m_bSnapToGrid( rNew.SnapToGrid() ),
m_nDirection( rNew.GetDirection() ),
- m_nExtraSpace( rNew.GetExtraSpace() )
+ m_nExtraSpace( rNew.GetExtraSpace() ),
+ m_nBreakWidth( rNew.GetBreakWidth() )
{
#if OSL_DEBUG_LEVEL > 0
ChkOutDev( *this );
@@ -269,6 +271,7 @@ void SwTextSizeInfo::CtorInitTextSizeInfo( OutputDevice* pRenderContext, SwTextF
m_pKanaComp = nullptr;
m_nKanaIdx = 0;
m_nExtraSpace = 0;
+ m_nBreakWidth = 0;
m_pFrame = pFrame;
CtorInitTextInfo( m_pFrame );
SwDoc const& rDoc(m_pFrame->GetDoc());
@@ -370,7 +373,8 @@ SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew, const OUString* pTex
m_bForbiddenChars( rNew.HasForbiddenChars() ),
m_bSnapToGrid( rNew.SnapToGrid() ),
m_nDirection( rNew.GetDirection() ),
- m_nExtraSpace( rNew.GetExtraSpace() )
+ m_nExtraSpace( rNew.GetExtraSpace() ),
+ m_nBreakWidth( rNew.GetBreakWidth() )
{
#if OSL_DEBUG_LEVEL > 0
ChkOutDev( *this );
@@ -2283,4 +2287,30 @@ bool SwTextFormatInfo::CheckCurrentPosBookmark()
}
}
+sal_Int32 SwTextFormatInfo::GetLineSpaceCount(TextFrameIndex nBreakPos)
+{
+ if ( sal_Int32(nBreakPos) >= GetText().getLength() )
+ return 0;
+
+ sal_Int32 nSpaces = 0;
+ sal_Int32 nInlineSpaces = -1;
+ for (sal_Int32 i = sal_Int32(GetLineStart()); i < sal_Int32(nBreakPos); ++i)
+ {
+ sal_Unicode cChar = GetText()[i];
+ if ( cChar == CH_BLANK )
+ ++nSpaces;
+ else
+ {
+ if ( nInlineSpaces == -1 )
+ {
+ nInlineSpaces = 0;
+ nSpaces = 0;
+ }
+ else
+ nInlineSpaces = nSpaces;
+ }
+ }
+ return nInlineSpaces == -1 ? 0: nInlineSpaces;
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/inftxt.hxx b/sw/source/core/text/inftxt.hxx
index 6440c76340f7..4792ce8399d1 100644
--- a/sw/source/core/text/inftxt.hxx
+++ b/sw/source/core/text/inftxt.hxx
@@ -174,6 +174,7 @@ protected:
bool m_bSnapToGrid : 1; // paragraph snaps to grid
sal_uInt8 m_nDirection : 2; // writing direction: 0/90/180/270 degree
SwTwips m_nExtraSpace; // extra space before shrinking = nSpacesInLine * (nSpaceWidth/0.8 - nSpaceWidth)
+ SwTwips m_nBreakWidth; // break width to calculate space width at justification
protected:
void CtorInitTextSizeInfo( OutputDevice* pRenderContext, SwTextFrame *pFrame,
@@ -302,6 +303,9 @@ public:
// extra space before shrinking = nSpacesInLine * (nSpaceWidth/0.8 - nSpaceWidth)
void SetExtraSpace(SwTwips nVal) { m_nExtraSpace = nVal; }
SwTwips GetExtraSpace() const { return m_nExtraSpace; }
+ // set break width to calculate space width later
+ void SetBreakWidth(SwTwips nVal) { m_nBreakWidth = nVal; }
+ SwTwips GetBreakWidth() const { return m_nBreakWidth; }
// If Kana Compression is enabled, a minimum and maximum portion width
// is calculated. We format lines with minimal size and share remaining
@@ -711,6 +715,9 @@ public:
void SetTabOverflow( bool bOverflow ) { m_bTabOverflow = bOverflow; }
bool IsTabOverflow() const { return m_bTabOverflow; }
+ // get line space count between line start and break position
+ // by stripping also terminating spaces
+ sal_Int32 GetLineSpaceCount(TextFrameIndex nBreakPos);
};
/**
diff --git a/sw/source/core/text/portxt.cxx b/sw/source/core/text/portxt.cxx
index df577062e712..36ad3e50f5a3 100644
--- a/sw/source/core/text/portxt.cxx
+++ b/sw/source/core/text/portxt.cxx
@@ -367,19 +367,19 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
const SvxAdjust aAdjust = aAdjustItem.GetAdjust();
bool bFullJustified = bFull && aAdjust == SvxAdjust::Block &&
pGuess->BreakPos() != TextFrameIndex(COMPLETE_STRING);
- bool bInteropSmartJustify = bFullJustified &&
+ bool bInteropSmartJustify =
rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
- DocumentSettingId::JUSTIFY_LINES_WITH_SHRINKING) &&
- // support different Kashida etc. values
- aAdjustItem.GetPropWordSpacing() == 100 &&
+ DocumentSettingId::JUSTIFY_LINES_WITH_SHRINKING);
+ bool bNoWordSpacing = aAdjustItem.GetPropWordSpacing() == 100 &&
aAdjustItem.GetPropWordSpacingMinimum() == 100 &&
aAdjustItem.GetPropWordSpacingMaximum() == 100;
- bool bWordSpacing = bFullJustified && !bInteropSmartJustify &&
- aAdjustItem.GetPropWordSpacing() != 100;
- bool bWordSpacingMaximum = bFullJustified && !bInteropSmartJustify &&
+ // support old ODT documents, where only JustifyLinesWithShrinking was set
+ bool bOldInterop = bInteropSmartJustify && bNoWordSpacing;
+ bool bWordSpacing = bFullJustified && (!bNoWordSpacing || bOldInterop);
+ bool bWordSpacingMaximum = bWordSpacing && !bOldInterop &&
aAdjustItem.GetPropWordSpacingMaximum() > aAdjustItem.GetPropWordSpacing();
- bool bWordSpacingMinimum = bFullJustified && !bInteropSmartJustify &&
- aAdjustItem.GetPropWordSpacingMinimum() < aAdjustItem.GetPropWordSpacing();
+ bool bWordSpacingMinimum = bWordSpacing && ( bOldInterop ||
+ aAdjustItem.GetPropWordSpacingMinimum() < aAdjustItem.GetPropWordSpacing() );
if ( ( bInteropSmartJustify || bWordSpacing || bWordSpacingMaximum || bWordSpacingMinimum ) &&
// tdf#164499 no shrinking in tabulated line
@@ -388,13 +388,9 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
// very short word resulted endless loop
!rInf.IsUnderflow() )
{
- sal_Int32 nSpacesInLine(0);
- for (sal_Int32 i = sal_Int32(rInf.GetLineStart()); i < sal_Int32(pGuess->BreakPos()); ++i)
- {
- sal_Unicode cChar = rInf.GetText()[i];
- if ( cChar == CH_BLANK )
- ++nSpacesInLine;
- }
+ sal_Int32 nSpacesInLine = rInf.GetLineSpaceCount( pGuess->BreakPos() );
+ sal_Int32 nSpacesInLineOrig = nSpacesInLine;
+ SwTextSizeInfo aOrigInf( rInf );
// call with an extra space: shrinking can result a new word in the line
// and a new space before that, which is also a shrank space
@@ -410,70 +406,111 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
++nSpacesInLine;
}
- if ( nSpacesInLine > 0 )
+ // there are spaces in the line, so it's possible to shrink them
+ if ( nSpacesInLineOrig > 0 )
{
SwTwips nOldWidth = pGuess->BreakWidth();
- if ( bInteropSmartJustify )
+ bool bIsPortion = rInf.GetLineWidth() < rInf.GetBreakWidth();
+
+ // measure ten spaces for higher precision
+ static constexpr OUStringLiteral STR_BLANK = u" ";
+ sal_Int16 nSpaceWidth = rInf.GetTextSize(STR_BLANK).Width();
+ sal_Int32 nRealSpaces = rInf.GetLineSpaceCount( pGuess->BreakPos() );
+ float fSpaceNormal = (rInf.GetLineWidth() - (rInf.GetBreakWidth() - nRealSpaces * nSpaceWidth/10.0))/nRealSpaces;
+
+ bool bOrigHyphenated = pGuess->HyphWord().is() &&
+ pGuess->BreakPos() > rInf.GetLineStart();
+ // calculate line breaking with desired word spacing, also
+ // if the desired word spacing is 100%, but there is a greater
+ // maximum word spacing, and the word is hyphenated at the desired
+ // word spacing: to skip hyphenation, if the maximum word spacing allows it
+ if ( bWordSpacing || ( bWordSpacingMaximum && bOrigHyphenated ) )
{
pGuess.emplace();
- bFull = !pGuess->Guess( *this, rInf, Height(), nSpacesInLine, SAL_MAX_UINT16 );
+ bFull = !pGuess->Guess( *this, rInf, Height(), nSpacesInLine, aAdjustItem.GetPropWordSpacing(), nSpaceWidth );
+ sal_Int32 nSpacesInLine2 = rInf.GetLineSpaceCount( pGuess->BreakPos() );
+
+ if ( rInf.GetBreakWidth() <= rInf.GetLineWidth() )
+ fSpaceNormal = (rInf.GetLineWidth() - (rInf.GetBreakWidth() - nSpacesInLine2 * nSpaceWidth/10.0))/nSpacesInLine2;
}
- else
+
+ sal_Int32 nSpacesInLineShrink = 0;
+ // TODO if both maximum word spacing or minimum word spacing can disable hyphenation, prefer the last one
+ if ( bWordSpacingMinimum )
{
- bool bOrigHyphenated = pGuess->HyphWord().is() &&
- pGuess->BreakPos() > rInf.GetLineStart();
- if ( bWordSpacing || bWordSpacingMaximum )
+ std::optional pGuess2(std::in_place);
+ SwTwips nOldExtraSpace = rInf.GetExtraSpace();
+ // break the line after the hyphenated word, if it's possible
+ // (hyphenation is disabled in Guess(), when called with GetPropWordSpacingMinimum())
+ sal_uInt16 nMinimum = bOldInterop ? 75 : aAdjustItem.GetPropWordSpacingMinimum();
+ bool bFull2 = !pGuess2->Guess( *this, rInf, Height(), nSpacesInLine, nMinimum, nSpaceWidth );
+ nSpacesInLineShrink = rInf.GetLineSpaceCount( pGuess2->BreakPos() );
+ if ( pGuess2->BreakWidth() > nOldWidth )
{
- pGuess.emplace();
- bFull = !pGuess->Guess( *this, rInf, Height(), nSpacesInLine, aAdjustItem.GetPropWordSpacing() );
- }
- // if both maximum word spacing or minimum word spacing can disable
- // hyphenation, prefer the last one
- if ( bWordSpacingMinimum && ( bWordSpacingMaximum ||
- ( pGuess->HyphWord().is() && pGuess->BreakPos() > rInf.GetLineStart() ) ) &&
- // if the desired word spacing is 100% (!bWordSpacing), and it was possible
- // to break the line without hyphenation in the first run (where maximum
- // word spacing was not used), no need to check minimum word spacing
- // FIXME: avoid too much shrinking, if desired word spacing is not 100%
- ( bWordSpacing || bOrigHyphenated )
- )
- {
- std::optional pGuess2(std::in_place);
- SwTwips nOldExtraSpace = rInf.GetExtraSpace();
- // break the line after the hyphenated word, if it's possible
- // (hyphenation is disabled in Guess(), when called with GetPropWordSpacingMinimum())
- bool bFull2 = !pGuess2->Guess( *this, rInf, Height(), nSpacesInLine, aAdjustItem.GetPropWordSpacingMinimum() );
- if ( pGuess2->BreakWidth() > nOldWidth )
+ // instead of the maximum shrinking, break after the word which was hyphenated before
+ sal_Int32 i = sal_Int32(pGuess->BreakPos());
+ sal_Int32 j = sal_Int32(pGuess2->BreakPos());
+ // skip terminal spaces
+ for (; i < j && rInf.GetText()[i] == CH_BLANK; ++i);
+ for (; j > i && rInf.GetText()[i] == CH_BLANK; --j);
+ sal_Int32 nOldBreakTrim = i;
+ sal_Int32 nOldBreak = j - i;
+ for (; i < j; ++i)
{
- // instead of the maximum shrinking, break after the word which was hyphenated before
- for (sal_Int32 i = sal_Int32(pGuess->BreakPos()); i < sal_Int32(pGuess2->BreakPos()); ++i)
+ sal_Unicode cChar = rInf.GetText()[i];
+ // first space after the hyphenated word, and it's not the chosen one
+ if ( cChar == CH_BLANK )
{
- sal_Unicode cChar = rInf.GetText()[i];
- // first space after the hyphenated word, and it's not the chosen one
- if ( cChar == CH_BLANK )
+ // using a weighted word spacing, try to break the line after the hyphenated word
+ sal_Int32 nNewBreak = i - nOldBreakTrim;
+ SwTwips nWeightedSpacing = nMinimum * (1.0 * nNewBreak/nOldBreak) +
+ aAdjustItem.GetPropWordSpacing() * (1.0 * (nOldBreak - nNewBreak)/nOldBreak);
+ std::optional pGuess3(std::in_place);
+ pGuess3->Guess( *this, rInf, Height(), nSpacesInLineShrink-1, nWeightedSpacing, nSpaceWidth );
+
+ sal_Int32 nSpacesInLineShrink2 = rInf.GetLineSpaceCount( pGuess3->BreakPos() );
+ if ( nSpacesInLineShrink2 == nSpacesInLineShrink )
{
- // using a weighted word spacing, try to break the line after the hyphenated word
- sal_Int32 nOldBreak = sal_Int32(pGuess2->BreakPos()) - sal_Int32(pGuess->BreakPos());
- sal_Int32 nNewBreak = i - sal_Int32(pGuess->BreakPos());
- SwTwips nWeightedSpacing = aAdjustItem.GetPropWordSpacingMinimum() * (1.0 * nNewBreak/nOldBreak) +
- aAdjustItem.GetPropWordSpacing() * (1.0 * (nOldBreak - nNewBreak)/nOldBreak);
- std::optional pGuess3(std::in_place);
- pGuess3->Guess( *this, rInf, Height(), nSpacesInLine, nWeightedSpacing );
- if ( pGuess3->BreakWidth() > nOldWidth )
- {
- pGuess2.emplace();
- pGuess2 = std::move(pGuess3);
- }
- break;
+ nNewBreak = i - nOldBreakTrim - 1;
+ nWeightedSpacing = nMinimum * (1.0 * nNewBreak/nOldBreak) +
+ aAdjustItem.GetPropWordSpacing() * (1.0 * (nOldBreak - nNewBreak)/nOldBreak);
+ pGuess3->Guess( *this, rInf, Height(), nSpacesInLineShrink-1, nWeightedSpacing, nSpaceWidth );
}
+
+ if ( pGuess3->BreakWidth() > nOldWidth )
+ {
+ pGuess2.emplace();
+ pGuess2 = std::move(pGuess3);
+ }
+ break;
}
+ }
+
+ nSpacesInLineShrink = rInf.GetLineSpaceCount( pGuess2->BreakPos() );
+ if ( rInf.GetBreakWidth() > rInf.GetLineWidth() || bIsPortion )
+ {
+ float fExpansionWeight = static_cast(1/1.7);
+ float fSpaceShrunk = nSpacesInLineShrink > 0
+ ? (rInf.GetLineWidth() - (rInf.GetBreakWidth() - nSpacesInLineShrink * nSpaceWidth/10.0))/nSpacesInLineShrink
+ : 0;
+ float z0 = (nSpaceWidth/10.0)/fSpaceShrunk;
+ float z1 = (nSpaceWidth/10.0+((fSpaceNormal-nSpaceWidth/10.0)*fExpansionWeight))/(nSpaceWidth/10.0);
+ // TODO shrink line portions only if needed
+ if ( z1 >= z0 || bIsPortion )
+ {
+ pGuess = std::move(pGuess2);
+ bFull = bFull2;
+ }
+ }
+ else if ( bOldInterop )
+ {
pGuess = std::move(pGuess2);
bFull = bFull2;
}
- else
- // minimum word spacing is not applicable
- rInf.SetExtraSpace(nOldExtraSpace);
}
+ else
+ // minimum word spacing is not applicable
+ rInf.SetExtraSpace(nOldExtraSpace);
}
if ( pGuess->BreakWidth() != nOldWidth )
diff --git a/sw/source/writerfilter/dmapper/DomainMapper.cxx b/sw/source/writerfilter/dmapper/DomainMapper.cxx
index 8ee662a8b3d7..51239d40bed8 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper.cxx
@@ -4887,6 +4887,12 @@ void DomainMapper::handleParaJustification(const sal_Int32 nIntValue, const ::to
case NS_ooxml::LN_Value_ST_Jc_both:
nAdjust = style::ParagraphAdjust_BLOCK;
aStringValue = "both";
+ // set default smart justify
+ if ( GetSettingsTable()->GetWordCompatibilityMode() >= 15 )
+ {
+ rContext->Insert( PROP_PARA_WORD_SPACING_MINIMUM, uno::Any( sal_uInt16(75) ) );
+ rContext->Insert( PROP_PARA_WORD_SPACING_MAXIMUM, uno::Any( sal_uInt16(133) ) );
+ }
break;
case NS_ooxml::LN_Value_ST_Jc_lowKashida:
nAdjust = style::ParagraphAdjust_BLOCK;