sw/qa/extras/layout/data/C4_must_start_on_p1.fodt |   95 +++++++++++++++++
 sw/qa/extras/layout/data/tdf162614.fodt           |   75 +++++++++++++
 sw/qa/extras/layout/layout3.cxx                   |  121 ++++++++++++++++++++++
 sw/source/core/inc/cntfrm.hxx                     |    3 
 sw/source/core/inc/flyfrm.hxx                     |    7 +
 sw/source/core/inc/frame.hxx                      |   22 +++-
 sw/source/core/inc/ftnfrm.hxx                     |    2 
 sw/source/core/inc/hffrm.hxx                      |    2 
 sw/source/core/inc/layfrm.hxx                     |    2 
 sw/source/core/inc/rootfrm.hxx                    |    2 
 sw/source/core/inc/rowfrm.hxx                     |    2 
 sw/source/core/inc/sectfrm.hxx                    |    5 
 sw/source/core/inc/tabfrm.hxx                     |    2 
 sw/source/core/inc/txtfrm.hxx                     |    7 +
 sw/source/core/layout/fly.cxx                     |   24 ++++
 sw/source/core/layout/ftnfrm.cxx                  |   23 +++-
 sw/source/core/layout/hffrm.cxx                   |   12 +-
 sw/source/core/layout/pagechg.cxx                 |    4 
 sw/source/core/layout/sectfrm.cxx                 |   11 +-
 sw/source/core/layout/tabfrm.cxx                  |   15 +-
 sw/source/core/layout/wsfrm.cxx                   |   45 ++++++--
 sw/source/core/text/widorp.cxx                    |   12 +-
 sw/source/core/text/widorp.hxx                    |    6 +
 23 files changed, 461 insertions(+), 38 deletions(-)

New commits:
commit d1c5ed30d1128c0500d3fab72bb5505f81a1bdc1
Author:     Mike Kaganski <[email protected]>
AuthorDate: Sat Aug 24 23:51:25 2024 +0500
Commit:     Mike Kaganski <[email protected]>
CommitDate: Sun Aug 25 23:40:26 2024 +0200

    tdf#162614: report the reason why a frame couldn't grow
    
    ... and use it in SwTextFrameBreak::IsBreakNow to decide if the content
    needs to go to a follow or not.
    
    The problem was, that in a specific case of the bugdoc, the follow text
    line was taller than the fixed row height on that page; that resulted
    in an attempt to break it again, creating an empty follow in the same
    table cell; it was not moved to the next page, detecting the problem in
    SwTextFrame::CalcFollow, and the formatting looped, until loop control
    fired, calling SwLooping::Drastic, which validated all page's content,
    including tables below, that had invalid zero client (prn) height.
    
    It was very difficult to tell when the break is needed, and when it's
    not, without the information why some frame eventually denied growing
    its height by the requested amount. In the bugdoc case, I failed to
    find at the SwTextFrameBreak::IsBreakNow level anything that could be
    used as a reliable distinction; some heuristic conditions used there
    broke layout in unit tests left and right. So eventually, I came with
    this mechanism of reporting the "why I can't grow" reason back to the
    caller, using the new out argument.
    
    I believe that, beyond this use case, it might be useful in many other
    cases, both to ease the decision, and to prevent needless trial-and-
    error iterations, increasing stability and performance. The enum used
    for the reason reporting includes 'FlowToFollow' and 'BalancedColumns',
    that could be used in those other cases. However, for my needs here, I
    only need the FixedSizeFrame reason, telling that it's not a case when
    breaking to a next frame is possible, so preventing the break.
    
    The choice of reported reasons in this patch is mostly best guess; it
    may be wrong in some places. The crucial for the current fix is the
    assignment inside the '!(GetType() & nTmpType) && HasFixSize()' check
    of SwLayoutFrame::GrowFrame.
    
    Change-Id: I9ce5dd4d2298b60e186fdf485efb85ab304308ee
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172362
    Reviewed-by: Mike Kaganski <[email protected]>
    Tested-by: Jenkins

diff --git a/sw/qa/extras/layout/data/C4_must_start_on_p1.fodt 
b/sw/qa/extras/layout/data/C4_must_start_on_p1.fodt
new file mode 100644
index 000000000000..067d1515d64a
--- /dev/null
+++ b/sw/qa/extras/layout/data/C4_must_start_on_p1.fodt
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" 
office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:font-face-decls>
+  <style:font-face style:name="Liberation Serif" 
svg:font-family="&apos;Liberation Serif&apos;" 
style:font-family-generic="roman" style:font-pitch="variable"/>
+ </office:font-face-decls>
+ <office:styles>
+  <style:default-style style:family="paragraph">
+   <style:paragraph-properties fo:margin-top="0" fo:margin-bottom="0.3cm" 
style:contextual-spacing="false" fo:line-height="108%" fo:orphans="2" 
fo:widows="2"/>
+   <style:text-properties style:font-name="Liberation Serif" 
fo:font-size="12pt"/>
+  </style:default-style>
+ </office:styles>
+ <office:automatic-styles>
+  <style:style style:name="Table1" style:family="table">
+   <style:table-properties style:width="8cm" fo:margin-left="0" 
fo:margin-top="0" fo:margin-bottom="0" table:align="left" 
style:writing-mode="page"/>
+  </style:style>
+  <style:style style:name="Table1.A" style:family="table-column">
+   <style:table-column-properties style:column-width="1cm"/>
+  </style:style>
+  <style:style style:name="Table1.B" style:family="table-column">
+   <style:table-column-properties style:column-width="2cm"/>
+  </style:style>
+  <style:style style:name="Table1.C" style:family="table-column">
+   <style:table-column-properties style:column-width="5cm"/>
+  </style:style>
+  <style:style style:name="Table1.1" style:family="table-row">
+   <style:table-row-properties style:min-row-height="1.5cm" 
fo:keep-together="auto"/>
+  </style:style>
+  <style:style style:name="Table1.2" style:family="table-row">
+   <style:table-row-properties style:min-row-height="0.5cm" 
fo:keep-together="auto"/>
+  </style:style>
+  <style:style style:name="Table1.4" style:family="table-row">
+   <style:table-row-properties style:min-row-height="1cm" 
fo:keep-together="auto"/>
+  </style:style>
+  <style:style style:name="P1" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties fo:margin-top="0" fo:margin-bottom="0" 
fo:line-height="100%"/>
+  </style:style>
+  <style:page-layout style:name="pm1">
+   <style:page-layout-properties fo:page-width="8cm" fo:page-height="5cm" 
fo:margin-top="0" fo:margin-bottom="0" fo:margin-left="0" fo:margin-right="0"/>
+  </style:page-layout>
+ </office:automatic-styles>
+ <office:master-styles>
+  <style:master-page style:name="Standard" style:page-layout-name="pm1"/>
+ </office:master-styles>
+ <office:body>
+  <office:text>
+   <text:p text:style-name="P1"/>
+   <table:table table:name="Table1" table:style-name="Table1">
+    <table:table-column table:style-name="Table1.A"/>
+    <table:table-column table:style-name="Table1.B"/>
+    <table:table-column table:style-name="Table1.C"/>
+    <table:table-header-rows>
+     <table:table-row table:style-name="Table1.1">
+      <table:table-cell>
+       <text:p>A1</text:p>
+      </table:table-cell>
+      <table:table-cell>
+       <text:p>B1</text:p>
+      </table:table-cell>
+      <table:table-cell>
+       <text:p>C1_repeated_header</text:p>
+      </table:table-cell>
+     </table:table-row>
+    </table:table-header-rows>
+    <table:table-row table:style-name="Table1.2">
+     <table:table-cell table:number-columns-spanned="3">
+      <text:p>A2</text:p>
+     </table:table-cell>
+     <table:covered-table-cell/>
+     <table:covered-table-cell/>
+    </table:table-row>
+    <table:table-row table:style-name="Table1.2">
+     <table:table-cell>
+      <text:p>A3</text:p>
+     </table:table-cell>
+     <table:table-cell table:number-rows-spanned="3">
+      <text:p>B3</text:p>
+     </table:table-cell>
+     <table:table-cell>
+      <text:p>C3_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</text:p>
+     </table:table-cell>
+    </table:table-row>
+    <table:table-row table:style-name="Table1.4">
+     <table:table-cell>
+      <text:p>A4</text:p>
+     </table:table-cell>
+     <table:covered-table-cell/>
+     <table:table-cell>
+      <text:p>C4_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</text:p>
+     </table:table-cell>
+    </table:table-row>
+   </table:table>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/sw/qa/extras/layout/data/tdf162614.fodt 
b/sw/qa/extras/layout/data/tdf162614.fodt
new file mode 100644
index 000000000000..f2e170a73c70
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf162614.fodt
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" 
office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:font-face-decls>
+  <style:font-face style:name="Liberation Serif" 
svg:font-family="&apos;Liberation Serif&apos;" 
style:font-family-generic="roman" style:font-pitch="variable"/>
+ </office:font-face-decls>
+ <office:styles>
+  <style:default-style style:family="paragraph">
+   <style:paragraph-properties fo:margin-bottom="4mm"/>
+   <style:text-properties style:font-name="Liberation Serif" 
fo:font-size="12pt" fo:language="en" fo:country="US"/>
+  </style:default-style>
+ </office:styles>
+ <office:automatic-styles>
+  <style:style style:name="Table2.1" style:family="table-row">
+   <style:table-row-properties style:min-row-height="8mm" 
fo:keep-together="always"/>
+  </style:style>
+  <style:style style:name="Table2.2" style:family="table-row">
+   <style:table-row-properties style:row-height="8mm" 
fo:keep-together="always"/>
+  </style:style>
+  <style:style style:name="A1" style:family="table-cell">
+   <style:table-cell-properties fo:border="1pt solid"/>
+  </style:style>
+  <style:style style:name="P1" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties fo:margin-top="3cm"/>
+  </style:style>
+  <style:style style:name="P2" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties fo:keep-together="always"/>
+  </style:style>
+  <style:page-layout style:name="pm1">
+   <style:page-layout-properties fo:page-width="9cm" fo:page-height="5cm" 
fo:margin-top="0" fo:margin-bottom="0"/>
+  </style:page-layout>
+ </office:automatic-styles>
+ <office:master-styles>
+  <style:master-page style:name="Standard" style:page-layout-name="pm1"/>
+ </office:master-styles>
+ <office:body>
+  <office:text>
+   <text:p text:style-name="P1">Table1 with Table2:</text:p>
+   <table:table table:name="Table1">
+    <table:table-column/>
+    <table:table-row>
+     <table:table-cell table:style-name="A1">
+      <table:table table:name="Table2">
+       <table:table-column table:number-columns-repeated="2"/>
+       <table:table-row table:style-name="Table2.1">
+        <table:table-cell table:style-name="A1">
+         <text:p>Table2.A1</text:p>
+        </table:table-cell>
+        <table:table-cell table:style-name="A1" table:number-rows-spanned="2">
+         <text:p text:style-name="P2">Table2.B1 <text:s 
text:c="99"/>(contd.)</text:p>
+        </table:table-cell>
+       </table:table-row>
+       <table:table-row table:style-name="Table2.2">
+        <table:table-cell table:style-name="A1">
+         <text:p>Table2.A2</text:p>
+        </table:table-cell>
+        <table:covered-table-cell/>
+       </table:table-row>
+      </table:table>
+     </table:table-cell>
+    </table:table-row>
+   </table:table>
+   <text:p>Press ENTER here</text:p>
+   <text:p>Below is Table3:</text:p>
+   <table:table table:name="Table3">
+    <table:table-column/>
+    <table:table-row>
+     <table:table-cell table:style-name="A1">
+      <text:p>This text was hidden.</text:p>
+     </table:table-cell>
+    </table:table-row>
+   </table:table>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/sw/qa/extras/layout/layout3.cxx b/sw/qa/extras/layout/layout3.cxx
index 3434291219fc..1a7e47dfeacc 100644
--- a/sw/qa/extras/layout/layout3.cxx
+++ b/sw/qa/extras/layout/layout3.cxx
@@ -3152,6 +3152,127 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, TestTdf162314)
                 u"aaaa"_ustr);
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, TestTdf162614)
+{
+    // Given a table inside another table, having a fixed-height last row, 
with a merged cell
+    // spanning two rows, with a text (having a spacing below) wrapping inside 
that merged cell,
+    // positioned so that the first line of the text in on the first page, and 
the second line
+    // flows onto the second page:
+    createSwDoc("tdf162614.fodt");
+    auto pXmlDoc = parseLayoutDump();
+
+    // Make sure that all the tables have the correct positions and sizes
+    // I find the clang-formatted version of the following awful (it is 
already ugly enough)
+    // clang-format off
+
+    assertXPath(pXmlDoc, "//page"_ostr, 2);
+    // One top-level table on page 1 (Table1), with a single row and a single 
cell
+    assertXPath(pXmlDoc, "//page[1]/body/tab"_ostr, 1);
+    OUString sTable1PrecedeId = getXPath(pXmlDoc, "//page[1]/body/tab"_ostr, 
"id"_ostr);
+    OUString sTable1FollowId = getXPath(pXmlDoc, "//page[1]/body/tab"_ostr, 
"follow"_ostr);
+    assertXPath(pXmlDoc, "//page[1]/body/tab/infos/bounds"_ostr, "top"_ostr, 
u"2261"_ustr);
+    assertXPath(pXmlDoc, "//page[1]/body/tab/infos/bounds"_ostr, 
"height"_ostr, u"810"_ustr);
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row"_ostr, 1);
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row/cell"_ostr, 1);
+    OUString sTable1A1PrecedeId = getXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell"_ostr, "id"_ostr);
+    OUString sTable1A1FollowId = getXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell"_ostr, "follow"_ostr);
+    // One sub-table inside it (Table2):
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row/cell/tab"_ostr, 1);
+    OUString sTable2PrecedeId = getXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab"_ostr, "id"_ostr);
+    OUString sTable2FollowId = getXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab"_ostr, "follow"_ostr);
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row/cell/tab/infos/bounds"_ostr, 
"top"_ostr, u"2508"_ustr);
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row/cell/tab/infos/bounds"_ostr, 
"height"_ostr, u"543"_ustr);
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row/cell/tab/row"_ostr, 1);
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row/cell/tab/row/cell"_ostr, 2);
+    // A1
+    assertXPathNoAttribute(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[1]"_ostr, "follow"_ostr);
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row/cell/tab/row/cell[1]"_ostr, 
"rowspan"_ostr, u"1"_ustr);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[1]/txt/SwParaPortion/SwLineLayout/*"_ostr,
 1);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[1]/txt/SwParaPortion/SwLineLayout/*[1]"_ostr,
 "type"_ostr, u"PortionType::Para"_ustr);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[1]/txt/SwParaPortion/SwLineLayout/*[1]"_ostr,
 "portion"_ostr, u"Table2.A1"_ustr);
+    // B1
+    OUString sTable2B1PrecedeId = getXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[2]"_ostr, "id"_ostr);
+    OUString sTable2B1FollowId = getXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[2]"_ostr, "follow"_ostr);
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row/cell/tab/row/cell[2]"_ostr, 
"rowspan"_ostr, u"2"_ustr);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[2]/infos/bounds"_ostr, "height"_ostr, 
u"523"_ustr);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[2]/infos/prtBounds"_ostr, 
"height"_ostr, u"503"_ustr);
+    OUString sTable2B1TextPrecedeId = getXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[2]/txt"_ostr, "id"_ostr);
+    OUString sTable2B1TextFollowId = getXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[2]/txt"_ostr, "follow"_ostr);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[2]/txt"_ostr, "offset"_ostr, 
u"0"_ustr);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[2]/txt/infos/bounds"_ostr, 
"height"_ostr, u"276"_ustr);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[2]/txt/infos/prtBounds"_ostr, 
"height"_ostr, u"276"_ustr);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[2]/txt/SwParaPortion/SwLineLayout/*"_ostr,
 2);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[2]/txt/SwParaPortion/SwLineLayout/*[1]"_ostr,
 "type"_ostr, u"PortionType::Text"_ustr);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[2]/txt/SwParaPortion/SwLineLayout/*[1]"_ostr,
 "portion"_ostr, u"Table2.B1"_ustr);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row/cell/tab/row/cell[2]/txt/SwParaPortion/SwLineLayout/*[2]"_ostr,
 "type"_ostr, u"PortionType::Hole"_ustr);
+
+    // Two top-level tables on page 2
+    assertXPath(pXmlDoc, "//page[2]/body/tab"_ostr, 2);
+    // Table1 (follow)
+    CPPUNIT_ASSERT_EQUAL(sTable1FollowId, getXPath(pXmlDoc, 
"//page[2]/body/tab[1]"_ostr, "id"_ostr));
+    CPPUNIT_ASSERT_EQUAL(sTable1PrecedeId, getXPath(pXmlDoc, 
"//page[2]/body/tab[1]"_ostr, "precede"_ostr));
+    assertXPath(pXmlDoc, "//page[2]/body/tab[1]/infos/bounds"_ostr, 
"top"_ostr, u"3403"_ustr);
+    assertXPath(pXmlDoc, "//page[2]/body/tab[1]/infos/bounds"_ostr, 
"height"_ostr, u"514"_ustr);
+    assertXPath(pXmlDoc, "//page[2]/body/tab[1]/row"_ostr, 1);
+    assertXPath(pXmlDoc, "//page[2]/body/tab[1]/row/cell"_ostr, 1);
+    CPPUNIT_ASSERT_EQUAL(sTable1A1FollowId, getXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell"_ostr, "id"_ostr));
+    CPPUNIT_ASSERT_EQUAL(sTable1A1PrecedeId, getXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell"_ostr, "precede"_ostr));
+    // Table2 (follow)
+    assertXPath(pXmlDoc, "//page[2]/body/tab[1]/row/cell/tab"_ostr, 1);
+    CPPUNIT_ASSERT_EQUAL(sTable2FollowId, getXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab"_ostr, "id"_ostr));
+    CPPUNIT_ASSERT_EQUAL(sTable2PrecedeId, getXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab"_ostr, "precede"_ostr));
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/infos/bounds"_ostr, "top"_ostr, 
u"3423"_ustr);
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/infos/bounds"_ostr, "height"_ostr, 
u"474"_ustr);
+    assertXPath(pXmlDoc, "//page[2]/body/tab[1]/row/cell/tab/row"_ostr, 2);
+    // Table2 row 1 (continued)
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[1]/cell"_ostr, 2);
+    // Placeholder for the cell in column 1
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[1]/cell[1]/infos/bounds"_ostr, 
"height"_ostr, u"0"_ustr);
+    // B1 (follow)
+    CPPUNIT_ASSERT_EQUAL(sTable2B1FollowId, getXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[1]/cell[2]"_ostr, "id"_ostr));
+    CPPUNIT_ASSERT_EQUAL(sTable2B1PrecedeId, getXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[1]/cell[2]"_ostr, "precede"_ostr));
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[1]/cell[2]"_ostr, "rowspan"_ostr, 
u"2"_ustr);
+    // Without the fix, this failed with
+    // - Expected: 1
+    // - Actual  : 2
+    // - In <>, XPath '//page[2]/body/tab[1]/row/cell/tab/row[1]/cell[2]/txt' 
number of nodes is incorrect
+    CPPUNIT_ASSERT_EQUAL(sTable2B1TextFollowId, getXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[1]/cell[2]/txt"_ostr, "id"_ostr));
+    CPPUNIT_ASSERT_EQUAL(sTable2B1TextPrecedeId, getXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[1]/cell[2]/txt"_ostr, "precede"_ostr));
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[1]/cell[2]/txt/SwParaPortion/SwLineLayout/*"_ostr,
 1);
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[1]/cell[2]/txt/SwParaPortion/SwLineLayout/*[1]"_ostr,
 "type"_ostr, u"PortionType::Para"_ustr);
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[1]/cell[2]/txt/SwParaPortion/SwLineLayout/*[1]"_ostr,
 "portion"_ostr, u"(contd.)"_ustr);
+    // Table2 row 2
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[2]/cell"_ostr, 2);
+    // A2
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[2]/cell[1]/txt/SwParaPortion/SwLineLayout/*"_ostr,
 1);
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[2]/cell[1]/txt/SwParaPortion/SwLineLayout/*[1]"_ostr,
 "type"_ostr, u"PortionType::Para"_ustr);
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[2]/cell[1]/txt/SwParaPortion/SwLineLayout/*[1]"_ostr,
 "portion"_ostr, u"Table2.A2"_ustr);
+    // B2 (covered cell)
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab[1]/row/cell/tab/row[2]/cell[2]"_ostr, "rowspan"_ostr, 
u"-1"_ustr);
+
+    // Table3 (must not be collapsed)
+    assertXPath(pXmlDoc, "//page[2]/body/tab[2]/infos/bounds"_ostr, 
"top"_ostr, u"4696"_ustr);
+    // Without the fix, this failed with
+    // - Expected: 770
+    // - Actual  : 267
+    assertXPath(pXmlDoc, "//page[2]/body/tab[2]/infos/bounds"_ostr, 
"height"_ostr, u"770"_ustr);
+
+    // Now a test for a case that took me some time to fix when creating the 
patch.
+    // It is the greatly simplified tdf124795-5.
+
+    createSwDoc("C4_must_start_on_p1.fodt");
+    pXmlDoc = parseLayoutDump();
+
+    // The first line of C4 text must start on the first page - the initial 
version of the fix
+    // moved it to page 2.
+
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row[4]/cell[3]/txt/SwParaPortion/SwLineLayout/*"_ostr, 1);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row[4]/cell[3]/txt/SwParaPortion/SwLineLayout/*[1]"_ostr, 
"type"_ostr, u"PortionType::Para"_ustr);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row[4]/cell[3]/txt/SwParaPortion/SwLineLayout/*[1]"_ostr, 
"portion"_ostr, u"C4_xxxxxxxxxxxxxxxxxxxx"_ustr);
+
+    // clang-format on
+}
+
 } // end of anonymous namespace
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/inc/cntfrm.hxx b/sw/source/core/inc/cntfrm.hxx
index 2f6c8b5ba3e4..67cfe38c4d9b 100644
--- a/sw/source/core/inc/cntfrm.hxx
+++ b/sw/source/core/inc/cntfrm.hxx
@@ -80,7 +80,8 @@ protected:
 
     virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
     virtual SwTwips ShrinkFrame( SwTwips, bool bTst = false, bool bInfo = 
false ) override;
-    virtual SwTwips GrowFrame  ( SwTwips, bool bTst = false, bool bInfo = 
false ) override;
+    using SwFrame::GrowFrame;
+    virtual SwTwips GrowFrame(SwTwips, SwResizeLimitReason&, bool bTst, bool 
bInfo) override;
 
     SwContentFrame( SwContentNode * const, SwFrame* );
 
diff --git a/sw/source/core/inc/flyfrm.hxx b/sw/source/core/inc/flyfrm.hxx
index 1f083dd6aca0..831cf7fecea8 100644
--- a/sw/source/core/inc/flyfrm.hxx
+++ b/sw/source/core/inc/flyfrm.hxx
@@ -185,6 +185,7 @@ public:
     bool    IsResizeValid(const SwBorderAttrs *pAttrs, Size aTargetSize);
     SwTwips Shrink_( SwTwips, bool bTst );
     SwTwips Grow_  ( SwTwips, bool bTst );
+    SwTwips Grow_(SwTwips, SwResizeLimitReason&, bool bTst);
     void    Invalidate_( SwPageFrame const *pPage = nullptr );
 
     bool FrameSizeChg( const SwFormatFrameSize & );
@@ -312,6 +313,12 @@ private:
     void UpdateUnfloatButton(SwWrtShell* pWrtSh, bool bShow) const;
     void PaintDecorators() const;
 };
+
+inline SwTwips SwFlyFrame::Grow_(SwTwips nDist, bool bTst)
+{
+    return Grow_(nDist, o3tl::temporary(SwResizeLimitReason()), bTst);
+}
+
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/frame.hxx b/sw/source/core/inc/frame.hxx
index eea75e34a3c7..00f913a9abb1 100644
--- a/sw/source/core/inc/frame.hxx
+++ b/sw/source/core/inc/frame.hxx
@@ -27,6 +27,7 @@
 #include <swrect.hxx>
 #include <calbck.hxx>
 #include <svl/SfxBroadcaster.hxx>
+#include <o3tl/temporary.hxx>
 #include <o3tl/typed_flags_set.hxx>
 #include <com/sun/star/style/TabStop.hpp>
 #include <basegfx/matrix/b2dhommatrix.hxx>
@@ -306,6 +307,15 @@ namespace o3tl {
     template<> struct typed_flags<SwFrameInvFlags> : 
is_typed_flags<SwFrameInvFlags, 0x003f> {};
 }
 
+// Possible reasons why SwFrame::Grow[Frame] failed to provide the requested 
growth
+enum class SwResizeLimitReason
+{
+    Unspecified, // no specific reason
+    FixedSizeFrame, // e.g., fixed-height table row; children must be clipped
+    FlowToFollow, // content must flow to a follow; includes next page, 
column, linked frames
+    BalancedColumns, // resize is performed by Format*, not by Grow*
+};
+
 /**
  * Base class of the Writer layout elements.
  *
@@ -473,7 +483,8 @@ protected:
 
     // change only frame size not the size of PrtArea
     virtual SwTwips ShrinkFrame( SwTwips, bool bTst = false, bool bInfo = 
false ) = 0;
-    virtual SwTwips GrowFrame  ( SwTwips, bool bTst = false, bool bInfo = 
false ) = 0;
+    virtual SwTwips GrowFrame(SwTwips, SwResizeLimitReason&, bool bTst, bool 
bInfo) = 0;
+    SwTwips GrowFrame(SwTwips, bool bTst = false, bool bInfo = false);
 
     /// use these so we can grep for SwFrame's GetRegisteredIn accesses
     /// beware that SwTextFrame may return sw::WriterMultiListener
@@ -529,6 +540,7 @@ public:
     // change PrtArea size and FrameSize
     SwTwips Shrink( SwTwips, bool bTst = false, bool bInfo = false );
     SwTwips Grow  ( SwTwips, bool bTst = false, bool bInfo = false );
+    SwTwips Grow(SwTwips nDist, SwResizeLimitReason&, bool bTst, bool bInfo);
 
     // different methods for inserting in layout tree (for performance reasons)
 
@@ -1277,6 +1289,14 @@ inline bool SwFrame::IsAccessibleFrame() const
 {
     return bool(GetType() & FRM_ACCESSIBLE);
 }
+inline SwTwips SwFrame::Grow(SwTwips nDist, bool bTst, bool bInfo)
+{
+    return Grow(nDist, o3tl::temporary(SwResizeLimitReason()), bTst, bInfo);
+}
+inline SwTwips SwFrame::GrowFrame(SwTwips nDist, bool bTst, bool bInfo)
+{
+    return GrowFrame(nDist, o3tl::temporary(SwResizeLimitReason()), bTst, 
bInfo);
+}
 
 //use this to protect a SwFrame for a given scope from getting deleted
 class SwFrameDeleteGuard
diff --git a/sw/source/core/inc/ftnfrm.hxx b/sw/source/core/inc/ftnfrm.hxx
index 7331ef07d100..766a72b2214c 100644
--- a/sw/source/core/inc/ftnfrm.hxx
+++ b/sw/source/core/inc/ftnfrm.hxx
@@ -57,7 +57,7 @@ public:
     static inline SwFootnoteFrame* PrependChained(SwFrame* pThis, bool 
bDefaultFormat);
 
     virtual SwTwips ShrinkFrame( SwTwips, bool bTst = false, bool bInfo = 
false ) override;
-    virtual SwTwips GrowFrame  ( SwTwips, bool bTst = false, bool bInfo = 
false ) override;
+    virtual SwTwips GrowFrame(SwTwips, SwResizeLimitReason&, bool bTst, bool 
bInfo) override;
     virtual void    Format( vcl::RenderContext* pRenderContext, const 
SwBorderAttrs *pAttrs = nullptr ) override;
     virtual void    PaintSwFrameShadowAndBorder(
         const SwRect&,
diff --git a/sw/source/core/inc/hffrm.hxx b/sw/source/core/inc/hffrm.hxx
index 4fed2517db1e..5e1a927f9418 100644
--- a/sw/source/core/inc/hffrm.hxx
+++ b/sw/source/core/inc/hffrm.hxx
@@ -38,7 +38,7 @@ public:
     SwHeadFootFrame(SwFrameFormat * pFrame, SwFrame*, SwFrameType aType);
     virtual void Format( vcl::RenderContext* pRenderContext, const 
SwBorderAttrs *pAttrs = nullptr ) override;
     virtual SwTwips GrowFrame( SwTwips,
-                             bool bTst = false, bool bInfo = false ) override;
+                               SwResizeLimitReason&, bool bTst, bool bInfo ) 
override;
     virtual SwTwips ShrinkFrame( SwTwips,
                                bool bTst = false, bool bInfo = false ) 
override;
     virtual void PaintSubsidiaryLines( const SwPageFrame*, const SwRect& ) 
const override;
diff --git a/sw/source/core/inc/layfrm.hxx b/sw/source/core/inc/layfrm.hxx
index d40fa7b5a341..f199475faef3 100644
--- a/sw/source/core/inc/layfrm.hxx
+++ b/sw/source/core/inc/layfrm.hxx
@@ -54,7 +54,7 @@ protected:
     std::vector<SwAnchoredObject*> m_VertPosOrientFramesFor;
 
     virtual SwTwips ShrinkFrame( SwTwips, bool bTst = false, bool bInfo = 
false ) override;
-    virtual SwTwips GrowFrame  ( SwTwips, bool bTst = false, bool bInfo = 
false ) override;
+    virtual SwTwips GrowFrame(SwTwips, SwResizeLimitReason&, bool bTst, bool 
bInfo) override;
 
     tools::Long CalcRel( const SwFormatFrameSize &rSz ) const;
 
diff --git a/sw/source/core/inc/rootfrm.hxx b/sw/source/core/inc/rootfrm.hxx
index f2b53a82f302..43d51b2dba6a 100644
--- a/sw/source/core/inc/rootfrm.hxx
+++ b/sw/source/core/inc/rootfrm.hxx
@@ -243,7 +243,7 @@ public:
     SW_DLLPUBLIC virtual void PaintSwFrame( vcl::RenderContext& 
rRenderContext, SwRect const&, PaintFrameMode mode = PAINT_ALL,
                                      SwPrintData const*const pPrintData = 
nullptr ) const override;
     virtual SwTwips ShrinkFrame( SwTwips, bool bTst = false, bool bInfo = 
false ) override;
-    virtual SwTwips GrowFrame  ( SwTwips, bool bTst = false, bool bInfo = 
false ) override;
+    virtual SwTwips GrowFrame(SwTwips, SwResizeLimitReason&, bool bTst, bool 
bInfo) override;
 #ifdef DBG_UTIL
     virtual void Cut() override;
     virtual void Paste( SwFrame* pParent, SwFrame* pSibling = nullptr ) 
override;
diff --git a/sw/source/core/inc/rowfrm.hxx b/sw/source/core/inc/rowfrm.hxx
index a48dacf6a37e..ea9e309679f9 100644
--- a/sw/source/core/inc/rowfrm.hxx
+++ b/sw/source/core/inc/rowfrm.hxx
@@ -31,7 +31,7 @@ class SwRowFrame final : public SwLayoutFrame
                         const SwBorderAttrs* pAttrs = nullptr) override;
     /// Only change the Frame size, not the PrtArea SSize
     virtual SwTwips ShrinkFrame(SwTwips, bool bTst = false, bool bInfo = 
false) override;
-    virtual SwTwips GrowFrame(SwTwips, bool bTst = false, bool bInfo = false) 
override;
+    virtual SwTwips GrowFrame(SwTwips, SwResizeLimitReason&, bool bTst, bool 
bInfo) override;
 
     const SwTableLine* m_pTabLine;
     SwRowFrame* m_pFollowRow; ///< note: this is *only* set on old-style 
tables!
diff --git a/sw/source/core/inc/sectfrm.hxx b/sw/source/core/inc/sectfrm.hxx
index 12646d08ebad..ad3095a912ed 100644
--- a/sw/source/core/inc/sectfrm.hxx
+++ b/sw/source/core/inc/sectfrm.hxx
@@ -119,6 +119,7 @@ public:
     bool Growable() const;
     SwTwips Shrink_( SwTwips, bool bTst );
     SwTwips Grow_  ( SwTwips, bool bTst );
+    SwTwips Grow_(SwTwips, SwResizeLimitReason&, bool bTst);
 
     /**
      * A sectionfrm has to maximize, if he has a follow or a ftncontainer at
@@ -189,6 +190,10 @@ inline const SwContentFrame 
*SwSectionFrame::FindLastContent() const
 {
     return const_cast<SwSectionFrame*>(this)->FindLastContent();
 }
+inline SwTwips SwSectionFrame::Grow_(SwTwips nDist, bool bTst)
+{
+    return Grow_(nDist, o3tl::temporary(SwResizeLimitReason()), bTst);
+}
 
 #endif // INCLUDED_SW_SOURCE_CORE_INC_SECTFRM_HXX
 
diff --git a/sw/source/core/inc/tabfrm.hxx b/sw/source/core/inc/tabfrm.hxx
index 3051ba029840..633f02806fbf 100644
--- a/sw/source/core/inc/tabfrm.hxx
+++ b/sw/source/core/inc/tabfrm.hxx
@@ -128,7 +128,7 @@ class SW_DLLPUBLIC SwTabFrame final: public SwLayoutFrame, 
public SwFlowFrame
     virtual void Format( vcl::RenderContext* pRenderContext, const 
SwBorderAttrs *pAttrs = nullptr ) override;
     virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
     // only changes the Framesize, not the PrtArea size
-    virtual SwTwips GrowFrame  ( SwTwips, bool bTst = false, bool bInfo = 
false ) override;
+    virtual SwTwips GrowFrame(SwTwips, SwResizeLimitReason&, bool bTst, bool 
bInfo) override;
     virtual const SwTabFrame* DynCastTabFrame() const override { return this; }
 
 public:
diff --git a/sw/source/core/inc/txtfrm.hxx b/sw/source/core/inc/txtfrm.hxx
index 42fa3c498724..186b014153c8 100644
--- a/sw/source/core/inc/txtfrm.hxx
+++ b/sw/source/core/inc/txtfrm.hxx
@@ -511,6 +511,7 @@ public:
 
     /// Test grow
     inline SwTwips GrowTst( const SwTwips nGrow );
+    inline SwTwips GrowTst( const SwTwips nGrow, SwResizeLimitReason& );
 
     SwParaPortion *GetPara();
     inline const SwParaPortion *GetPara() const;
@@ -855,7 +856,11 @@ inline bool SwTextFrame::HasPara() const
 
 inline SwTwips SwTextFrame::GrowTst( const SwTwips nGrow )
 {
-    return Grow( nGrow, true );
+    return GrowTst(nGrow, o3tl::temporary(SwResizeLimitReason()));
+}
+inline SwTwips SwTextFrame::GrowTst(const SwTwips nGrow, SwResizeLimitReason& 
reason)
+{
+    return Grow(nGrow, reason, true, false);
 }
 
 inline bool SwTextFrame::IsInside(TextFrameIndex const nPos) const
diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx
index 2036a089b83e..051c049d02f2 100644
--- a/sw/source/core/layout/fly.cxx
+++ b/sw/source/core/layout/fly.cxx
@@ -2181,10 +2181,22 @@ SwFlyAtContentFrame* 
SwFlyFrame::DynCastFlyAtContentFrame()
     return IsFlyAtContentFrame() ? static_cast<SwFlyAtContentFrame*>(this) : 
nullptr;
 }
 
-SwTwips SwFlyFrame::Grow_( SwTwips nDist, bool bTst )
+SwTwips SwFlyFrame::Grow_(SwTwips nDist, SwResizeLimitReason& reason, bool 
bTst)
 {
-    if (!Lower() || IsColLocked() || HasFixSize())
+    if (!Lower())
+    {
+        reason = SwResizeLimitReason::Unspecified; // refusing because we have 
no content?
+        return 0;
+    }
+    if (IsColLocked() || HasFixSize())
+    {
+        if (nDist <= 0 || !HasFixSize())
+            reason = SwResizeLimitReason::Unspecified;
+        else
+            reason = GetNextLink() ? SwResizeLimitReason::FlowToFollow
+                                   : SwResizeLimitReason::FixedSizeFrame;
         return 0;
+    }
 
     SwRectFnSet aRectFnSet(this);
     SwTwips nSize = aRectFnSet.GetHeight(getFrameArea());
@@ -2192,7 +2204,10 @@ SwTwips SwFlyFrame::Grow_( SwTwips nDist, bool bTst )
         nDist = LONG_MAX - nSize;
 
     if ( nDist <= 0 )
+    {
+        reason = SwResizeLimitReason::Unspecified;
         return 0;
+    }
 
     if ( Lower()->IsColumnFrame() )
     {   // If it's a Column Frame, the Format takes control of the
@@ -2204,9 +2219,12 @@ SwTwips SwFlyFrame::Grow_( SwTwips nDist, bool bTst )
             InvalidatePos_();
             InvalidateSize();
         }
+        reason = SwResizeLimitReason::BalancedColumns;
         return 0;
     }
 
+    reason = SwResizeLimitReason::Unspecified;
+
     if (bTst)
     {
         // We're in test mode. Don't promise infinite growth for split flys, 
rather limit the
@@ -2227,6 +2245,7 @@ SwTwips SwFlyFrame::Grow_( SwTwips nDist, bool bTst )
             if (nDist > nMaxGrow)
             {
                 nDist = nMaxGrow;
+                reason = SwResizeLimitReason::FlowToFollow;
             }
         }
         return nDist;
@@ -2294,6 +2313,7 @@ SwTwips SwFlyFrame::Grow_( SwTwips nDist, bool bTst )
             {
                 // The requested growth is more than what we can provide, 
limit it.
                 nDist = nMaxGrow;
+                reason = SwResizeLimitReason::FlowToFollow;
             }
             // Grow & invalidate the size.
             SwTwips nRemaining = nDist - (aNew.Height() - aOld.Height());
diff --git a/sw/source/core/layout/ftnfrm.cxx b/sw/source/core/layout/ftnfrm.cxx
index bb35d1c1025c..6550eb5714ac 100644
--- a/sw/source/core/layout/ftnfrm.cxx
+++ b/sw/source/core/layout/ftnfrm.cxx
@@ -364,13 +364,16 @@ void SwFootnoteContFrame::Format( vcl::RenderContext* 
/*pRenderContext*/, const
     setFrameAreaSizeValid(true);
 }
 
