[Libreoffice-commits] core.git: Branch 'distro/vector/vector-7.0-10.0' - 3 commits - sw/qa sw/source

Michael Stahl (via logerrit) logerrit at kemper.freedesktop.org
Fri Oct 8 13:51:52 UTC 2021


 sw/qa/extras/layout/data/tdf134298.ott |binary
 sw/qa/extras/layout/data/tdf138039.odt |binary
 sw/qa/extras/layout/layout.cxx         |   45 +++++++++++++
 sw/source/core/inc/pagefrm.hxx         |    3 
 sw/source/core/layout/layact.cxx       |   20 +++---
 sw/source/core/layout/pagechg.cxx      |  108 ++++++++++++++++++++-------------
 sw/source/core/layout/tabfrm.cxx       |   36 ++++++++++-
 sw/source/core/layout/wsfrm.cxx        |   16 ++++
 8 files changed, 177 insertions(+), 51 deletions(-)

New commits:
commit 72669b98cf2c71fb0006b2d100248e8234b1b61b
Author:     Michael Stahl <Michael.Stahl at cib.de>
AuthorDate: Fri Nov 13 20:52:28 2020 +0100
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Oct 8 15:03:04 2021 +0200

    tdf#134298 sw: layout: remove left-over page frame without content
    
    Once tdf#138039 is fixed, this bugdoc has an additional empty page 3.
    
    This is because it first goes to 3 pages, and then the SwTextFrame
    on page does a MoveBwd, leaving behind a page frame with just a body
    frame and nothing else.
    
    It turns out that SwRootFrame::RemoveSuperfluous() only removes
    empty frames at the end of the document, but here there's a non-empty
    frame following it.  Also, this function doesn't handle cases like
    right/left page styles so it can't delete pages in the middle.
    
    SwFrame::CheckPageDescs() doesn't remove page frames that don't have
    content, it only removes those that have the intentionally-empty flag set.
    
    Extend CheckPageDescs() to also remove page frames that don't have
    content, and make sure it is called when SwContentFrame::Cut()
    removes the last content from a page frame (it will be called after
    all pages are valid in SwLayAction::InternalAction()).
    
    (Alternatively it might be possible to prevent the problem from
     occurring in SwTextFly::ForEach() by ignoring the fly so that the first
     paragraph never leaves page 1, but we didn't explore that.)
    
    (cherry picked from commit b9ef71476fd70bc13f50ebe80390e0730d1b7afb)
    
    Conflicts:
            sw/qa/extras/layout/layout2.cxx
            sw/source/core/layout/pagechg.cxx
    
    Change-Id: I3a3f1efe6d7ed28e05dc159a86abc3d702cc272b

diff --git a/sw/qa/extras/layout/data/tdf134298.ott b/sw/qa/extras/layout/data/tdf134298.ott
new file mode 100644
index 000000000000..effb595eb328
Binary files /dev/null and b/sw/qa/extras/layout/data/tdf134298.ott differ
diff --git a/sw/qa/extras/layout/layout.cxx b/sw/qa/extras/layout/layout.cxx
index b8dd065967c4..cb873bbf59b6 100644
--- a/sw/qa/extras/layout/layout.cxx
+++ b/sw/qa/extras/layout/layout.cxx
@@ -4259,6 +4259,27 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf138039)
     assertXPath(pXmlDoc, "/root/page[3]/body/txt[1]/anchored", 0);
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf134298)
+{
+    createDoc("tdf134298.ott");
+
+    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+    // there are 2 pages
+    assertXPath(pXmlDoc, "/root/page", 2);
+    // table and first para on first page
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab", 1);
+    assertXPath(pXmlDoc, "/root/page[1]/body/txt", 1);
+    assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/anchored", 0);
+    // paragraph with large fly on second page
+    assertXPath(pXmlDoc, "/root/page[2]/body/tab", 0);
+    assertXPath(pXmlDoc, "/root/page[2]/body/txt", 1);
+    assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly", 1);
+    assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "top", "17897");
+    assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "height",
+                "15819");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/pagefrm.hxx b/sw/source/core/inc/pagefrm.hxx
index 3c7f29f52ad0..c8101ef3157b 100644
--- a/sw/source/core/inc/pagefrm.hxx
+++ b/sw/source/core/inc/pagefrm.hxx
@@ -438,6 +438,9 @@ SwTextGridItem const* GetGridItem(SwPageFrame const*const);
 
 sal_uInt16 GetGridWidth(SwTextGridItem const&, SwDoc const&);
 
