sw/qa/core/text/text.cxx | 43 +++++++++++++++++++++++++++++++++++++++++ sw/source/core/text/itrtxt.cxx | 37 ++++++++++++++++++++++++++++++++++- sw/source/core/text/porrst.cxx | 16 ++++++++++++++- sw/source/core/text/txtfly.cxx | 19 ++++++++++++------ 4 files changed, 107 insertions(+), 8 deletions(-)
New commits: commit 251589c17c0dfa33cf5e5e5bb1ed782d2293a67c Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Mon Aug 15 15:00:47 2022 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Mon Aug 15 20:43:03 2022 +0200 tdf#148289 sw clearing breaks: fix vertical layout There were 3 problems here: 1) SwTextFly::GetMaxBottom() gets an already swapped frame, so going via the aRectFnSet abstraction is not correct, need to use Left() and similar functions directly. 2) SwTextCursor::AdjustBaseLine() centers portions in the vertical case, so a clearing break portion with larger height will push the other portions towards the left, and this is not wanted. Fix this by filtering out the (logic) height of the clearing break portions in the vertical case. 3) The fix for 2) has the side effect that the non-printable line break indicator character now has the wrong (logic) top position in SwBreakPortion::Paint(). Fix this by compensating for the offset done in AdjustBaseLine(). No functional changes intended for the horizontal layout. Change-Id: I9ea27b4247944cbab8e96c5c5e2c8f82bbbd731c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/138314 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins diff --git a/sw/qa/core/text/text.cxx b/sw/qa/core/text/text.cxx index ddd39bff7b9f..46d7c5156b12 100644 --- a/sw/qa/core/text/text.cxx +++ b/sw/qa/core/text/text.cxx @@ -412,6 +412,49 @@ CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testClearingLineBreakLeftRTL) assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout[1]", "height", "276"); } +CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testClearingLineBreakVertical) +{ + // Given a document with an anchored object in a vertical page and a clearing break (type=all): + loadURL("private:factory/swriter", nullptr); + uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + uno::Reference<beans::XPropertySet> xStandard(getStyles("PageStyles")->getByName("Standard"), + uno::UNO_QUERY); + xStandard->setPropertyValue("WritingMode", uno::Any(text::WritingMode2::TB_RL)); + { + uno::Reference<drawing::XShape> xShape( + xFactory->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY); + xShape->setSize(awt::Size(5000, 5000)); + uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); + xShapeProps->setPropertyValue("AnchorType", + uno::Any(text::TextContentAnchorType_AT_CHARACTER)); + uno::Reference<text::XTextContent> xShapeContent(xShape, uno::UNO_QUERY); + xText->insertTextContent(xCursor, xShapeContent, /*bAbsorb=*/false); + } + uno::Reference<text::XTextContent> xLineBreak( + xFactory->createInstance("com.sun.star.text.LineBreak"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xLineBreakProps(xLineBreak, uno::UNO_QUERY); + auto eClear = static_cast<sal_Int16>(SwLineBreakClear::ALL); + xLineBreakProps->setPropertyValue("Clear", uno::Any(eClear)); + xText->insertString(xCursor, "foo", /*bAbsorb=*/false); + xText->insertTextContent(xCursor, xLineBreak, /*bAbsorb=*/false); + xText->insertString(xCursor, "bar", /*bAbsorb=*/false); + + // When laying out that document: + calcLayout(); + + // Then make sure the "bar" does jump (logic) down the correct amount: + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 2837 + // - Actual : 7135 + // i.e. the expected break height is the twips value of the 5cm rectangle size, it was much + // more. + assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout[1]/SwBreakPortion", "height", "2837"); +} + CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testClearingLineBreakHeader) { // Given a document with a shape in the header and a clearing break in the body text: diff --git a/sw/source/core/text/itrtxt.cxx b/sw/source/core/text/itrtxt.cxx index 53e084b03736..1d1eed3e0837 100644 --- a/sw/source/core/text/itrtxt.cxx +++ b/sw/source/core/text/itrtxt.cxx @@ -27,6 +27,7 @@ #include <pagefrm.hxx> #include <tgrditem.hxx> #include "porfld.hxx" +#include "porrst.hxx" #include "itrtxt.hxx" #include <txtfrm.hxx> @@ -283,7 +284,41 @@ SwTwips SwTextCursor::AdjustBaseLine( const SwLineLayout& rLine, if (GetInfo().GetTextFrame()->IsVertLR() && !GetInfo().GetTextFrame()->IsVertLRBT()) nOfst += rLine.Height() - ( rLine.Height() - nPorHeight ) / 2 - nPorAscent; else - nOfst += ( rLine.Height() - nPorHeight ) / 2 + nPorAscent; + { + SwTwips nLineHeight = 0; + bool bHadClearingBreak = false; + if (GetInfo().GetTextFrame()->IsVertical()) + { + // Ignore the height of clearing break portions in the automatic + // alignment case. + const SwLinePortion* pLinePor = rLine.GetFirstPortion(); + while (pLinePor) + { + bool bClearingBreak = false; + if (pLinePor->IsBreakPortion()) + { + auto pBreakPortion = static_cast<const SwBreakPortion*>(pLinePor); + bClearingBreak = pBreakPortion->GetClear() != SwLineBreakClear::NONE; + if (bClearingBreak) + { + bHadClearingBreak = true; + } + } + if (!bClearingBreak && pLinePor->Height() > nLineHeight) + { + nLineHeight = pLinePor->Height(); + } + pLinePor = pLinePor->GetNextPortion(); + } + } + + if (!bHadClearingBreak) + { + nLineHeight = rLine.Height(); + } + + nOfst += ( nLineHeight - nPorHeight ) / 2 + nPorAscent; + } break; } [[fallthrough]]; diff --git a/sw/source/core/text/porrst.cxx b/sw/source/core/text/porrst.cxx index 0afa5ef37fcd..e863ba29641d 100644 --- a/sw/source/core/text/porrst.cxx +++ b/sw/source/core/text/porrst.cxx @@ -131,9 +131,23 @@ void SwBreakPortion::Paint( const SwTextPaintInfo &rInf ) const // Reduce height to text height for the duration of the print, so the vertical height will look // correct for the line break character, even for clearing breaks. SwTwips nHeight = Height(); + SwTwips nVertPosOffset = (nHeight - m_nTextHeight) / 2; auto pPortion = const_cast<SwBreakPortion*>(this); pPortion->Height(m_nTextHeight, false); - comphelper::ScopeGuard g([pPortion, nHeight] { pPortion->Height(nHeight, false); }); + if (rInf.GetTextFrame()->IsVertical()) + { + // Compensate for the offset done in SwTextCursor::AdjustBaseLine() for the vertical case. + const_cast<SwTextPaintInfo&>(rInf).Y(rInf.Y() + nVertPosOffset); + } + comphelper::ScopeGuard g( + [pPortion, nHeight, &rInf, nVertPosOffset] + { + if (rInf.GetTextFrame()->IsVertical()) + { + const_cast<SwTextPaintInfo&>(rInf).Y(rInf.Y() - nVertPosOffset); + } + pPortion->Height(nHeight, false); + }); rInf.DrawLineBreak( *this ); diff --git a/sw/source/core/text/txtfly.cxx b/sw/source/core/text/txtfly.cxx index bc526b27f14b..ccb9ff00306b 100644 --- a/sw/source/core/text/txtfly.cxx +++ b/sw/source/core/text/txtfly.cxx @@ -998,16 +998,17 @@ SwTwips SwTextFly::CalcMinBottom() const SwTwips SwTextFly::GetMaxBottom(const SwBreakPortion& rPortion, const SwTextFormatInfo& rInfo) const { + // Note that m_pCurrFrame is already swapped at this stage, so it's correct to bypass + // SwRectFnSet here. SwTwips nRet = 0; size_t nCount(m_bOn ? GetAnchoredObjList()->size() : 0); - SwRectFnSet aRectFnSet(m_pCurrFrame); // Get the horizontal position of the break portion in absolute twips. The frame area is in // absolute twips, the frame's print area is relative to the frame area. Finally the portion's // position is relative to the frame's print area. SwTwips nX = rInfo.X(); - nX += aRectFnSet.GetLeft(m_pCurrFrame->getFrameArea()); - nX += aRectFnSet.GetLeft(m_pCurrFrame->getFramePrintArea()); + nX += m_pCurrFrame->getFrameArea().Left(); + nX += m_pCurrFrame->getFramePrintArea().Left(); for (size_t i = 0; i < nCount; ++i) { @@ -1020,9 +1021,15 @@ SwTwips SwTextFly::GetMaxBottom(const SwBreakPortion& rPortion, const SwTextForm } SwRect aRect(pAnchoredObj->GetObjRectWithSpaces()); + + if (m_pCurrFrame->IsVertical()) + { + m_pCurrFrame->SwitchVerticalToHorizontal(aRect); + } + if (rPortion.GetClear() == SwLineBreakClear::LEFT) { - if (nX < aRectFnSet.GetLeft(aRect)) + if (nX < aRect.Left()) { // Want to jump down to the first line that's unblocked on the left. This object is // on the right of the break, ignore it. @@ -1031,14 +1038,14 @@ SwTwips SwTextFly::GetMaxBottom(const SwBreakPortion& rPortion, const SwTextForm } if (rPortion.GetClear() == SwLineBreakClear::RIGHT) { - if (nX > aRectFnSet.GetRight(aRect)) + if (nX > aRect.Right()) { // Want to jump down to the first line that's unblocked on the right. This object is // on the left of the break, ignore it. continue; } } - SwTwips nBottom = aRectFnSet.GetBottom(aRect); + SwTwips nBottom = aRect.Top() + aRect.Height(); if (nBottom > nRet) { nRet = nBottom;