sw/qa/extras/layout/data/tdf134298.ott |binary sw/qa/extras/layout/layout.cxx | 21 ++++++ sw/source/core/inc/pagefrm.hxx | 3 sw/source/core/layout/pagechg.cxx | 107 ++++++++++++++++++++------------- sw/source/core/layout/wsfrm.cxx | 16 ++++ 5 files changed, 107 insertions(+), 40 deletions(-)
New commits: commit dedebd6368da7954dc3c2beabb8891ff73e5180d Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Fri Nov 13 20:52:28 2020 +0100 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Wed May 19 10:44:13 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.) Change-Id: I3a3f1efe6d7ed28e05dc159a86abc3d702cc272b Reviewed-on: https://gerrit.libreoffice.org/c/core/+/105810 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@cib.de> (cherry picked from commit b9ef71476fd70bc13f50ebe80390e0730d1b7afb) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114097 Tested-by: Michael Stahl <michael.st...@allotropia.de> Reviewed-by: Michael Stahl <michael.st...@allotropia.de> 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 b872cf9087b3..955fa2ff9bc6 100644 --- a/sw/qa/extras/layout/layout.cxx +++ b/sw/qa/extras/layout/layout.cxx @@ -3476,6 +3476,27 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf127235) pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->CalcLayout(); } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf134298) +{ + createDoc("tdf134298.ott"); + + xmlDocPtr 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_TEST_FIXTURE(SwLayoutWriter, testShapeAllowOverlap) { // Need to find out why this fails on macOS. diff --git a/sw/source/core/inc/pagefrm.hxx b/sw/source/core/inc/pagefrm.hxx index 95d69bd699ee..878fae3a0520 100644 --- a/sw/source/core/inc/pagefrm.hxx +++ b/sw/source/core/inc/pagefrm.hxx @@ -435,6 +435,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 a684b1602a1a..0f97c40a9da0 100644 --- a/sw/source/core/layout/pagechg.cxx +++ b/sw/source/core/layout/pagechg.cxx @@ -990,11 +990,66 @@ 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 - consider that drawing objects in + // header/footer are supported now. + bool bOnlySuperfluousObjs = true; + SwSortedObjs const& rObjs = *rPage.GetSortedObjs(); + for (size_t i = 0; bOnlySuperfluousObjs && i < rObjs.size(); ++i) + { + // #i28701# + SwAnchoredObject* pAnchoredObj = rObjs[i]; + // do not consider hidden objects + if ( rPage.GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( + pAnchoredObj->GetDrawObj()->GetLayer() ) && + !pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() ) + { + bOnlySuperfluousObjs = false; + } + } + bExistEssentialObjs = !bOnlySuperfluousObjs; + } + + // optimization: check first if essential objects exist. + 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 @@ -1032,7 +1087,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(); @@ -1129,6 +1187,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. { @@ -1172,16 +1231,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) @@ -1441,44 +1505,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 f17a7ba68870..5cd3a3d6b889 100644 --- a/sw/source/core/layout/wsfrm.cxx +++ b/sw/source/core/layout/wsfrm.cxx @@ -57,6 +57,7 @@ #include <sortedobjs.hxx> #include <frmatr.hxx> #include <frmtool.hxx> +#include <layact.hxx> #include <ndtxt.hxx> // RotateFlyFrame3 @@ -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 ); } _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits