sw/qa/extras/layout/data/tdf152298.docx |binary sw/qa/extras/layout/layout3.cxx | 23 ++++++++++ sw/source/core/layout/tabfrm.cxx | 67 +++++++++++++++++++++++++++----- 3 files changed, 81 insertions(+), 9 deletions(-)
New commits: commit 0a5fff7bb409db38088c79811f4280b9a41ee5e2 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Wed Sep 18 18:07:25 2024 +0500 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Mon Sep 23 09:33:44 2024 +0200 tdf#152298: Handle "keep with next, allow split, span some rows" case All the rows on the table in bugdoc require to be kept with next (their first cells' first paragraphs have that flag). The layout finds the table break position after row 39; but then it checks that that row should be kept with row 38, that with row 37, and so on. The layout loop happens because the search for the break position can't find a row. But cell A38 spans over four rows; in this case, Word checks if this row can split, and splits the spanning cells, apparently as keeping "part" of the row with next. This change implements that algorithm. Change-Id: I234694138ac6b660781ad773cef20151daf874eb Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173675 Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Tested-by: Jenkins Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173706 diff --git a/sw/qa/extras/layout/data/tdf152298.docx b/sw/qa/extras/layout/data/tdf152298.docx new file mode 100644 index 000000000000..22c3de81a038 Binary files /dev/null and b/sw/qa/extras/layout/data/tdf152298.docx differ diff --git a/sw/qa/extras/layout/layout3.cxx b/sw/qa/extras/layout/layout3.cxx index 45d5b91cfbe0..a1ccbb0385c3 100644 --- a/sw/qa/extras/layout/layout3.cxx +++ b/sw/qa/extras/layout/layout3.cxx @@ -3496,6 +3496,29 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, TestTdf152142DoNotMirrorRtlDrawObjs) CPPUNIT_ASSERT_LESS(nShapeEnd, nTextBoxEnd); } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf152298) +{ + createSwDoc("tdf152298.docx"); + auto pDump = parseLayoutDump(); + + assertXPath(pDump, "//page"_ostr, 2); + // Without the fix, this was 39 + assertXPath(pDump, "//page[1]/body/tab/row"_ostr, 38); + assertXPath(pDump, "//page[1]/body/tab/row[38]/cell[1]"_ostr, "rowspan"_ostr, u"4"_ustr); + OUString a38_id = getXPath(pDump, "//page[1]/body/tab/row[38]/cell[1]"_ostr, "id"_ostr); + OUString follow_id = getXPath(pDump, "//page[1]/body/tab/row[38]/cell[1]"_ostr, "follow"_ostr); + // The text of A38, that spans four rows, must be split: empty paragraph here + assertXPathContent(pDump, "//page[1]/body/tab/row[38]/cell[1]/txt"_ostr, u""_ustr); + // First row is the repeating line + assertXPathContent(pDump, "//page[2]/body/tab/row[1]/cell[1]/txt"_ostr, u"1"_ustr); + assertXPathContent(pDump, "//page[2]/body/tab/row[1]/cell[2]/txt"_ostr, u"2"_ustr); + assertXPathContent(pDump, "//page[2]/body/tab/row[1]/cell[3]/txt"_ostr, u"3"_ustr); + // The text in the follow row's first cell is the second paragraph of A38, "10" + assertXPath(pDump, "//page[2]/body/tab/row[2]/cell[1]"_ostr, "id"_ostr, follow_id); + assertXPath(pDump, "//page[2]/body/tab/row[2]/cell[1]"_ostr, "precede"_ostr, a38_id); + assertXPathContent(pDump, "//page[2]/body/tab/row[2]/cell[1]/txt"_ostr, u"10"_ustr); +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx index f753113287fb..fad21eebcce9 100644 --- a/sw/source/core/layout/tabfrm.cxx +++ b/sw/source/core/layout/tabfrm.cxx @@ -1248,6 +1248,26 @@ bool SwTabFrame::Split(const SwTwips nCutPos, bool bTryToSplit, while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() && nRowCount > nRepeat ) { + // Special case: pTmpRow wants to keep with pRow, but allows splitting, and some its + // cells span several rows, including pRow. In this case, the "split" of the spanning + // cells of the pTmpRow may still happen by moving pRow to the next page, even here + // with !bSplitRowAllowed. + if (pTmpRow->IsRowSplitAllowed()) + { + bool bCellSpanCanSplit = false; + for (auto pCellFrame = static_cast<const SwCellFrame*>(pTmpRow->GetLower()); + pCellFrame; + pCellFrame = static_cast<const SwCellFrame*>(pCellFrame->GetNext())) + { + if (pCellFrame->GetTabBox()->getRowSpan() > 1) // Master cell + { + bCellSpanCanSplit = true; + break; + } + } + if (bCellSpanCanSplit) + break; + } pRow = pTmpRow; --nRowCount; pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetPrev()); @@ -2251,9 +2271,31 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) const SwRowFrame* pTmpRow = static_cast<const SwRowFrame*>(GetLastLower()); if ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() ) { - if ( HasFollowFlowLine() ) - RemoveFollowFlowLine(); - Join(); + // Special case: pTmpRow wants to keep with next, but allows splitting, and some its + // cells span several rows - i.e., also the next row. In this case, the "split" of the + // spanning cells of the pTmpRow may still happen by moving next row to the next page, + // even here with bTableRowKeep. + bool bCellSpanCanSplit = false; + if (pTmpRow->IsRowSplitAllowed()) + { + for (auto pCellFrame = static_cast<const SwCellFrame*>(pTmpRow->GetLower()); + pCellFrame; + pCellFrame = static_cast<const SwCellFrame*>(pCellFrame->GetNext())) + { + if (pCellFrame->GetTabBox()->getRowSpan() > 1) // Master cell + { + bCellSpanCanSplit = true; + break; + } + } + } + + if (!bCellSpanCanSplit) + { + if (HasFollowFlowLine()) + RemoveFollowFlowLine(); + Join(); + } } } @@ -2936,8 +2978,18 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) oAccess.reset(); bool isFootnoteGrowth(false); + bool bEffectiveTableRowKeep; + if (bTryToSplit == true) + { + bEffectiveTableRowKeep = bTableRowKeep && !(bAllowSplitOfRow || bEmulateTableKeepSplitAllowed); + } + else + { + // The second attempt; ignore all the flags allowing to split the row + bEffectiveTableRowKeep = bTableRowKeep; + } const bool bSplitError = !Split(nDeadLine, bTryToSplit, - (bTableRowKeep && !(bAllowSplitOfRow || bEmulateTableKeepSplitAllowed)), + bEffectiveTableRowKeep, isFootnoteGrowth); // tdf#130639 don't start table on a new page after the fallback "switch off repeating header" commit d1a4d4c7b44d45818169322ad61e3063cfcc1da1 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Wed Sep 18 13:01:44 2024 +0500 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Mon Sep 23 09:33:01 2024 +0200 Related: tdf#152298 Consider bottom margin in remaining space calculation The value passed to lcl_RecalcSplitLine could be too large; then the call to rTab.Shrink succeeds, and the height of rTab changes as requested; but then the values of nDistanceToUpperPrtBottom and nDistanceToFootnoteBodyPrtBottom are negative, resulting in failed split and layout loop. Change-Id: I797baee701b6a07e0c9310ea8cf902795c053642 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173595 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173705 diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx index e8fa6a39da53..f753113287fb 100644 --- a/sw/source/core/layout/tabfrm.cxx +++ b/sw/source/core/layout/tabfrm.cxx @@ -1096,14 +1096,11 @@ bool SwTabFrame::Split(const SwTwips nCutPos, bool bTryToSplit, SwTwips nRemainingSpaceForLastRow = aRectFnSet.YDiff(nCutPos, aRectFnSet.GetTop(getFrameArea())); - nRemainingSpaceForLastRow -= aRectFnSet.GetTopMargin(*this); + nRemainingSpaceForLastRow -= aRectFnSet.GetTopMargin(*this) + aRectFnSet.GetBottomMargin(*this); // Make pRow point to the line that does not fit anymore: while( pRow->GetNext() && - nRemainingSpaceForLastRow >= ( aRectFnSet.GetHeight(pRow->getFrameArea()) + - (IsCollapsingBorders() ? - pRow->GetBottomLineSize() : - 0 ) ) ) + nRemainingSpaceForLastRow >= ( aRectFnSet.GetHeight(pRow->getFrameArea()) ) ) { if( bTryToSplit || !pRow->IsRowSpanLine() || 0 != aRectFnSet.GetHeight(pRow->getFrameArea()) )