-SwTwips SwFootnoteContFrame::GrowFrame( SwTwips nDist, bool bTst, bool )
+SwTwips SwFootnoteContFrame::GrowFrame(SwTwips nDist, SwResizeLimitReason& 
reason, bool bTst, bool)
 {
     // No check if FixSize since FootnoteContainer are variable up to their 
max. height.
     // If the max. height is LONG_MAX, take as much space as needed.
     // If the page is a special footnote page, take also as much as possible.
     assert(GetUpper() && GetUpper()->IsFootnoteBossFrame());
 
+    const auto nOrigDist = std::max(nDist, SwTwips(0));
+    reason = SwResizeLimitReason::Unspecified;
+
     SwRectFnSet aRectFnSet(this);
     if( aRectFnSet.GetHeight(getFrameArea()) > 0 &&
          nDist > ( LONG_MAX - aRectFnSet.GetHeight(getFrameArea()) ) )
@@ -387,6 +390,8 @@ SwTwips SwFootnoteContFrame::GrowFrame( SwTwips nDist, bool 
bTst, bool )
             pSect->ToMaximize( false ) && pSect->Growable() )
         {
             pSect->InvalidateSize();
+            if (nOrigDist)
+                reason = SwResizeLimitReason::FlowToFollow;
             return 0;
         }
     }
