diff --git a/sw/qa/extras/layout/data/Cool16071_footnoteTailKeptWhenTruncated.fodt b/sw/qa/extras/layout/data/Cool16071_footnoteTailKeptWhenTruncated.fodt
new file mode 100644
index 000000000000..a5ab90290900
--- /dev/null
+++ b/sw/qa/extras/layout/data/Cool16071_footnoteTailKeptWhenTruncated.fodt
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Row 11alpha bravo charlie delta echo foxtrot golf hotel india juliet kilo lima mike november oscar papa quebec romeo sierra tango uniform victor whiskey xray yankee zulu alpha bravo charlie delta echo foxtrot golf hotel india juliet kilo lima mike november oscar papa quebec romeo sierra tango uniform victor whiskey xray yankee zulu alpha bravo charlie delta echo foxtrot golf hotel india juliet kilo lima mike november oscar papa quebec romeo sierra tango uniform victor whiskey xray yankee zulu alpha bravo charlie delta echo foxtrot golf hotel india juliet filler
+
+
+ Row 2
+
+
+ Row 3
+
+
+ Row 42alpha bravo charlie delta echo foxtrot golf hotel india juliet kilo lima mike november oscar papa quebec romeo sierra TAILMARK
+
+
+
+
+
diff --git a/sw/qa/extras/layout/layout6.cxx b/sw/qa/extras/layout/layout6.cxx
index 5c37ac4240ab..0402916b58bf 100644
--- a/sw/qa/extras/layout/layout6.cxx
+++ b/sw/qa/extras/layout/layout6.cxx
@@ -15,6 +15,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -1610,6 +1611,38 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testCool16067_footnoteSpaceFreedBySplit)
assertXPath(pXmlDoc, "//page[4]/body/tab/row", 1);
}
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testCool16071_footnoteTailKeptWhenTruncated)
+{
+ // The first row's footnote fills the footnote area of the page it ends up on, and the last
+ // row travels between pages carrying a footnote of three lines. That leaves the lines after
+ // the first formatted against a footnote area with no room for them.
+ createSwDoc("Cool16071_footnoteTailKeptWhenTruncated.fodt");
+ // A layout dump would format what it reads, and that alone repairs the frame: measure the
+ // export instead.
+ save(TestFilter::PDF_WRITER);
+ std::unique_ptr pPdf = parsePDFExport();
+ if (!pPdf)
+ return;
+
+ // the last word of the last footnote, on its third line
+ bool bTailExported = false;
+ for (int nPage = 0; nPage < pPdf->getPageCount() && !bTailExported; ++nPage)
+ {
+ std::unique_ptr pPage = pPdf->openPage(nPage);
+ std::unique_ptr pTextPage = pPage->getTextPage();
+ for (int i = 0; i < pPage->getObjectCount() && !bTailExported; ++i)
+ {
+ std::unique_ptr pObject = pPage->getObject(i);
+ if (pObject->getType() == vcl::pdf::PDFPageObjectType::Text)
+ bTailExported = pObject->getText(pTextPage).indexOf("TAILMARK") >= 0;
+ }
+ }
+
+ // Without the fix the footnote kept its first line only: the two lines cut away from it had
+ // nowhere to go, so their text was missing from the document altogether.
+ CPPUNIT_ASSERT(bTailExported);
+}
+
CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testHiddenParagraphFollowFrame)
{
createSwDoc("hidden-para-follow-frame.fodt");
diff --git a/sw/source/core/text/frmform.cxx b/sw/source/core/text/frmform.cxx
index a37e808f8668..f7a1ce8da2b9 100644
--- a/sw/source/core/text/frmform.cxx
+++ b/sw/source/core/text/frmform.cxx
@@ -1849,7 +1849,7 @@ void SwTextFrame::Format_( SwTextFormatter &rLine, SwTextFormatInfo &rInf,
rRepaint.Top( rLine.Y() );
if( 0 >= rRepaint.Width() )
rRepaint.Width(1);
- WidowsAndOrphans aFrameBreak( this, rInf.IsTest() ? 1 : 0 );
+ WidowsAndOrphans aFrameBreak(this, rInf.IsTest() ? std::optional(0) : std::nullopt);
// rLine is now set to the first line which needs formatting.
// The bFirst flag makes sure that Next() is not called.
diff --git a/sw/source/core/text/widorp.cxx b/sw/source/core/text/widorp.cxx
index f8fc286d333d..f7260cde37a7 100644
--- a/sw/source/core/text/widorp.cxx
+++ b/sw/source/core/text/widorp.cxx
@@ -59,8 +59,9 @@ bool IsNastyFollow( const SwTextFrame *pFrame )
}
-SwTextFrameBreak::SwTextFrameBreak( SwTextFrame *pNewFrame, const SwTwips nRst )
- : m_nRstHeight(nRst), m_pFrame(pNewFrame)
+SwTextFrameBreak::SwTextFrameBreak(SwTextFrame* pNewFrame, const std::optional& oRst)
+ : m_oRstHeight(oRst)
+ , m_pFrame(pNewFrame)
{
SwSwapIfSwapped swap(m_pFrame);
SwRectFnSet aRectFnSet(*m_pFrame);
@@ -84,13 +85,13 @@ SwTextFrameBreak::SwTextFrameBreak( SwTextFrame *pNewFrame, const SwTwips nRst )
m_bBreak = false;
- if( !m_nRstHeight && !m_pFrame->IsFollow() && m_pFrame->IsInFootnote() && m_pFrame->HasPara() )
+ if (!m_oRstHeight && !m_pFrame->IsFollow() && m_pFrame->IsInFootnote() && m_pFrame->HasPara())
{
- m_nRstHeight = m_pFrame->GetFootnoteFrameHeight();
- m_nRstHeight += aRectFnSet.GetHeight(m_pFrame->getFramePrintArea()) -
- aRectFnSet.GetHeight(m_pFrame->getFrameArea());
- if( m_nRstHeight < 0 )
- m_nRstHeight = 0;
+ m_oRstHeight = m_pFrame->GetFootnoteFrameHeight()
+ + aRectFnSet.GetHeight(m_pFrame->getFramePrintArea())
+ - aRectFnSet.GetHeight(m_pFrame->getFrameArea());
+ if (*m_oRstHeight <= 0)
+ m_oRstHeight.reset();
}
}
@@ -144,8 +145,8 @@ bool SwTextFrameBreak::IsInside(SwTextMargin const& rLine, SwResizeLimitReason&
&& rLine.GetEnd() >= TextFrameIndex(m_pFrame->GetText().getLength()))
nLineHeight += m_pFrame->CalcAddLowerSpaceAsLastInTableCell();
- if( m_nRstHeight )
- bFit = m_nRstHeight >= nLineHeight;
+ if (m_oRstHeight)
+ bFit = *m_oRstHeight >= nLineHeight;
else
{
// The Frame has a height to fit on the page.
@@ -297,22 +298,25 @@ void SwTextFrameBreak::SetRstHeight( const SwTextMargin &rLine )
// Consider bottom margin
SwRectFnSet aRectFnSet(*m_pFrame);
- m_nRstHeight = aRectFnSet.GetBottomMargin(*m_pFrame);
+ m_oRstHeight = aRectFnSet.GetBottomMargin(*m_pFrame);
if ( aRectFnSet.IsVert() )
{
if ( m_pFrame->IsVertLR() )
- m_nRstHeight = aRectFnSet.YDiff( m_pFrame->SwitchHorizontalToVertical( rLine.Y() ) , m_nOrigin );
+ m_oRstHeight
+ = aRectFnSet.YDiff(m_pFrame->SwitchHorizontalToVertical(rLine.Y()), m_nOrigin);
else
- m_nRstHeight += m_nOrigin - m_pFrame->SwitchHorizontalToVertical( rLine.Y() );
+ *m_oRstHeight += m_nOrigin - m_pFrame->SwitchHorizontalToVertical(rLine.Y());
}
else
- m_nRstHeight += rLine.Y() - m_nOrigin;
+ *m_oRstHeight += rLine.Y() - m_nOrigin;
}
-WidowsAndOrphans::WidowsAndOrphans( SwTextFrame *pNewFrame, const SwTwips nRst,
- bool bChkKeep )
- : SwTextFrameBreak( pNewFrame, nRst ), m_nWidLines( 0 ), m_nOrphLines( 0 )
+WidowsAndOrphans::WidowsAndOrphans(SwTextFrame* pNewFrame, const std::optional& oRst,
+ bool bChkKeep)
+ : SwTextFrameBreak(pNewFrame, oRst)
+ , m_nWidLines(0)
+ , m_nOrphLines(0)
{
SwSwapIfSwapped swap(m_pFrame);
diff --git a/sw/source/core/text/widorp.hxx b/sw/source/core/text/widorp.hxx
index bf3051df2298..2b5666e90a00 100644
--- a/sw/source/core/text/widorp.hxx
+++ b/sw/source/core/text/widorp.hxx
@@ -18,6 +18,8 @@
*/
#pragma once
+#include
+
#include
#include "itrtxt.hxx"
@@ -26,14 +28,16 @@ class SwTextFrame;
class SwTextFrameBreak
{
private:
- SwTwips m_nRstHeight;
+ // The height the frame has to fit into. Zero and negative values are considered valid. If not
+ // set, the whole upper is measured instead.
+ std::optional m_oRstHeight;
SwTwips m_nOrigin;
protected:
SwTextFrame *m_pFrame;
bool m_bBreak;
bool m_bKeep;
public:
- SwTextFrameBreak( SwTextFrame *pFrame, const SwTwips nRst = 0 );
+ SwTextFrameBreak(SwTextFrame* pFrame, const std::optional& oRst = std::nullopt);
bool IsBreakNow( SwTextMargin &rLine );
bool IsKeepAlways() const { return m_bKeep; }
@@ -56,8 +60,8 @@ private:
sal_uInt16 m_nWidLines, m_nOrphLines;
public:
- WidowsAndOrphans( SwTextFrame *pFrame, const SwTwips nRst = 0,
- bool bCheckKeep = true );
+ WidowsAndOrphans(SwTextFrame* pFrame, const std::optional& oRst = std::nullopt,
+ bool bCheckKeep = true);
bool FindWidows( SwTextFrame *pFrame, SwTextMargin &rLine );
sal_uInt16 GetOrphansLines() const
{ return m_nOrphLines; }