+namespace sw { bool IsPageFrameEmpty(SwPageFrame const& rPage); }
+
+
 #endif // INCLUDED_SW_SOURCE_CORE_INC_PAGEFRM_HXX
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/pagechg.cxx b/sw/source/core/layout/pagechg.cxx
index a3188eb2a5ca..e119ee4dca08 100644
--- a/sw/source/core/layout/pagechg.cxx
+++ b/sw/source/core/layout/pagechg.cxx
@@ -991,11 +991,67 @@ void SwPageFrame::PrepareRegisterChg()
     }
 }
 
+namespace sw {
+
+/// check if there's content on the page that requires it to exist
+bool IsPageFrameEmpty(SwPageFrame const& rPage)
+{
+    bool bExistEssentialObjs = ( nullptr != rPage.GetSortedObjs() );
+    if ( bExistEssentialObjs )
+    {
+        // Only because the page has Flys does not mean that it is needed. If all Flys are
+        // attached to generic content it is also superfluous (checking DocBody should be enough)
+        // OD 19.06.2003 #108784# - consider that drawing objects in
+        // header/footer are supported now.
+        bool bOnlySuperfluosObjs = true;
+        const SwSortedObjs &rObjs = *rPage.GetSortedObjs();
+        for ( size_t i = 0; bOnlySuperfluosObjs && i < rObjs.size(); ++i )
+        {
+            // #i28701#
+            SwAnchoredObject* pAnchoredObj = rObjs[i];
+            // OD 2004-01-19 #110582# - do not consider hidden objects
+            if ( rPage.GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
+                                pAnchoredObj->GetDrawObj()->GetLayer() ) &&
+                 !pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() )
+            {
+                bOnlySuperfluosObjs = false;
+            }
+        }
+        bExistEssentialObjs = !bOnlySuperfluosObjs;
+    }
+
+    // OD 19.06.2003 #108784# - optimization: check first, if essential objects
+    // exists.
+    const SwLayoutFrame* pBody = nullptr;
+    if ( bExistEssentialObjs ||
+         rPage.FindFootnoteCont() ||
+         ( nullptr != ( pBody = rPage.FindBodyCont() ) &&
+            ( pBody->ContainsContent() ||
+                // #i47580#
+                // Do not delete page if there's an empty tabframe
+                // left. I think it might be correct to use ContainsAny()
+                // instead of ContainsContent() to cover the empty-table-case,
+                // but I'm not fully sure, since ContainsAny() also returns
+                // SectionFrames. Therefore I prefer to do it the safe way:
+              ( pBody->Lower() && pBody->Lower()->IsTabFrame() ) ) ) )
+    {
+        return false;
+    }
+    else
+    {
+        return true;
+    }
+}
+
+} // namespace sw
+
 //FIXME: provide missing documentation
 /** Check all pages (starting from the given one) if they use the appropriate frame format.
  *
  * If "wrong" pages are found, try to fix this as simple as possible.
  *
+ * Also delete pages that don't have content on them.
+ *
  * @param pStart        the page from where to start searching
  * @param bNotifyFields
  * @param ppPrev
@@ -1033,7 +1089,10 @@ void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFra
         SwPageFrame *pNextPage = static_cast<SwPageFrame*>(pPage->GetNext());
 
         SwPageDesc *pDesc = pPage->FindPageDesc();
+        /// page is intentionally empty page
         bool bIsEmpty = pPage->IsEmptyPage();
+        // false for intentionally empty pages, they need additional check
+        bool isPageFrameEmpty(!bIsEmpty && sw::IsPageFrameEmpty(*pPage));
         bool bIsOdd = pPage->OnRightPage();
         bool bWantOdd = pPage->WannaRightPage();
         bool bFirst = pPage->OnFirstPage();
@@ -1130,6 +1189,7 @@ void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFra
                 pTmp->Paste( pRoot, pPage );
                 pTmp->PreparePage( false );
                 pPage = pTmp;
+                isPageFrameEmpty = false; // don't delete it right away!
             }
             else if ( pPage->GetPageDesc() != pDesc )           //4.
             {
@@ -1173,16 +1233,21 @@ void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFra
             }
 #endif
         }
-        if ( bIsEmpty )
+        assert(!bIsEmpty || !isPageFrameEmpty);
+        if (bIsEmpty || isPageFrameEmpty)
         {
             // It also might be that an empty page is not needed at all.
             // However, the algorithm above cannot determine that. It is not needed if the following
             // page can live without it. Do obtain that information, we need to dig deeper...
             SwPageFrame *pPg = static_cast<SwPageFrame*>(pPage->GetNext());
-            if( !pPg || pPage->OnRightPage() == pPg->WannaRightPage() )
+            if (isPageFrameEmpty || !pPg || pPage->OnRightPage() == pPg->WannaRightPage())
             {
                 // The following page can find a FrameFormat or has no successor -> empty page not needed
                 SwPageFrame *pTmp = static_cast<SwPageFrame*>(pPage->GetNext());
+                if (isPageFrameEmpty && pPage->GetPrev())
+                {   // check previous *again* vs. its new next! see "ooo321_stylepagenumber.odt"
+                    pTmp = static_cast<SwPageFrame*>(pPage->GetPrev());
+                }
                 pPage->Cut();
                 bool bUpdatePrev = false;
                 if (ppPrev && *ppPrev == pPage)
@@ -1442,44 +1507,7 @@ void SwRootFrame::RemoveSuperfluous()
     // Check the corresponding last page if it is empty and stop loop at the last non-empty page.
     do
     {
-        bool bExistEssentialObjs = ( nullptr != pPage->GetSortedObjs() );
-        if ( bExistEssentialObjs )
-        {
-            // Only because the page has Flys does not mean that it is needed. If all Flys are
-            // attached to generic content it is also superfluous (checking DocBody should be enough)
-            // OD 19.06.2003 #108784# - consider that drawing objects in
-            // header/footer are supported now.
-            bool bOnlySuperfluosObjs = true;
-            SwSortedObjs &rObjs = *pPage->GetSortedObjs();
-            for ( size_t i = 0; bOnlySuperfluosObjs && i < rObjs.size(); ++i )
-            {
-                // #i28701#
-                SwAnchoredObject* pAnchoredObj = rObjs[i];
-                // OD 2004-01-19 #110582# - do not consider hidden objects
-                if ( pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
-                                    pAnchoredObj->GetDrawObj()->GetLayer() ) &&
-                     !pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() )
-                {
-                    bOnlySuperfluosObjs = false;
-                }
-            }
-            bExistEssentialObjs = !bOnlySuperfluosObjs;
-        }
-
-        // OD 19.06.2003 #108784# - optimization: check first, if essential objects
-        // exists.
-        const SwLayoutFrame* pBody = nullptr;
-        if ( bExistEssentialObjs ||
-             pPage->FindFootnoteCont() ||
-             ( nullptr != ( pBody = pPage->FindBodyCont() ) &&
-                ( pBody->ContainsContent() ||
-                    // #i47580#
-                    // Do not delete page if there's an empty tabframe
-                    // left. I think it might be correct to use ContainsAny()
-                    // instead of ContainsContent() to cover the empty-table-case,
-                    // but I'm not fully sure, since ContainsAny() also returns
-                    // SectionFrames. Therefore I prefer to do it the safe way:
-                  ( pBody->Lower() && pBody->Lower()->IsTabFrame() ) ) ) )
+        if (!sw::IsPageFrameEmpty(*pPage))
         {
             if ( pPage->IsFootnotePage() )
             {
diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx
index 57db1547a1c6..11d20243baf4 100644
--- a/sw/source/core/layout/wsfrm.cxx
+++ b/sw/source/core/layout/wsfrm.cxx
@@ -56,6 +56,7 @@
 #include <sortedobjs.hxx>
 #include <frmatr.hxx>
 #include <frmtool.hxx>
+#include <layact.hxx>
 #include <ndtxt.hxx>
 #include <swtable.hxx>
 
@@ -1200,6 +1201,21 @@ void SwContentFrame::Cut()
             if ( pRoot )
             {
                 pRoot->SetSuperfluous();
+                // RemoveSuperfluous can only remove empty pages at the end;
+                // find if there are pages without content following pPage
+                // and if so request a call to CheckPageDescs()
+                SwPageFrame const* pNext(pPage);
+                if (pRoot->GetCurrShell()->Imp()->IsAction())
+                {
+                    while ((pNext = static_cast<SwPageFrame const*>(pNext->GetNext())))
+                    {
+                        if (!sw::IsPageFrameEmpty(*pNext) && !pNext->IsFootnotePage())
+                        {
+                            pRoot->GetCurrShell()->Imp()->GetLayAction().SetCheckPageNum(pPage->GetPhyPageNum());
+                            break;
+                        }
+                    }
+                }
                 GetUpper()->SetCompletePaint();
                 GetUpper()->InvalidatePage( pPage );
             }
commit a543433c730eac8f91b49c6a7993ae26e67d309b
Author:     Michael Stahl <Michael.Stahl at cib.de>
AuthorDate: Mon Nov 16 13:08:48 2020 +0100
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Oct 8 15:03:02 2021 +0200

    (related tdf#134298) sw: layout: avoid infinite loop in InternalAction()
    
    The condition IsInterrupt() && pPage && (m_nCheckPageNum != USHRT_MAX)
    isn't handled properly and the while loop will never terminate with
    the fix for tdf#134298 in several UITest_writer_tests*.
    
    If m_nCheckPageNum is set, then it must result in a call to
    CheckPageDescs() here; it's a member of SwLayAction so won't survive
    until the next idle layout invocation.
    
    There is a funny history of these loop conditions with
    commit 9eff9e699e17cc5a8a25895bd28dc8e4ceb8071e
    and cee296066ab780217395201ab84c2150c8840d25 so we can only hope
    this time we got it right...
    
    (cherry picked from commit 094ee3955ee81e1bc631d50cc216cbb17a777839)
    
    Change-Id: I91b63540bf4280296d747cb8e841594f8dd3b140

diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx
index 7f913c8a02ff..d3dd8b62027d 100644
--- a/sw/source/core/layout/layact.cxx
+++ b/sw/source/core/layout/layact.cxx
@@ -447,15 +447,19 @@ void SwLayAction::InternalAction(OutputDevice* pRenderContext)
     sal_uInt16 nPercentPageNum = 0;
     while ((!IsInterrupt() && pPage) || (m_nCheckPageNum != USHRT_MAX))
     {
-        if (!pPage && m_nCheckPageNum != USHRT_MAX)
+        // note: this is the only place that consumes and resets m_nCheckPageNum
+        if ((IsInterrupt() || !pPage) && m_nCheckPageNum != USHRT_MAX)
         {
-            SwPageFrame *pPg = static_cast<SwPageFrame*>(m_pRoot->Lower());
-            while (pPg && pPg->GetPhyPageNum() < m_nCheckPageNum)
-                pPg = static_cast<SwPageFrame*>(pPg->GetNext());
-            if (pPg)
-                pPage = pPg;
-            if (!pPage)
-                break;
+            if (!pPage || m_nCheckPageNum < pPage->GetPhyPageNum())
+            {
+                SwPageFrame *pPg = static_cast<SwPageFrame*>(m_pRoot->Lower());
+                while (pPg && pPg->GetPhyPageNum() < m_nCheckPageNum)
+                    pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+                if (pPg)
+                    pPage = pPg;
+                if (!pPage)
+                    break;
+            }
 
             SwPageFrame *pTmp = pPage->GetPrev() ?
                                         static_cast<SwPageFrame*>(pPage->GetPrev()) : pPage;
commit f6405603547435442ad4b11a37c8cdc68b063f3e
Author:     Michael Stahl <Michael.Stahl at cib.de>
AuthorDate: Fri Nov 13 20:51:42 2020 +0100
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Oct 8 15:03:00 2021 +0200

    tdf#138039 tdf#134298 sw: layout: fix overlap of fly and table
    
    The layout is horribly borked, the fly anchored in the body-level
    paragraph messed with the preceding table:
    
    page id="1" top="284" width="11905" height="16837" bottom="17120"
        tab id="3" top="794"
            row id="4" top="17121"
                fly id="8" top="16725"
        txt id="7" top="1394"
            fly ptr="0x6ce5510" id="10" top="1302"
    
    SwTabFrame::CalcFlyOffsets() detects an overlap with the large fly, and
    since it has wrap NONE it resizes to below the large image.
    
    Then the SwTabFrame doesn't fit on the page, so it is split, but the split
    fails because nDistanceToUpperPrtBottom is -720 (negative); hence it is
    joined again.
    
    Meanwhile the fly was invalidated, so now CalcFlyOffsets() ignores it and
    the table shrinks again.
    
    Once the fly is positioned again, the process repeats from the start.
    
    Fix this in SwTabFrame::CalcFlyOffsets() by ignoring flys with wrap NONE that
    extend below the body of the document and are anchored in a frame in the
    next-chain of the table frame: these must move to the next page with their
    anchor frame.
    
    For the bugdoc this gives the same layout as LO 5.2.
    
    Reportedly this problem started to happen since commit
    6f5024de2e1a5cc533527e45b33d9a415467c48d, but it's not obvious why.
    
    (cherry picked from commit 6b92d2e8522ecc98d2c5532f5076c20ae295168e)
    
    Conflicts:
            sw/qa/extras/layout/layout2.cxx
            sw/source/core/layout/tabfrm.cxx
    
    Change-Id: Iafb8a6afcba634f11c5db73869313ded0fe13bbd

diff --git a/sw/qa/extras/layout/data/tdf138039.odt b/sw/qa/extras/layout/data/tdf138039.odt
new file mode 100644
index 000000000000..f355fd1349a6
Binary files /dev/null and b/sw/qa/extras/layout/data/tdf138039.odt differ
diff --git a/sw/qa/extras/layout/layout.cxx b/sw/qa/extras/layout/layout.cxx
index 44512e2ce8a2..b8dd065967c4 100644
--- a/sw/qa/extras/layout/layout.cxx
+++ b/sw/qa/extras/layout/layout.cxx
@@ -4235,6 +4235,30 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf134548)
     }
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf138039)
+{
+    createDoc("tdf138039.odt");
+
+    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+    // there are 3 pages
+    assertXPath(pXmlDoc, "/root/page", 3);
+    // table on first page
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab", 1);
+    assertXPath(pXmlDoc, "/root/page[1]/body/txt", 0);
+    // paragraph with large fly on second page
+    assertXPath(pXmlDoc, "/root/page[2]/body/tab", 0);
+    assertXPath(pXmlDoc, "/root/page[2]/body/txt", 1);
+    assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly", 1);
+    assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "top", "17915");
+    assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly[1]/infos/bounds", "height",
+                "15819");
+    // paragraph on third page
+    assertXPath(pXmlDoc, "/root/page[3]/body/tab", 0);
+    assertXPath(pXmlDoc, "/root/page[3]/body/txt", 1);
+    assertXPath(pXmlDoc, "/root/page[3]/body/txt[1]/anchored", 0);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx
index b89b21b9205a..009099250954 100644
--- a/sw/source/core/layout/tabfrm.cxx
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -2706,6 +2706,21 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext)
         aNotify.SetInvaKeep();
 }
 
+static bool IsNextOnSamePage(SwPageFrame const& rPage,
+        SwTabFrame const& rTabFrame, SwTextFrame const& rAnchorFrame)
+{
+    for (SwContentFrame const* pContentFrame = rTabFrame.FindNextCnt();
+        pContentFrame && pContentFrame->FindPageFrame() == &rPage;
+        pContentFrame = pContentFrame->FindNextCnt())
+    {
+        if (pContentFrame == &rAnchorFrame)
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
 /// Calculate the offsets arising because of FlyFrames
 bool SwTabFrame::CalcFlyOffsets( SwTwips& rUpper,
                                long& rLeftOffset,
@@ -2835,10 +2850,25 @@ bool SwTabFrame::CalcFlyOffsets( SwTwips& rUpper,
 
                     if (bShiftDown)
                     {
+                        // possible cases:
+                        //        both in body
+                        //        both in same fly
+                        //        any comb. of body, footnote, header/footer
+                        // to keep it safe, check only in doc body vs page margin for now
                         long nBottom = aRectFnSet.GetBottom(aFlyRect);
-                        if( aRectFnSet.YDiff( nPrtPos, nBottom ) < 0 )
-                            nPrtPos = nBottom;
-                        bInvalidatePrtArea = true;
+                        // tdf#138039 don't grow beyond the page body
+                        // if the fly is anchored below the table; the fly
+                        // must move with its anchor frame to the next page
+                        SwRectFnSet fnPage(pPage);
+                        if (!IsInDocBody() // TODO
+                            || fnPage.YDiff(fnPage.GetBottom(aFlyRect), fnPage.GetPrtBottom(*pPage)) <= 0
+                            || !IsNextOnSamePage(*pPage, *this,
+                                *static_cast<SwTextFrame*>(pFly->GetAnchorFrameContainingAnchPos())))
+                        {
+                            if (aRectFnSet.YDiff( nPrtPos, nBottom ) < 0)
+                                nPrtPos = nBottom;
+                            bInvalidatePrtArea = true;
+                        }
                     }
                     if ( (css::text::WrapTextMode_RIGHT    == rSur.GetSurround() ||
                           css::text::WrapTextMode_PARALLEL == rSur.GetSurround())&&


More information about the Libreoffice-commits mailing list