@@ -400,21 +405,33 @@ SwTwips SwFootnoteContFrame::GrowFrame( SwTwips nDist, 
bool bTst, bool )
             nDist = std::min( nDist,
                         SwTwips(pBoss->GetMaxFootnoteHeight() - 
aRectFnSet.GetHeight(getFrameArea())) );
             if ( nDist <= 0 )
+            {
+                if (nOrigDist)
+                    reason = SwResizeLimitReason::FlowToFollow;
                 return 0;
+            }
         }
         // FootnoteBoss also influences the max value
         if( !IsInSct() )
         {
             const SwTwips nMax = pBoss->GetVarSpace();
             if ( nDist > nMax )
+            {
                 nDist = nMax;
+                if (nOrigDist)
+                    reason = SwResizeLimitReason::FlowToFollow;
+            }
             if ( nDist <= 0 )
                 return 0;
         }
     }
     else if( nDist > aRectFnSet.GetHeight(GetPrev()->getFrameArea()) )
+    {
         // do not use more space than the body has
         nDist = aRectFnSet.GetHeight(GetPrev()->getFrameArea());
+        if (nOrigDist)
+            reason = SwResizeLimitReason::FlowToFollow;
+    }
 
     tools::Long nAvail = 0;
     if ( bBrowseMode )
