sw/qa/core/text/text.cxx | 52 +++++++++++++++++++++++++++++++++++++++++ sw/qa/unit/swmodeltestbase.cxx | 6 ++-- sw/source/core/inc/txtfly.hxx | 11 ++------ sw/source/core/text/inftxt.cxx | 27 ++++++++++++++++++++- sw/source/core/text/porrst.cxx | 2 - sw/source/core/text/txtfly.cxx | 37 +++++++++++++++++++++++------ 6 files changed, 115 insertions(+), 20 deletions(-)
New commits: commit 6c6d9a5457af20f14a0e6344efa19b93e0f7c026 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Mon Mar 28 08:45:53 2022 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Thu Mar 31 13:10:25 2022 +0200 sw clearing breaks: add layout support for the left and right cases This means that the vertical position calculated in SwTextFly now depend son the horizontal position of the break portion, so it doesn't make sense to cache it. (cherry picked from commit e799ebfd7a2deab13b47092807335670abb7b485) Change-Id: I4e30bb12d9ba117d3af065881a65a1c2001e1164 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132353 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/qa/core/text/text.cxx b/sw/qa/core/text/text.cxx index 9987fac58a3f..0923758694b8 100644 --- a/sw/qa/core/text/text.cxx +++ b/sw/qa/core/text/text.cxx @@ -293,6 +293,58 @@ CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testClearingLineBreakAtStart) assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout[1]", "height", "1024"); } +CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testClearingLineBreakLeft) +{ + // Given a document with two anchored objects (left height is 5cm, right height is 7.5cm) and a + // clearing break (type=left): + 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<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::makeAny(text::TextContentAnchorType_AT_CHARACTER)); + uno::Reference<text::XTextContent> xShapeContent(xShape, uno::UNO_QUERY); + xText->insertTextContent(xCursor, xShapeContent, /*bAbsorb=*/false); + } + { + uno::Reference<drawing::XShape> xShape( + xFactory->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY); + xShape->setSize(awt::Size(5000, 7500)); + uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); + xShapeProps->setPropertyValue("AnchorType", + uno::makeAny(text::TextContentAnchorType_AT_CHARACTER)); + xShapeProps->setPropertyValue("HoriOrientPosition", uno::makeAny(sal_Int32(10000))); + uno::Reference<text::XTextContent> xShapeContent2(xShape, uno::UNO_QUERY); + xText->insertTextContent(xCursor, xShapeContent2, /*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::LEFT); + xLineBreakProps->setPropertyValue("Clear", uno::makeAny(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" jumps down below the left shape, but not below the right shape (due + // to type=left): + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 2837 + // - Actual : 4254 + // i.e. any non-none type was handled as type=all, and this was jumping below both shapes. + assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout[1]", "height", "2837"); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/unit/swmodeltestbase.cxx b/sw/qa/unit/swmodeltestbase.cxx index ee35a1cc7259..28ee075120fa 100644 --- a/sw/qa/unit/swmodeltestbase.cxx +++ b/sw/qa/unit/swmodeltestbase.cxx @@ -218,9 +218,9 @@ xmlDocUniquePtr SwModelTestBase::parseLayoutDump() if (!mpXmlBuffer) dumpLayout(mxComponent); - return xmlDocUniquePtr( - xmlParseMemory(reinterpret_cast<const char*>(xmlBufferContent(mpXmlBuffer)), - xmlBufferLength(mpXmlBuffer))); + auto pBuffer = reinterpret_cast<const char*>(xmlBufferContent(mpXmlBuffer)); + SAL_INFO("sw", "SwModelTestBase::parseLayoutDump: pBuffer is '" << pBuffer << "'"); + return xmlDocUniquePtr(xmlParseMemory(pBuffer, xmlBufferLength(mpXmlBuffer))); } OUString SwModelTestBase::parseDump(const OString& aXPath, const OString& aAttribute) diff --git a/sw/source/core/inc/txtfly.hxx b/sw/source/core/inc/txtfly.hxx index 82ee5f085d6a..f5d913f7df9d 100644 --- a/sw/source/core/inc/txtfly.hxx +++ b/sw/source/core/inc/txtfly.hxx @@ -36,6 +36,8 @@ class SwAnchoredObject; class SwTextFrame; class SwDrawTextInfo; class SwContourCache; +class SwBreakPortion; +class SwTextFormatInfo; typedef std::vector< SwAnchoredObject* > SwAnchoredObjList; @@ -126,7 +128,6 @@ class SwTextFly std::unique_ptr<SwAnchoredObjList> mpAnchoredObjList; tools::Long m_nMinBottom; - mutable tools::Long m_nMaxBottom; tools::Long m_nNextTop; /// Stores the upper edge of the "next" frame SwNodeOffset m_nCurrFrameNodeIndex; @@ -202,7 +203,6 @@ class SwTextFly const bool bInFooterOrHeader ); SwTwips CalcMinBottom() const; - SwTwips CalcMaxBottom() const; const SwTextFrame* GetMaster_(); @@ -232,7 +232,7 @@ public: SwTwips GetMinBottom() const; /// Gets the maximum of the fly frame bottoms. - SwTwips GetMaxBottom() const; + SwTwips GetMaxBottom(const SwBreakPortion& rPortion, const SwTextFormatInfo& rInfo) const; const SwTextFrame* GetMaster() const; @@ -345,11 +345,6 @@ inline SwTwips SwTextFly::GetMinBottom() const return mpAnchoredObjList ? m_nMinBottom : CalcMinBottom(); } -inline SwTwips SwTextFly::GetMaxBottom() const -{ - return mpAnchoredObjList ? m_nMaxBottom : CalcMaxBottom(); -} - inline const SwTextFrame* SwTextFly::GetMaster() const { return m_pMaster ? m_pMaster : const_cast<SwTextFly*>(this)->GetMaster_(); diff --git a/sw/source/core/text/porrst.cxx b/sw/source/core/text/porrst.cxx index 82467d110aa6..b5e98e913f14 100644 --- a/sw/source/core/text/porrst.cxx +++ b/sw/source/core/text/porrst.cxx @@ -182,7 +182,7 @@ bool SwBreakPortion::Format( SwTextFormatInfo &rInf ) SwTextFly& rTextFly = rInf.GetTextFly(); if (rTextFly.IsOn()) { - SwTwips nHeight = rTextFly.GetMaxBottom() - rInf.Y(); + SwTwips nHeight = rTextFly.GetMaxBottom(*this, rInf) - rInf.Y(); if (nHeight > Height()) { Height(nHeight, /*bText=*/false); diff --git a/sw/source/core/text/txtfly.cxx b/sw/source/core/text/txtfly.cxx index aa193c50220f..1b9c3b55308a 100644 --- a/sw/source/core/text/txtfly.cxx +++ b/sw/source/core/text/txtfly.cxx @@ -33,6 +33,8 @@ #include <frmtool.hxx> #include <ndtxt.hxx> #include <txtfly.hxx> +#include "inftxt.hxx" +#include "porrst.hxx" #include "txtpaint.hxx" #include <notxtfrm.hxx> #include <fmtcnct.hxx> @@ -48,6 +50,7 @@ #include <sortedobjs.hxx> #include <IDocumentDrawModelAccess.hxx> #include <IDocumentSettingAccess.hxx> +#include <formatlinebreak.hxx> #include <svx/svdoedge.hxx> #ifdef DBG_UTIL @@ -309,7 +312,6 @@ SwTextFly::SwTextFly() , m_pCurrFrame(nullptr) , m_pMaster(nullptr) , m_nMinBottom(0) - , m_nMaxBottom(0) , m_nNextTop(0) , m_nCurrFrameNodeIndex(0) , m_bOn(false) @@ -340,7 +342,6 @@ SwTextFly::SwTextFly( const SwTextFly& rTextFly ) m_bOn = rTextFly.m_bOn; m_bTopRule = rTextFly.m_bTopRule; m_nMinBottom = rTextFly.m_nMinBottom; - m_nMaxBottom = rTextFly.m_nMaxBottom; m_nNextTop = rTextFly.m_nNextTop; m_nCurrFrameNodeIndex = rTextFly.m_nCurrFrameNodeIndex; mbIgnoreCurrentFrame = rTextFly.mbIgnoreCurrentFrame; @@ -371,7 +372,6 @@ void SwTextFly::CtorInitTextFly( const SwTextFrame *pFrame ) m_bOn = m_pPage->GetSortedObjs() != nullptr; m_bTopRule = true; m_nMinBottom = 0; - m_nMaxBottom = 0; m_nNextTop = 0; m_nCurrFrameNodeIndex = NODE_OFFSET_MAX; } @@ -958,8 +958,6 @@ SwAnchoredObjList* SwTextFly::InitAnchoredObjList() mpAnchoredObjList.reset( new SwAnchoredObjList ); } - CalcMaxBottom(); - // #i68520# return mpAnchoredObjList.get(); } @@ -998,22 +996,47 @@ SwTwips SwTextFly::CalcMinBottom() const return nRet; } -SwTwips SwTextFly::CalcMaxBottom() const +SwTwips SwTextFly::GetMaxBottom(const SwBreakPortion& rPortion, const SwTextFormatInfo& rInfo) const { 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()); + for (size_t i = 0; i < nCount; ++i) { const SwAnchoredObject* pAnchoredObj = (*mpAnchoredObjList)[i]; SwRect aRect(pAnchoredObj->GetObjRectWithSpaces()); + if (rPortion.GetClear() == SwLineBreakClear::LEFT) + { + if (nX < aRectFnSet.GetLeft(aRect)) + { + // 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. + continue; + } + } + if (rPortion.GetClear() == SwLineBreakClear::RIGHT) + { + if (nX > aRectFnSet.GetRight(aRect)) + { + // 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); if (nBottom > nRet) { nRet = nBottom; } } - m_nMaxBottom = nRet; return nRet; } commit 3704aa48d00f96864679c3bdbcbf3f6dbeaf4e4c Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Fri Mar 25 08:37:22 2022 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Thu Mar 31 13:10:11 2022 +0200 sw clearing breaks: add clearing indicator during rendering A left / right line around the break portion now allows seeing if the clearing is none, left, right or all (somewhat familiar from Word). No test for this, SwBreakPortion::Paint() is a NOP unless rendering on a window, so the metafile-based rendering used for testing won't detect the difference. (cherry picked from commit eb53efed80302c4ca6409c6dbd023d8ba1eb8e47) Change-Id: I3ff0c89bc4bb45deb03bea43c3ee4589887dee7c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132352 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx index 16af09c8ccbf..422187624cd9 100644 --- a/sw/source/core/text/inftxt.cxx +++ b/sw/source/core/text/inftxt.cxx @@ -67,6 +67,7 @@ #include <vcl/virdev.hxx> #include <vcl/gradient.hxx> #include <i18nlangtag/mslangid.hxx> +#include <formatlinebreak.hxx> using namespace ::com::sun::star; using namespace ::com::sun::star::linguistic2; @@ -964,6 +965,13 @@ void SwTextPaintInfo::DrawLineBreak( const SwLinePortion &rPor ) const if( !OnWin() ) return; + SwLineBreakClear eClear = SwLineBreakClear::NONE; + if (rPor.IsBreakPortion()) + { + const auto& rBreakPortion = static_cast<const SwBreakPortion&>(rPor); + eClear = rBreakPortion.GetClear(); + } + sal_uInt16 nOldWidth = rPor.Width(); const_cast<SwLinePortion&>(rPor).Width( LINE_BREAK_WIDTH ); @@ -976,7 +984,24 @@ void SwTextPaintInfo::DrawLineBreak( const SwLinePortion &rPor ) const CHAR_LINEBREAK_RTL : CHAR_LINEBREAK; const sal_uInt8 nOptions = 0; - lcl_DrawSpecial( *this, rPor, aRect, NON_PRINTING_CHARACTER_COLOR, cChar, nOptions ); + SwRect aTextRect(aRect); + if (eClear == SwLineBreakClear::LEFT || eClear == SwLineBreakClear::ALL) + aTextRect.AddLeft(30); + if (eClear == SwLineBreakClear::RIGHT || eClear == SwLineBreakClear::ALL) + aTextRect.AddRight(-30); + lcl_DrawSpecial( *this, rPor, aTextRect, NON_PRINTING_CHARACTER_COLOR, cChar, nOptions ); + + if (eClear != SwLineBreakClear::NONE) + { + // Paint indicator if this clear is left/right/all. + m_pOut->Push(vcl::PushFlags::LINECOLOR); + m_pOut->SetLineColor(NON_PRINTING_CHARACTER_COLOR); + if (eClear != SwLineBreakClear::RIGHT) + m_pOut->DrawLine(aRect.BottomLeft(), aRect.TopLeft()); + if (eClear != SwLineBreakClear::LEFT) + m_pOut->DrawLine(aRect.BottomRight(), aRect.TopRight()); + m_pOut->Pop(); + } } const_cast<SwLinePortion&>(rPor).Width( nOldWidth );