sw/qa/core/layout/data/floattable-compat14-body.docx |binary sw/qa/core/layout/flycnt.cxx | 46 +++++++++++++++++++ sw/source/core/layout/fly.cxx | 33 +++++++++---- 3 files changed, 70 insertions(+), 9 deletions(-)
New commits: commit fd8ae820cb604f8698e5a5f4fe3a5ab15f5b283d Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Fri Mar 17 08:22:26 2023 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Mon Mar 20 11:17:48 2023 +0000 sw floattable, legacy: also consier fly position when deciding the bottom The bugdoc has 2 rows: the 2nd row was split and the master row went to page 1 and the follow row went to page 2. Word positioned the entire row to page 2. The problem seems to be that the layout code wants a "deadline" (so it can grow the frame to that bottom position), but the deadline depends on the height: split flys need to fit the body frame, then they can have a vertical offset (resulting in an overlap between the bottom margin area & the fly frame), in case the fly frame is still inside the page area. This means that this deadline depends on the body height, the fly top and the fly height at the same time. (Or in other words: the overlap is only allowed if the fly would fit the body frame, but an additional vertical offset pushes it to the bottom margin area, with an unchanged height.) Solve this fly height <-> deadline circular dependency by first checking against the page size, using the current fly height; and then against the body height, using the assumed / future fly height (assuming that the fly will grow so its new height will match the deadline). This results in a construct where usually you can't grow outside the body area, but in some limited cases you can (till you only use the bottom margin area), but only in case you grow your top position as well, which is Word-compatible, but quite unusual at other places in Writer. With this, the original tdf#61594 bugdoc gets rendered correctly, once you opt in with SW_FORCE_FLY_SPLIT=1. (cherry picked from commit 90523e10ec053347719309403a4d8566da1dfc4a) Change-Id: I016df1bad79f4b09abc7e2b4fe9ea613355e3794 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/149143 Tested-by: Miklos Vajna <vmik...@collabora.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/qa/core/layout/data/floattable-compat14-body.docx b/sw/qa/core/layout/data/floattable-compat14-body.docx new file mode 100644 index 000000000000..134128348ce2 Binary files /dev/null and b/sw/qa/core/layout/data/floattable-compat14-body.docx differ diff --git a/sw/qa/core/layout/flycnt.cxx b/sw/qa/core/layout/flycnt.cxx index 07aaf5deba3b..a9da83353ecd 100644 --- a/sw/qa/core/layout/flycnt.cxx +++ b/sw/qa/core/layout/flycnt.cxx @@ -566,6 +566,52 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFlyCompat14Nosplit) SwFrame* pRow2 = pTab2->GetLower(); CPPUNIT_ASSERT(!pRow2->GetNext()); } + +CPPUNIT_TEST_FIXTURE(Test, testSplitFlyCompat14Body) +{ + // Given a Word 2010 document with 2 pages, 1 row on page 1, 1 row on page 2: + std::shared_ptr<comphelper::ConfigurationChanges> pChanges( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Writer::Filter::Import::DOCX::ImportFloatingTableAsSplitFly::set(true, + pChanges); + pChanges->commit(); + comphelper::ScopeGuard g([pChanges] { + officecfg::Office::Writer::Filter::Import::DOCX::ImportFloatingTableAsSplitFly::set( + false, pChanges); + pChanges->commit(); + }); + createSwDoc("floattable-compat14-body.docx"); + + // When laying out that document: + // (This is legacy mode, but still Word doesn't split row 2 because 1) row 2 has a minimal + // height, so even the first part of row 2 would not fit the body frame and 2) Word allows using + // the bottom margin area in legacy mode, but only in case the fly height <= body height.) + calcLayout(); + + // Then make sure that the second row is on a page 2: + SwDoc* pDoc = getSwDoc(); + SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + auto pPage1 = dynamic_cast<SwPageFrame*>(pLayout->Lower()); + CPPUNIT_ASSERT(pPage1); + const SwSortedObjs& rPage1Objs = *pPage1->GetSortedObjs(); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPage1Objs.size()); + auto pPage1Fly = dynamic_cast<SwFlyAtContentFrame*>(rPage1Objs[0]); + CPPUNIT_ASSERT(pPage1Fly); + SwFrame* pTab1 = pPage1Fly->GetLower(); + SwFrame* pRow1 = pTab1->GetLower(); + // Without the accompanying fix in place, this test would have failed, part of row 2 was on page + // 1. + CPPUNIT_ASSERT(!pRow1->GetNext()); + auto pPage2 = dynamic_cast<SwPageFrame*>(pPage1->GetNext()); + CPPUNIT_ASSERT(pPage2); + const SwSortedObjs& rPage2Objs = *pPage2->GetSortedObjs(); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPage2Objs.size()); + auto pPage2Fly = dynamic_cast<SwFlyAtContentFrame*>(rPage2Objs[0]); + CPPUNIT_ASSERT(pPage2Fly); + SwFrame* pTab2 = pPage2Fly->GetLower(); + SwFrame* pRow2 = pTab2->GetLower(); + CPPUNIT_ASSERT(!pRow2->GetNext()); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx index 9ffb1a480c96..6c6a2933952b 100644 --- a/sw/source/core/layout/fly.cxx +++ b/sw/source/core/layout/fly.cxx @@ -90,21 +90,36 @@ SwTwips GetFlyAnchorBottom(SwFlyFrame* pFly, const SwFrame& rAnchor) bool bLegacy = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN); if (bLegacy) { - // Word <= 2010 style: the fly can overlap with the bottom margin / footer area. - const SwFrame* pAnchorUpper = rAnchor.FindPageFrame(); - if (!pAnchorUpper) + // Word <= 2010 style: the fly can overlap with the bottom margin / footer area in case the + // fly height fits the body height and the fly bottom fits the page. + const SwPageFrame* pPage = rAnchor.FindPageFrame(); + if (!pPage) { return 0; } - // See if the fly height would still fit the body frame and the overlap would happen just - // because of a vertical offset. + const SwFrame* pBody = pPage->FindBodyCont(); + if (!pBody) + { + return 0; + } + + // See if the fly height would fit at least the page height, ignoring the vertical offset. SwTwips nFlyHeight = aRectFnSet.GetHeight(pFly->getFrameArea()); - SwTwips nAnchorUpperHeight = aRectFnSet.GetHeight(pAnchorUpper->getFramePrintArea()); - if (nFlyHeight <= nAnchorUpperHeight) + SwTwips nPageHeight = aRectFnSet.GetHeight(pPage->getFramePrintArea()); + if (nFlyHeight <= nPageHeight) { - // Yes, it would fit: allow overlap. - return aRectFnSet.GetBottom(pAnchorUpper->getFrameArea()); + // Yes, it would fit: allow overlap if there is no problematic vertical offset. + SwTwips nDeadline = aRectFnSet.GetBottom(pPage->getFrameArea()); + SwTwips nFlyTop = aRectFnSet.GetTop(pFly->getFrameArea()); + SwTwips nBodyHeight = aRectFnSet.GetHeight(pBody->getFramePrintArea()); + if (nDeadline - nFlyTop > nBodyHeight) + { + // If the fly would now grow to nDeadline then it would not fit the body height, so + // limit the height. + nDeadline = nFlyTop + nBodyHeight; + } + return nDeadline; } }