@@ -462,7 +479,7 @@ SwTwips SwFootnoteContFrame::GrowFrame( SwTwips nDist, bool 
bTst, bool )
                     }
                 }
             }
-            nReal += pBoss->Grow( nGrow - nReal, bTst );
+            nReal += pBoss->Grow(nGrow - nReal, reason, bTst, false);
             if( ( SwNeighbourAdjust::GrowAdjust == nAdjust || 
SwNeighbourAdjust::AdjustGrow == nAdjust )
                   && nReal < nGrow )
                 nReal += AdjustNeighbourhood( nGrow - nReal, bTst );
@@ -495,6 +512,8 @@ SwTwips SwFootnoteContFrame::GrowFrame( SwTwips nDist, bool 
bTst, bool )
             InvalidatePage( pPage );
         }
     }
+    if (nOrigDist > nReal && reason == SwResizeLimitReason::Unspecified)
+        reason = SwResizeLimitReason::FlowToFollow;
     return nReal;
 }
 
diff --git a/sw/source/core/layout/hffrm.cxx b/sw/source/core/layout/hffrm.cxx
index 495564283670..f773e8393911 100644
--- a/sw/source/core/layout/hffrm.cxx
+++ b/sw/source/core/layout/hffrm.cxx
@@ -437,17 +437,18 @@ void SwHeadFootFrame::Format(vcl::RenderContext* 
pRenderContext, const SwBorderA
     }
 }
 
