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;

Reply via email to