diff options
-rw-r--r-- | sw/qa/extras/uiwriter/data/tdf104425.odt | bin | 0 -> 9459 bytes | |||
-rw-r--r-- | sw/qa/extras/uiwriter/uiwriter.cxx | 17 | ||||
-rw-r--r-- | sw/source/core/inc/rowfrm.hxx | 19 | ||||
-rw-r--r-- | sw/source/core/layout/tabfrm.cxx | 155 |
4 files changed, 146 insertions, 45 deletions
diff --git a/sw/qa/extras/uiwriter/data/tdf104425.odt b/sw/qa/extras/uiwriter/data/tdf104425.odt Binary files differnew file mode 100644 index 000000000000..8bbf265499ef --- /dev/null +++ b/sw/qa/extras/uiwriter/data/tdf104425.odt diff --git a/sw/qa/extras/uiwriter/uiwriter.cxx b/sw/qa/extras/uiwriter/uiwriter.cxx index 46fd478f5fe8..d609b99dffbf 100644 --- a/sw/qa/extras/uiwriter/uiwriter.cxx +++ b/sw/qa/extras/uiwriter/uiwriter.cxx @@ -215,6 +215,7 @@ public: void testCursorWindows(); void testLandscape(); void testTdf95699(); + void testTdf104425(); CPPUNIT_TEST_SUITE(SwUiWriterTest); CPPUNIT_TEST(testReplaceForward); @@ -326,6 +327,7 @@ public: CPPUNIT_TEST(testCursorWindows); CPPUNIT_TEST(testLandscape); CPPUNIT_TEST(testTdf95699); + CPPUNIT_TEST(testTdf104425); CPPUNIT_TEST_SUITE_END(); private: @@ -4067,6 +4069,21 @@ void SwUiWriterTest::testTdf95699() CPPUNIT_ASSERT_EQUAL(OUString("vnd.oasis.opendocument.field.FORMCHECKBOX"), pFieldMark->GetFieldname()); } +void SwUiWriterTest::testTdf104425() +{ + createDoc("tdf104425.odt"); + xmlDocPtr pXmlDoc = parseLayoutDump(); + // The document contains one top-level 1-cell table with minimum row height set to 70 cm, + // and the cell contents does not exceed the minimum row height. + // It should span over 3 pages. + assertXPath(pXmlDoc, "//page", 3); + sal_Int32 nHeight1 = getXPath(pXmlDoc, "//page[1]/body/tab/row/infos/bounds", "height").toInt32(); + sal_Int32 nHeight2 = getXPath(pXmlDoc, "//page[2]/body/tab/row/infos/bounds", "height").toInt32(); + sal_Int32 nHeight3 = getXPath(pXmlDoc, "//page[3]/body/tab/row/infos/bounds", "height").toInt32(); + double fSumHeight_mm = (nHeight1 + nHeight2 + nHeight3) * 25.4 / 1440.0; + CPPUNIT_ASSERT_DOUBLES_EQUAL(700.0, fSumHeight_mm, 0.05); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SwUiWriterTest); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/core/inc/rowfrm.hxx b/sw/source/core/inc/rowfrm.hxx index 84bb857c3fee..6bc9c22a13f0 100644 --- a/sw/source/core/inc/rowfrm.hxx +++ b/sw/source/core/inc/rowfrm.hxx @@ -45,6 +45,8 @@ class SwRowFrame: public SwLayoutFrame bool m_bIsRepeatedHeadline; bool m_bIsRowSpanLine; + bool m_bIsInSplit; + virtual void DestroyImpl() override; virtual ~SwRowFrame() override; @@ -101,6 +103,23 @@ public: bool IsRowSpanLine() const { return m_bIsRowSpanLine; } void SetRowSpanLine( bool bNew ) { m_bIsRowSpanLine = bNew; } + // A row may only be split if the minimum height of the row frame + // fits into the vertical space left. + // The minimum height is found as maxinum of two values: minimal + // contents of the row (e.g., height of first line of text, or an + // object, or lower table cell), and the minimum height setting. + // As the minimum height setting should not prevent the row to + // flow, (it only should ensure that *total* height is no less), we + // should not consider the setting when the split is performed + // (we should be able to keep on first page as little as required). + // When IsInSplit is true, lcl_CalcMinRowHeight will ignore the + // mininum height setting. It is set in lcl_RecalcSplitLine around + // lcl_RecalcRow and SwRowFrame::Calc that decide if it's possible + // to keep part of row's content on first page, and update table's + // height to fit the rest of space. + bool IsInSplit() const { return m_bIsInSplit; } + void SetInSplit(bool bNew = true) { m_bIsInSplit = bNew; } + DECL_FIXEDMEMPOOL_NEWDEL(SwRowFrame) }; diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx index e22b064990d1..b2e56e8bbbc6 100644 --- a/sw/source/core/layout/tabfrm.cxx +++ b/sw/source/core/layout/tabfrm.cxx @@ -194,6 +194,8 @@ static SwTwips lcl_CalcMinRowHeight( const SwRowFrame *pRow, const bool _bConsiderObjs ); static SwTwips lcl_CalcTopAndBottomMargin( const SwLayoutFrame&, const SwBorderAttrs& ); +static SwTwips lcl_calcHeightOfRowBeforeThisFrame(const SwRowFrame& rRow); + static SwTwips lcl_GetHeightOfRows( const SwFrame* pStart, long nCount ) { if ( !nCount || !pStart) @@ -516,8 +518,7 @@ static void lcl_PreprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine, // pTmpLastLineRow does not fit to the line or it is the last line // Check if we can move pTmpLastLineRow to the follow table, // or if we have to split the line: - SwFrame* pCell = pTmpLastLineRow->Lower(); - bool bTableLayoutToComplex = false; + bool bTableLayoutTooComplex = false; long nMinHeight = 0; // We have to take into account: @@ -528,12 +529,19 @@ static void lcl_PreprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine, nMinHeight = (pTmpLastLineRow->Frame().*aRectFnSet->fnGetHeight)(); else { + { + const SwFormatFrameSize &rSz = pTmpLastLineRow->GetFormat()->GetFrameSize(); + if ( rSz.GetHeightSizeType() == ATT_MIN_SIZE ) + nMinHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*pTmpLastLineRow); + } + + SwFrame* pCell = pTmpLastLineRow->Lower(); while ( pCell ) { if ( static_cast<SwCellFrame*>(pCell)->Lower() && static_cast<SwCellFrame*>(pCell)->Lower()->IsRowFrame() ) { - bTableLayoutToComplex = true; + bTableLayoutTooComplex = true; break; } @@ -542,10 +550,6 @@ static void lcl_PreprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine, nMinHeight = std::max( nMinHeight, lcl_CalcTopAndBottomMargin( *static_cast<SwLayoutFrame*>(pCell), rAttrs ) ); pCell = pCell->GetNext(); } - - const SwFormatFrameSize &rSz = pTmpLastLineRow->GetFormat()->GetFrameSize(); - if ( rSz.GetHeightSizeType() == ATT_MIN_SIZE ) - nMinHeight = std::max( nMinHeight, rSz.GetHeight() ); } // 1. Case: @@ -558,7 +562,7 @@ static void lcl_PreprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine, // the master table, and the table structure is not to complex. if ( nTmpCut > nCurrentHeight || ( pTmpLastLineRow->IsRowSplitAllowed() && - !bTableLayoutToComplex && nMinHeight < nTmpCut ) ) + !bTableLayoutTooComplex && nMinHeight < nTmpCut ) ) { // The line has to be split: SwRowFrame* pNewRow = new SwRowFrame( *pTmpLastLineRow->GetTabLine(), &rTab, false ); @@ -624,12 +628,14 @@ inline void TableSplitRecalcLock( SwFlowFrame *pTab ) { pTab->LockJoin(); } inline void TableSplitRecalcUnlock( SwFlowFrame *pTab ) { pTab->UnlockJoin(); } static bool lcl_RecalcSplitLine( SwRowFrame& rLastLine, SwRowFrame& rFollowLine, - SwTwips nRemainingSpaceForLastRow ) + SwTwips nRemainingSpaceForLastRow, SwTwips nAlreadyFree ) { bool bRet = true; vcl::RenderContext* pRenderContext = rLastLine.getRootFrame()->GetCurrShell()->GetOut(); SwTabFrame& rTab = static_cast<SwTabFrame&>(*rLastLine.GetUpper()); + SwRectFnSet aRectFnSet(rTab.GetUpper()); + SwTwips nCurLastLineHeight = (rLastLine.Frame().*aRectFnSet->fnGetHeight)(); // If there are nested cells in rLastLine, the recalculation of the last // line needs some preprocessing. @@ -639,7 +645,6 @@ static bool lcl_RecalcSplitLine( SwRowFrame& rLastLine, SwRowFrame& rFollowLine, rTab.SetRebuildLastLine( true ); // #i26945# rTab.SetDoesObjsFit( true ); - SwRectFnSet aRectFnSet(rTab.GetUpper()); // #i26945# - invalidate and move floating screen // objects 'out of range' @@ -656,6 +661,11 @@ static bool lcl_RecalcSplitLine( SwRowFrame& rLastLine, SwRowFrame& rFollowLine, // invalidate last line ::SwInvalidateAll( &rLastLine, LONG_MAX ); + // Shrink the table to account for the shrunk last row, as well as lower rows + // that had been moved to follow table in SwTabFrame::Split. + // It will grow later when last line will recalc its height. + rTab.Shrink(nAlreadyFree + nCurLastLineHeight - nRemainingSpaceForLastRow + 1); + // Lock this tab frame and its follow bool bUnlockMaster = false; bool bUnlockFollow = false; @@ -671,6 +681,9 @@ static bool lcl_RecalcSplitLine( SwRowFrame& rLastLine, SwRowFrame& rFollowLine, ::TableSplitRecalcLock( rTab.GetFollow() ); } + bool bInSplit = rLastLine.IsInSplit(); + rLastLine.SetInSplit(); + // Do the recalculation lcl_RecalcRow( rLastLine, LONG_MAX ); // #115759# - force a format of the last line in order to @@ -678,6 +691,8 @@ static bool lcl_RecalcSplitLine( SwRowFrame& rLastLine, SwRowFrame& rFollowLine, rLastLine.InvalidateSize(); rLastLine.Calc(pRenderContext); + rLastLine.SetInSplit(bInSplit); + // Unlock this tab frame and its follow if ( bUnlockFollow ) ::TableSplitRecalcUnlock( rTab.GetFollow() ); @@ -933,7 +948,7 @@ bool SwTabFrame::Split( const SwTwips nCutPos, bool bTryToSplit, bool bTableRowK lcl_InnerCalcLayout( this->Lower(), LONG_MAX, true ); } - //In order to be able to compare the positions of the cells whit CutPos, + //In order to be able to compare the positions of the cells whith CutPos, //they have to be calculated consecutively starting from the table. //They can definitely be invalid because of position changes of the table. SwRowFrame *pRow = static_cast<SwRowFrame*>(Lower()); @@ -965,7 +980,7 @@ bool SwTabFrame::Split( const SwTwips nCutPos, bool bTryToSplit, bool bTableRowK // bTryToSplit: Row will never be split if bTryToSplit = false. // This can either be passed as a parameter, indicating // that we are currently doing the second try to split the - // table, or it will be set to falseunder certain + // table, or it will be set to false under certain // conditions that are not suitable for splitting // the row. bool bSplitRowAllowed = pRow->IsRowSplitAllowed(); @@ -1140,9 +1155,9 @@ bool SwTabFrame::Split( const SwTwips nCutPos, bool bTryToSplit, bool bTableRowK } } - SwRowFrame* pLastRow = nullptr; // will point to the last remaining line in master - SwRowFrame* pFollowRow = nullptr; // points to either the follow flow line of the - // first regular line in the follow + SwRowFrame* pLastRow = nullptr; // points to the last remaining line in master + SwRowFrame* pFollowRow = nullptr; // points to either the follow flow line or the + // first regular line in the follow if ( bSplitRowAllowed ) { @@ -1178,7 +1193,7 @@ bool SwTabFrame::Split( const SwTwips nCutPos, bool bTryToSplit, bool bTableRowK pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, true ); } - SwTwips nRet = 0; + SwTwips nShrink = 0; //Optimization: There is no paste needed for the new Follow and the //optimized insert can be used (big amounts of rows luckily only occurs in @@ -1190,7 +1205,7 @@ bool SwTabFrame::Split( const SwTwips nCutPos, bool bTryToSplit, bool bTableRowK while ( pRow ) { SwFrame* pNxt = pRow->GetNext(); - nRet += (pRow->Frame().*aRectFnSet->fnGetHeight)(); + nShrink += (pRow->Frame().*aRectFnSet->fnGetHeight)(); // The footnotes do not have to be moved, this is done in the // MoveFwd of the follow table!!! pRow->RemoveFromLayout(); @@ -1209,7 +1224,7 @@ bool SwTabFrame::Split( const SwTwips nCutPos, bool bTryToSplit, bool bTableRowK while ( pRow ) { SwFrame* pNxt = pRow->GetNext(); - nRet += (pRow->Frame().*aRectFnSet->fnGetHeight)(); + nShrink += (pRow->Frame().*aRectFnSet->fnGetHeight)(); // The footnotes have to be moved: lcl_MoveFootnotes( *this, *GetFollow(), *pRow ); @@ -1222,13 +1237,15 @@ bool SwTabFrame::Split( const SwTwips nCutPos, bool bTryToSplit, bool bTableRowK } } - Shrink( nRet ); - - // we rebuild the last line to assure that it will be fully formatted - if ( pLastRow ) + if ( !pLastRow ) + Shrink( nShrink ); + else { + // we rebuild the last line to assure that it will be fully formatted + // we also don't shrink here, because we will be doing that in lcl_RecalcSplitLine + // recalculate the split line - bRet = lcl_RecalcSplitLine( *pLastRow, *pFollowRow, nRemainingSpaceForLastRow ); + bRet = lcl_RecalcSplitLine( *pLastRow, *pFollowRow, nRemainingSpaceForLastRow, nShrink ); // NEW TABLES // check if each cell in the row span line has a good height @@ -3539,6 +3556,7 @@ SwRowFrame::SwRowFrame(const SwTableLine &rLine, SwFrame* pSib, bool bInsertCont // <-- split table rows , m_bIsRepeatedHeadline( false ) , m_bIsRowSpanLine( false ) + , m_bIsInSplit( false ) { mnFrameType = SwFrameType::Row; @@ -3811,17 +3829,24 @@ static SwTwips lcl_CalcMinCellHeight( const SwLayoutFrame *_pCell, static SwTwips lcl_CalcMinRowHeight( const SwRowFrame* _pRow, const bool _bConsiderObjs ) { - SwRectFnSet aRectFnSet(_pRow); - - const SwFormatFrameSize &rSz = _pRow->GetFormat()->GetFrameSize(); - - if ( _pRow->HasFixSize() && !_pRow->IsRowSpanLine() ) + SwTwips nHeight = 0; + if ( !_pRow->IsRowSpanLine() ) { - OSL_ENSURE( ATT_FIX_SIZE == rSz.GetHeightSizeType(), "pRow claims to have fixed size" ); - return rSz.GetHeight(); + const SwFormatFrameSize &rSz = _pRow->GetFormat()->GetFrameSize(); + if ( _pRow->HasFixSize() ) + { + OSL_ENSURE(ATT_FIX_SIZE == rSz.GetHeightSizeType(), "pRow claims to have fixed size"); + return rSz.GetHeight(); + } + // If this row frame is being split, then row's minimal height shouldn't restrict + // this frame's minimal height, because the rest will go to follow frame. + else if ( !_pRow->IsInSplit() && rSz.GetHeightSizeType() == ATT_MIN_SIZE ) + { + nHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*_pRow); + } } - SwTwips nHeight = 0; + SwRectFnSet aRectFnSet(_pRow); const SwCellFrame* pLow = static_cast<const SwCellFrame*>(_pRow->Lower()); while ( pLow ) { @@ -3858,8 +3883,7 @@ static SwTwips lcl_CalcMinRowHeight( const SwRowFrame* _pRow, pLow = static_cast<const SwCellFrame*>(pLow->GetNext()); } - if ( rSz.GetHeightSizeType() == ATT_MIN_SIZE && !_pRow->IsRowSpanLine() ) - nHeight = std::max( nHeight, rSz.GetHeight() ); + return nHeight; } @@ -3956,6 +3980,41 @@ static sal_uInt16 lcl_GetBottomLineDist( const SwRowFrame& rRow ) return nBottomLineDist; } +// tdf#104425: calculate the height of all row frames, +// for which this frame is a follow. +// When a row has fixed/minimum height, it may span over +// several pages. The minimal height on this page should +// take into account the sum of all the heights of previous +// frames that constitute the table row on previous pages. +// Otherwise, trying to split a too high row frame will +// result in loop trying to create that too high row +// on each following page +static SwTwips lcl_calcHeightOfRowBeforeThisFrame(const SwRowFrame& rRow) +{ + SwRectFnSet aRectFnSet(&rRow); + const SwTableLine* pLine = rRow.GetTabLine(); + const SwTabFrame* pTab = rRow.FindTabFrame(); + if (!pLine || !pTab || !pTab->IsFollow()) + return 0; + SwTwips nResult = 0; + SwIterator<SwRowFrame, SwFormat> aIter(*pLine->GetFrameFormat()); + for (const SwRowFrame* pCurRow = aIter.First(); pCurRow; pCurRow = aIter.Next()) + { + if (pCurRow != &rRow && pCurRow->GetTabLine() == pLine) + { + // We've found another row frame that is part of the same table row + const SwTabFrame* pCurTab = pCurRow->FindTabFrame(); + if (pCurTab->IsAnFollow(pTab)) + { + // The found row frame belongs to a table frame that preceedes + // (above) this one in chain. So, include it in the sum + nResult += (pCurRow->Frame().*aRectFnSet->fnGetHeight)(); + } + } + } + return nResult; +} + void SwRowFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs ) { SwRectFnSet aRectFnSet(this); @@ -4301,7 +4360,7 @@ SwTwips SwRowFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo ) return 0L; } - // bInfo may be set to true by SwRowFrame::Format; we need to hangle this + // bInfo may be set to true by SwRowFrame::Format; we need to handle this // here accordingly const bool bShrinkAnyway = bInfo; @@ -4311,9 +4370,10 @@ SwTwips SwRowFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo ) if (pMod) { const SwFormatFrameSize &rSz = pMod->GetFrameSize(); - SwTwips nMinHeight = rSz.GetHeightSizeType() == ATT_MIN_SIZE ? - rSz.GetHeight() : - 0; + SwTwips nMinHeight = 0; + if (rSz.GetHeightSizeType() == ATT_MIN_SIZE) + nMinHeight = std::max(rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*this), + 0L); // Only necessary to calculate minimal row height if height // of pRow is at least nMinHeight. Otherwise nMinHeight is the @@ -5167,7 +5227,7 @@ static SwTwips lcl_CalcHeightOfFirstContentLine( const SwRowFrame& rSourceLine ) const SwRowFrame* pTmpSourceRow = static_cast<const SwRowFrame*>(pCurrSourceCell->Lower()); nTmpHeight = lcl_CalcHeightOfFirstContentLine( *pTmpSourceRow ); } - if ( pTmp->IsTabFrame() ) + else if ( pTmp->IsTabFrame() ) { nTmpHeight = static_cast<const SwTabFrame*>(pTmp)->CalcHeightOfFirstContentLine(); } @@ -5282,12 +5342,12 @@ SwTwips SwTabFrame::CalcHeightOfFirstContentLine() const SwTwips nTmpHeight = 0; - SwRowFrame* pFirstRow = GetFirstNonHeadlineRow(); + const SwRowFrame* pFirstRow = GetFirstNonHeadlineRow(); OSL_ENSURE( !IsFollow() || pFirstRow, "FollowTable without Lower" ); // NEW TABLES if ( pFirstRow && pFirstRow->IsRowSpanLine() && pFirstRow->GetNext() ) - pFirstRow = static_cast<SwRowFrame*>(pFirstRow->GetNext()); + pFirstRow = static_cast<const SwRowFrame*>(pFirstRow->GetNext()); // Calculate the height of the headlines: const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat(); @@ -5304,7 +5364,7 @@ SwTwips SwTabFrame::CalcHeightOfFirstContentLine() const while ( pFirstRow && pFirstRow->ShouldRowKeepWithNext() ) { ++nKeepRows; - pFirstRow = static_cast<SwRowFrame*>(pFirstRow->GetNext()); + pFirstRow = static_cast<const SwRowFrame*>(pFirstRow->GetNext()); } if ( nKeepRows > nRepeat ) @@ -5338,7 +5398,7 @@ SwTwips SwTabFrame::CalcHeightOfFirstContentLine() const // line as it would be on the last page. Since this is quite complicated to calculate, // we only calculate the height of the first line. if ( pFirstRow->GetPrev() && - static_cast<SwRowFrame*>(pFirstRow->GetPrev())->IsRowSpanLine() ) + static_cast<const SwRowFrame*>(pFirstRow->GetPrev())->IsRowSpanLine() ) { // Calculate maximum height of all cells with rowspan = 1: SwTwips nMaxHeight = 0; @@ -5370,9 +5430,14 @@ SwTwips SwTabFrame::CalcHeightOfFirstContentLine() const const SwTwips nHeightOfFirstContentLine = lcl_CalcHeightOfFirstContentLine( *pFirstRow ); // Consider minimum row height: - const SwFormatFrameSize &rSz = static_cast<const SwRowFrame*>(pFirstRow)->GetFormat()->GetFrameSize(); - const SwTwips nMinRowHeight = rSz.GetHeightSizeType() == ATT_MIN_SIZE ? - rSz.GetHeight() : 0; + const SwFormatFrameSize &rSz = pFirstRow->GetFormat()->GetFrameSize(); + + SwTwips nMinRowHeight = 0; + if (rSz.GetHeightSizeType() == ATT_MIN_SIZE) + { + nMinRowHeight = std::max(rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*pFirstRow), + 0L); + } nTmpHeight += std::max( nHeightOfFirstContentLine, nMinRowHeight ); |