-SwTwips SwHeadFootFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+SwTwips SwHeadFootFrame::GrowFrame( SwTwips nDist, SwResizeLimitReason& 
reason, bool bTst, bool bInfo )
 {
     SwTwips nResult;
 
     if ( IsColLocked() )
     {
         nResult = 0;
+        reason = SwResizeLimitReason::Unspecified;
     }
     else if (!GetEatSpacing())
     {
-        nResult = SwLayoutFrame::GrowFrame(nDist, bTst, bInfo);
+        nResult = SwLayoutFrame::GrowFrame(nDist, reason, bTst, bInfo);
     }
     else
     {
@@ -514,15 +515,16 @@ SwTwips SwHeadFootFrame::GrowFrame( SwTwips nDist, bool 
bTst, bool bInfo )
 
         if (nDist - nEat > 0)
         {
-            const SwTwips nFrameGrow =
-                SwLayoutFrame::GrowFrame( nDist - nEat, bTst, bInfo );
+            const SwTwips nFrameGrow = SwLayoutFrame::GrowFrame(nDist - nEat, 
reason, bTst, bInfo);
 
             nResult += nFrameGrow;
-            if ( nFrameGrow > 0 )
+            if (nFrameGrow > 0)
             {
                 bNotifyFlys = false;
             }
         }
+        else
+            reason = SwResizeLimitReason::Unspecified;
 
         // notify fly frames, if necessary and triggered.
         if ( ( nResult > 0 ) && bNotifyFlys )
diff --git a/sw/source/core/layout/pagechg.cxx 
b/sw/source/core/layout/pagechg.cxx
index d6a2aaf320ac..379f0cc16631 100644
--- a/sw/source/core/layout/pagechg.cxx
+++ b/sw/source/core/layout/pagechg.cxx
@@ -1476,14 +1476,14 @@ sw::sidebarwindows::SidebarPosition 
SwPageFrame::SidebarPosition() const
     }
 }
 
-SwTwips SwRootFrame::GrowFrame( SwTwips nDist, bool bTst, bool )
+SwTwips SwRootFrame::GrowFrame(SwTwips nDist, SwResizeLimitReason& reason, 
bool bTst, bool)
 {
     if ( !bTst )
     {
         SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
         aFrm.AddHeight(nDist );
     }
-
+    reason = SwResizeLimitReason::Unspecified;
     return nDist;
 }
 
diff --git a/sw/source/core/layout/sectfrm.cxx 
b/sw/source/core/layout/sectfrm.cxx
index 2fc5ac4f6afa..0e96e542a04e 100644
--- a/sw/source/core/layout/sectfrm.cxx
+++ b/sw/source/core/layout/sectfrm.cxx
@@ -2249,18 +2249,22 @@ bool SwSectionFrame::Growable() const
     return ( GetUpper() && const_cast<SwFrame*>(static_cast<SwFrame const 
*>(GetUpper()))->Grow( LONG_MAX, true ) );
 }
 
