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="'Liberation Serif'" 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="'Liberation Serif'" 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;
