[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