-SwTwips SwSectionFrame::Grow_( SwTwips nDist, bool bTst )
+SwTwips SwSectionFrame::Grow_(SwTwips nDist, SwResizeLimitReason& reason, bool 
bTst)
 {
     if (GetSection()->CalcHiddenFlag())
     {
+        reason = SwResizeLimitReason::Unspecified;
         return 0;
     }
 
     if (IsColLocked() || HasFixSize())
     {
+        reason = HasFixSize() ? SwResizeLimitReason::FixedSizeFrame : 
SwResizeLimitReason::Unspecified;
         return 0;
     }
 
+    const auto nOrigDist = nDist;
+    reason = SwResizeLimitReason::Unspecified;
     SwRectFnSet aRectFnSet(this);
     tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
     if( nFrameHeight > 0 && nDist > (LONG_MAX - nFrameHeight) )
@@ -2276,6 +2280,8 @@ SwTwips SwSectionFrame::Grow_( SwTwips nDist, bool bTst )
     {
         SwSection* pSection = GetSection();
         bGrow = pSection && 
pSection->GetFormat()->GetBalancedColumns().GetValue();
+        if (!bGrow && nOrigDist)
+            reason = SwResizeLimitReason::BalancedColumns;
     }
     if( !bGrow )
     {
@@ -2293,6 +2299,7 @@ SwTwips SwSectionFrame::Grow_( SwTwips nDist, bool bTst )
         }
         return 0;
     }
+    reason = SwResizeLimitReason::Unspecified; // reset what maybe was set in 
balanced columns check
     SwTwips nGrow;
     if( IsInFootnote() )
         nGrow = 0;
@@ -2324,7 +2331,7 @@ SwTwips SwSectionFrame::Grow_( SwTwips nDist, bool bTst )
         if( bInCalcContent )
             InvalidateSize_();
         else if( nSpace < nGrow &&  nDist != nSpace + GetUpper()->
-                 Grow( nGrow - nSpace ) )
+                 Grow(nGrow - nSpace, reason, false, false))
             InvalidateSize();
         else
         {
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx
index ee3cb240c590..f88993f39d79 100644
--- a/sw/source/core/layout/tabfrm.cxx
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -3761,13 +3761,15 @@ void SwTabFrame::Format( vcl::RenderContext* 
/*pRenderContext*/, const SwBorderA
         Grow( -nDiff );
 }
 
-SwTwips SwTabFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+SwTwips SwTabFrame::GrowFrame(SwTwips nDist, SwResizeLimitReason& reason, bool 
bTst, bool bInfo)
 {
     SwRectFnSet aRectFnSet(this);
     SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
     if( nHeight > 0 && nDist > ( LONG_MAX - nHeight ) )
         nDist = LONG_MAX - nHeight;
 
+    reason = SwResizeLimitReason::Unspecified;
+
     if ( bTst && !IsRestrictTableGrowth() )
         return nDist;
 
@@ -3782,7 +3784,7 @@ SwTwips SwTabFrame::GrowFrame( SwTwips nDist, bool bTst, 
bool bInfo )
 
         if ( nReal < nDist )
         {
-            tools::Long nTmp = GetUpper()->Grow(nDist - std::max(nReal, 
SwTwips(0)), bTst, bInfo);
+            tools::Long nTmp = GetUpper()->Grow(nDist - std::max(nReal, 
SwTwips(0)), reason, bTst, bInfo);
 
             if ( IsRestrictTableGrowth() )
             {
@@ -3840,6 +3842,7 @@ SwTwips SwTabFrame::GrowFrame( SwTwips nDist, bool bTst, 
bool bInfo )
 
     return nDist;
 }
+
 void SwTabFrame::Invalidate(SwTabFrameInvFlags eInvFlags)
 {
     if(eInvFlags == SwTabFrameInvFlags::NONE)
@@ -5313,8 +5316,9 @@ void SwRowFrame::Cut()
     SwLayoutFrame::Cut();
 }
 
-SwTwips SwRowFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+SwTwips SwRowFrame::GrowFrame(SwTwips nDist, SwResizeLimitReason& reason, bool 
bTst, bool bInfo)
 {
+    const auto nOrigDist = nDist;
     SwTwips nReal = 0;
 
     SwTabFrame* pTab = FindTabFrame();
@@ -5363,7 +5367,7 @@ SwTwips SwRowFrame::GrowFrame( SwTwips nDist, bool bTst, 
bool bInfo )
         pTab->SetFollowFlowLine( false );
     }
 
-    nReal += SwLayoutFrame::GrowFrame( nDist, bTst, bInfo);
+    nReal += SwLayoutFrame::GrowFrame(nDist, reason, bTst, bInfo);
 
     pTab->SetRestrictTableGrowth( false );
     pTab->SetFollowFlowLine( bHasFollowFlowLine );
@@ -5376,7 +5380,8 @@ SwTwips SwRowFrame::GrowFrame( SwTwips nDist, bool bTst, 
bool bInfo )
         if ( nReal )
             SetCompletePaint();
     }
-
+    if (reason == SwResizeLimitReason::Unspecified && nReal < nOrigDist && 
IsInSplit())
+        reason = SwResizeLimitReason::FlowToFollow;
     return nReal;
 }
 
diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx
index b776eedea063..eea9b354e5d4 100644
--- a/sw/source/core/layout/wsfrm.cxx
+++ b/sw/source/core/layout/wsfrm.cxx
@@ -1546,18 +1546,21 @@ void SwLayoutFrame::Cut()
     }
 }
 
-SwTwips SwFrame::Grow( SwTwips nDist, bool bTst, bool bInfo )
+SwTwips SwFrame::Grow(SwTwips nDist, SwResizeLimitReason& reason, bool bTst, 
bool bInfo)
 {
     OSL_ENSURE( nDist >= 0, "Negative growth?" );
 
     PROTOCOL_ENTER( this, bTst ? PROT::GrowTest : PROT::Grow, DbgAction::NONE, 
&nDist )
 
     if ( !nDist )
+    {
+        reason = SwResizeLimitReason::Unspecified;
         return 0;
+    }
     if ( IsFlyFrame() )
-        return static_cast<SwFlyFrame*>(this)->Grow_( nDist, bTst );
+        return static_cast<SwFlyFrame*>(this)->Grow_(nDist, reason, bTst);
     if ( IsSctFrame() )
-        return static_cast<SwSectionFrame*>(this)->Grow_( nDist, bTst );
+        return static_cast<SwSectionFrame*>(this)->Grow_(nDist, reason, bTst);
     if (IsCellFrame())
     {
         const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(this);
@@ -1566,7 +1569,10 @@ SwTwips SwFrame::Grow( SwTwips nDist, bool bTst, bool 
bInfo )
         // NEW TABLES
         if ( pTab->IsVertical() != IsVertical() ||
              pThisCell->GetLayoutRowSpan() < 1 )
+        {
+            reason = SwResizeLimitReason::FixedSizeFrame;
             return 0;
+        }
     }
 
     SwRectFnSet aRectFnSet(this);
@@ -1575,7 +1581,7 @@ SwTwips SwFrame::Grow( SwTwips nDist, bool bTst, bool 
bInfo )
     if( nPrtHeight > 0 && nDist > (LONG_MAX - nPrtHeight) )
         nDist = LONG_MAX - nPrtHeight;
 
-    const SwTwips nReal = GrowFrame( nDist, bTst, bInfo );
+    const SwTwips nReal = GrowFrame(nDist, reason, bTst, bInfo);
     if( !bTst )
     {
         nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
@@ -2137,7 +2143,7 @@ void SwFrame::ValidateThisAndAllLowers( const sal_uInt16 
nStage )
     }
 }
 
