sw/qa/core/layout/data/floattable-compat14.docx |binary sw/qa/core/layout/flycnt.cxx | 36 ++++++++++++++++++ sw/source/core/inc/cellfrm.hxx | 2 - sw/source/core/layout/fly.cxx | 47 +++++++++++++++++++----- 4 files changed, 75 insertions(+), 10 deletions(-)
New commits: commit aa6a72e93746871c53b594db380237c9f42945d6 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Mon Mar 13 08:14:38 2023 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Tue Mar 14 12:52:28 2023 +0000 sw floattable: handle Word 2010 legacy mode in SwFlyFrame The tdf#61594 bugdoc has 3 non-header rows on the first page, while Writer splits the 3rd row, so the follow row goes to the 2nd page. It turns out that the 3rd row fits the first page in Word because >= 2013 matches Writer, but <= 2010 used to behave differently, see <https://answers.microsoft.com/en-us/msoffice/forum/all/floating-tables-in-word-2013-will-not-extend/576962cc-392d-43e5-b603-654406e1969f>. (The spec doesn't seem to document this, sadly.) Fix the problem by using one of the existing compat flags that we only set in the Word <= 2010 case (triggered by <w:compatSetting w:name="compatibilityMode" w:val="14"> or lower values), that scenario wants to allow the fly frame to extend till the bottom of the page, not only till the bottom of the body frame print area. The bugdoc now doesn't split that 3rd row, but the layout of the 4th row is still bad with this. (cherry picked from commit da2707a83f13cba98b22aba1ca6568dbbc4c5fd8) Change-Id: I547abb19df250e5fbe4d8e123593a645cdbdc6b0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/148835 Tested-by: Miklos Vajna <vmik...@collabora.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/qa/core/layout/data/floattable-compat14.docx b/sw/qa/core/layout/data/floattable-compat14.docx new file mode 100644 index 000000000000..5afb0af477d8 Binary files /dev/null and b/sw/qa/core/layout/data/floattable-compat14.docx differ diff --git a/sw/qa/core/layout/flycnt.cxx b/sw/qa/core/layout/flycnt.cxx index d76a3447b938..079030d80054 100644 --- a/sw/qa/core/layout/flycnt.cxx +++ b/sw/qa/core/layout/flycnt.cxx @@ -28,6 +28,7 @@ #include <itabenum.hxx> #include <frmmgr.hxx> #include <frameformats.hxx> +#include <cellfrm.hxx> namespace { @@ -475,6 +476,41 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFlyWidow) // And then similarly this was 1, not 2. CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(2), pText2->GetThisLines()); } + +CPPUNIT_TEST_FIXTURE(Test, testSplitFlyCompat14) +{ + // Given a Word 2010 document with 2 pages, one table row each: + 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.docx"); + + // When laying out that document: + calcLayout(); + + // Then make sure that the first row is entirely on page 1: + 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(); + auto pCell1 = dynamic_cast<SwCellFrame*>(pRow1->GetLower()); + // Without the accompanying fix in place, this test would have failed, the first row was split, + // but not in Word. + CPPUNIT_ASSERT(!pCell1->GetFollowCell()); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/inc/cellfrm.hxx b/sw/source/core/inc/cellfrm.hxx index 2e34b8a2fddf..4fdbc707de6e 100644 --- a/sw/source/core/inc/cellfrm.hxx +++ b/sw/source/core/inc/cellfrm.hxx @@ -27,7 +27,7 @@ struct SwCursorMoveState; class SwBorderAttrs; /// SwCellFrame is one table cell in the document layout. -class SwCellFrame final : public SwLayoutFrame +class SW_DLLPUBLIC SwCellFrame final : public SwLayoutFrame { const SwTableBox* m_pTabBox; diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx index 88c58c7f27f3..65dad61a23f0 100644 --- a/sw/source/core/layout/fly.cxx +++ b/sw/source/core/layout/fly.cxx @@ -79,6 +79,38 @@ using namespace ::com::sun::star; +namespace +{ +/// Gets the bottom position which is a deadline for a split fly. +SwTwips GetFlyAnchorBottom(SwFlyFrame* pFly, const SwFrame& rAnchor) +{ + SwRectFnSet aRectFnSet(pFly); + + const IDocumentSettingAccess& rIDSA = pFly->GetFrameFormat().getIDocumentSettingAccess(); + 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) + { + return 0; + } + + return aRectFnSet.GetBottom(pAnchorUpper->getFrameArea()); + } + + // Word >= 2013 style: the fly has to stay inside the body frame. + const SwFrame* pAnchorUpper = rAnchor.GetUpper(); + if (!pAnchorUpper) + { + return 0; + } + + return aRectFnSet.GetPrtBottom(*pAnchorUpper); +} +} + static SwTwips lcl_CalcAutoWidth( const SwLayoutFrame& rFrame ); SwFlyFrame::SwFlyFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch, bool bFollow ) : @@ -1325,12 +1357,11 @@ void SwFlyFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderA // then use that as the anchor for sizing purposes. pAnchor = pAnchorChar; } - const SwFrame* pAnchorUpper = pAnchor ? pAnchor->GetUpper() : nullptr; - if (pAnchorUpper && IsFlySplitAllowed()) + if (pAnchor && IsFlySplitAllowed()) { // If the fly is allowed to be split, then limit its size to the upper of the // anchor. - SwTwips nDeadline = aRectFnSet.GetPrtBottom(*pAnchorUpper); + SwTwips nDeadline = GetFlyAnchorBottom(this, *pAnchor); SwTwips nTop = aRectFnSet.GetTop(getFrameArea()); SwTwips nBottom = aRectFnSet.GetTop(getFrameArea()) + nRemaining; if (nBottom > nDeadline) @@ -2064,10 +2095,9 @@ SwTwips SwFlyFrame::Grow_( SwTwips nDist, bool bTst ) { pAnchor = pAnchorChar; } - const SwFrame* pAnchorUpper = pAnchor ? pAnchor->GetUpper() : nullptr; - if (pAnchorUpper) + if (pAnchor) { - SwTwips nDeadline = aRectFnSet.GetPrtBottom(*pAnchorUpper); + SwTwips nDeadline = GetFlyAnchorBottom(this, *pAnchor); SwTwips nTop = aRectFnSet.GetTop(getFrameArea()); SwTwips nBottom = nTop + aRectFnSet.GetHeight(getFrameArea()); SwTwips nMaxGrow = nDeadline - nBottom; @@ -2105,10 +2135,9 @@ SwTwips SwFlyFrame::Grow_( SwTwips nDist, bool bTst ) { pAnchor = pAnchorChar; } - const SwFrame* pAnchorUpper = pAnchor ? pAnchor->GetUpper() : nullptr; - if (pAnchorUpper && IsFlySplitAllowed()) + if (pAnchor && IsFlySplitAllowed()) { - SwTwips nDeadline = aRectFnSet.GetPrtBottom(*pAnchorUpper); + SwTwips nDeadline = GetFlyAnchorBottom(this, *pAnchor); SwTwips nTop = aRectFnSet.GetTop(getFrameArea()); SwTwips nBottom = nTop + aRectFnSet.GetHeight(getFrameArea()); // Calculate max grow and compare to the requested growth, adding to nDist may