-SwTwips SwContentFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+SwTwips SwContentFrame::GrowFrame(SwTwips nDist, SwResizeLimitReason& reason, 
bool bTst, bool bInfo)
 {
     SwRectFnSet aRectFnSet(this);
 
@@ -2179,9 +2185,18 @@ SwTwips SwContentFrame::GrowFrame( SwTwips nDist, bool 
bTst, bool bInfo )
                 InvalidateNextPos();
             }
         }
+        if (!nDist)
+            reason = SwResizeLimitReason::Unspecified;
+        else if (GetUpper()->IsBodyFrame() // Page / column body
+                 || (GetUpper()->IsFlyFrame()
+                     && static_cast<SwFlyFrame*>(GetUpper())->GetNextLink()))
+            reason = SwResizeLimitReason::FlowToFollow;
+        else
+            reason = SwResizeLimitReason::FixedSizeFrame;
         return 0;
     }
 
+    reason = SwResizeLimitReason::Unspecified;
     SwTwips nReal = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
     for (SwFrame* pFrame = GetUpper()->Lower(); pFrame && nReal > 0; pFrame = 
pFrame->GetNext())
         nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
@@ -2221,7 +2236,7 @@ SwTwips SwContentFrame::GrowFrame( SwTwips nDist, bool 
bTst, bool bInfo )
         if( GetUpper() )
         {
             if( bTst || !GetUpper()->IsFooterFrame() )
-                nReal = GetUpper()->Grow(nDist - std::max(nReal, SwTwips(0)), 
bTst, bInfo);
+                nReal = GetUpper()->Grow(nDist - std::max(nReal, SwTwips(0)), 
reason, bTst, bInfo);
             else
             {
                 nReal = 0;
@@ -2664,7 +2679,7 @@ SwTwips SwLayoutFrame::InnerHeight() const
     return nRet;
 }
 
-SwTwips SwLayoutFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+SwTwips SwLayoutFrame::GrowFrame(SwTwips nDist, SwResizeLimitReason& reason, 
bool bTst, bool bInfo)
 {
     const SwViewShell *pSh = getRootFrame()->GetCurrShell();
     const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
@@ -2672,7 +2687,14 @@ SwTwips SwLayoutFrame::GrowFrame( SwTwips nDist, bool 
bTst, bool bInfo )
     if (bBrowse)
         nTmpType |= SwFrameType::Body;
     if( !(GetType() & nTmpType) && HasFixSize() )
+    {
+        if (nDist <= 0)
+            reason = SwResizeLimitReason::Unspecified;
+        else
+            reason = IsBodyFrame() ? SwResizeLimitReason::FlowToFollow // Page 
/ column body
+                                   : SwResizeLimitReason::FixedSizeFrame;
         return 0;
+    }
 
     SwRectFnSet aRectFnSet(this);
     const SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
@@ -2708,6 +2730,7 @@ SwTwips SwLayoutFrame::GrowFrame( SwTwips nDist, bool 
bTst, bool bInfo )
         bMoveAccFrame = true;
     }
 
+    reason = SwResizeLimitReason::Unspecified;
     SwTwips nReal = nDist - nMin;
     if ( nReal > 0 )
     {
@@ -2740,10 +2763,13 @@ SwTwips SwLayoutFrame::GrowFrame( SwTwips nDist, bool 
bTst, bool bInfo )
                             if ( -1 == rEndCell.GetTabBox()->getRowSpan() )
                                 pToGrow = rEndCell.GetUpper();
                             else
+                            {
                                 pToGrow = nullptr;
+                                reason = SwResizeLimitReason::FlowToFollow;
+                            }
                         }
                     }
-                    nGrow = pToGrow ? pToGrow->Grow( nReal, bTst, bInfo ) : 0;
+                    nGrow = pToGrow ? pToGrow->Grow(nReal, reason, bTst, 
bInfo) : 0;
                 }
 
                 if( SwNeighbourAdjust::GrowAdjust == nAdjust && nGrow < nReal )
@@ -2857,6 +2883,9 @@ SwTwips SwLayoutFrame::GrowFrame( SwTwips nDist, bool 
bTst, bool bInfo )
     (void)aOldFrame;
 #endif
 
+    if (reason == SwResizeLimitReason::Unspecified && nReal < nDist && 
IsBodyFrame()) // Page / column body
+        reason = SwResizeLimitReason::FlowToFollow;
+
     return nReal;
 }
 
diff --git a/sw/source/core/text/widorp.cxx b/sw/source/core/text/widorp.cxx
index 8fe76fac2a8f..4c0dd129da04 100644
--- a/sw/source/core/text/widorp.cxx
+++ b/sw/source/core/text/widorp.cxx
@@ -105,7 +105,7 @@ SwTextFrameBreak::SwTextFrameBreak( SwTextFrame *pNewFrame, 
const SwTwips nRst )
  * be done until the Follow is formatted. Unfortunately this is crucial
  * to decide if the whole paragraph goes to the next page or not.
  */
-bool SwTextFrameBreak::IsInside( SwTextMargin const &rLine ) const
+bool SwTextFrameBreak::IsInside(SwTextMargin const& rLine, 
SwResizeLimitReason& reason) const
 {
     bool bFit = false;
 
@@ -206,7 +206,7 @@ bool SwTextFrameBreak::IsInside( SwTextMargin const &rLine 
) const
             // The LineHeight exceeds the current Frame height.
             // Call a test Grow to detect if the Frame could
             // grow the requested area.
-            nHeight += m_pFrame->GrowTst( LONG_MAX );
+            nHeight += m_pFrame->GrowTst(LONG_MAX, reason);
 
             // The Grow() returns the height by which the Upper of the 
TextFrame
             // would let the TextFrame grow.
@@ -221,10 +221,11 @@ bool SwTextFrameBreak::IsInside( SwTextMargin const 
&rLine ) const
 bool SwTextFrameBreak::IsBreakNow( SwTextMargin &rLine )
 {
     SwSwapIfSwapped swap(m_pFrame);
+    SwResizeLimitReason reason = SwResizeLimitReason::Unspecified;
 
     // bKeep is stronger than IsBreakNow()
     // Is there enough space ?
-    if( m_bKeep || IsInside( rLine ) )
+    if (m_bKeep || IsInside(rLine, reason))
         m_bBreak = false;
     else
     {
@@ -259,6 +260,11 @@ bool SwTextFrameBreak::IsBreakNow( SwTextMargin &rLine )
             if( !pTmp || !pTmp->Lower() )
                 m_bBreak = false;
         }
+        else if (reason == SwResizeLimitReason::FixedSizeFrame)
+        {
+            // The content is in a clipping frame - no need to break at all
+            m_bBreak = false;
+        }
     }
 
     return m_bBreak;
diff --git a/sw/source/core/text/widorp.hxx b/sw/source/core/text/widorp.hxx
index 996a7fc913bc..bf3051df2298 100644
--- a/sw/source/core/text/widorp.hxx
+++ b/sw/source/core/text/widorp.hxx
@@ -40,6 +40,7 @@ public:
     void SetKeep( const bool bNew ) { m_bKeep = bNew; }
 
     bool IsInside( SwTextMargin const &rLine ) const;
+    bool IsInside(SwTextMargin const& rLine, SwResizeLimitReason&) const;
 
     // In order to be able to handle special cases with Footnote.
     // SetRstHeight sets the rest height for SwTextFrameBreak. This is needed
@@ -79,6 +80,11 @@ public:
     }
 };
 
+inline bool SwTextFrameBreak::IsInside(SwTextMargin const& rLine) const
+{
+    return IsInside(rLine, o3tl::temporary(SwResizeLimitReason()));
+}
+
 namespace sw {
 
 auto FindNonFlyPortion(SwLineLayout const& rLine) -> bool;

Reply via email to